Java接入支付宝支付超级详细教程——从入门到精通_java接入支付宝自动扣费-程序员宅基地

技术标签: 支付宝支付接入  java  spring拦截器  ajax  javascript  

Java接入支付宝支付教程

源码下载

源码获取:点击获取源码
本文介绍了“二维码付款”的代码。其他支付方式的代码都在源码中。

一、创建应用

1.登录支付宝开放平台

支付宝开放平台网址:https://open.alipay.com/platform/developerIndex.htm

2.创建一个应用

在这里插入图片描述

图1

在这里插入图片描述
图2

二 、设置应用密钥

1.下载安装支付宝开放平台助手

软件下载地址:https://gw.alipayobjects.com/os/bmw-prod/0e6297a2-89b9-4cab-972b-f9534ba07082.exe)如:图3
在这里插入图片描述

图3

2.使用支付宝开放平台助手生成密钥

请注意此处会生成两个密钥,一个是应用公钥、一个是应用私钥,应用公钥需要填写到支付宝开放平台–具体请看下方图片,应用私钥需要开发者自己保存注意保密) 如:图4
在这里插入图片描述

图4

3.将生成的生成的应用公钥填写到支付宝开放平台

在这里插入图片描述

图5

​ 复制支付宝开放平台助手生成的公钥 如:图6
在这里插入图片描述

图6

​ 将公钥填写至支付宝开放平台然后点击“保存设置” 如:图7
在这里插入图片描述

图7

提交审核
在这里插入图片描述

图8

三、沙箱环境控配置

说明:(由于上方的应用审核需要时间,所以在没有通过之前,使用沙箱环境进行测试)

沙箱环境网址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

1设置沙箱环境密钥

我这里之前已经设置好了,根据上方的:“二 、设置应用密钥” 步骤进行操作(注意应用私钥也是需要开发者自己保存的)如:图9
在这里插入图片描述

图9

四、搭建支付宝开放环境

说明:我这里的项目结构采用Spring+springmvc+mybatis 其他框架差不多没有太大的区别

1.导入jar包(支付宝SDK)

​ JDK 1.8 及其以上版本

1.1maven方式

将下列代码放入到项目中的pom.xml 文件中

<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.10.209.ALL</version>
</dependency>
1.2下载jar导入

jar包下载地址:https://repo1.maven.org/maven2/com/alipay/sdk/alipay-sdk-java/4.10.209.ALL/alipay-sdk-java-4.10.209.ALL.jar

将该jar包导入到项目中的lib文件夹中

2创建zfbinfo.properties资源文件

在里面输入如下代码(请注意参数值后面是否有空格

# 支付宝网关名、partnerId和appId
open_api_domain = https://openapi.alipay.com/gateway.do
mcloud_api_domain = http://mcloudmonitor.com/gateway.do
#此处请填写你的PID
pid = 
#此处请填写你当面付的APPID
appid =

# RSA私钥、公钥和支付宝公钥
#此处请填写你的应用私钥且转PKCS8格式
private_key =
#此处请填写你的应用公钥
public_key = 

#SHA256withRsa对应支付宝公钥
alipay_public_key = 

# 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa
sign_type = RSA2
# 当面付最大查询次数和查询间隔(毫秒)
max_query_retry = 5
query_duration = 5000

# 当面付最大撤销次数和撤销间隔(毫秒)
max_cancel_retry = 3
cancel_duration = 2000

# 交易保障线程第一次调度延迟和调度间隔(秒)
heartbeat_delay = 5
heartbeat_duration = 900

#异步通知url(注意拦截器是否拦截)
NotifyUrl=http://8u862c.natappfree.cc/NBOSTA_WSBM/Alipay/ZFBcallbackAction.do

将下方 图10中的"appid"和"支付宝网关"写入到zfbinfo.properties资源文件对应的变量中
在这里插入图片描述

图10

将下面 图11中的“ 应用公钥”和“ 支付宝公钥”分别写入到 zfbinfo.properties资源文件对应的变量中( public_keyalipay_public_key
在这里插入图片描述
图11

将图12中的pid填入到zfbinfo.properties资源文件对应的变量中
在这里插入图片描述

图12

3.创建CommonUtils.java工具类

getZFBinfoValue():用于获取zfbinfo.properties资源文件中的参数

package Alipay.util;

import java.util.Properties;

public class CommonUtils {
    
    /**
     * 获取zfbinfo.properties文件里的值
     * @param name key
     * @return
     * @throws Exception
     */
    public String getZFBinfoValue(String name) throws Exception{
    
        Properties props = new Properties();
        props.load(getClass().getResourceAsStream("/zfbinfo.properties"));
        String filepath = props.getProperty(name);;
        return filepath;
    }
}

3.项目结构图

在这里插入图片描述

五、支付当面付(扫码付款)

1.预约下单

1.1main函数执行

下方代码为预下单,main函数调用一下这个方法就可以运行了,调用此方法后支付宝会返回一个json格式的字符串里面包含二维码地址,然后可以利用二维码生成器 生成二维码然后就可以使用支付宝付款了,如:图13

package Alipay;

import Alipay.util.CommonUtils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;

import java.util.Properties;

/**
 * 支付宝面对面付款
 * 20201217 毛毛哇
 */
public class AlipayFaceToFace {
    
    public static void main(String[] srgs) throws Exception {
    
        CommonUtils commonUtils=new CommonUtils();
        /** 支付宝网关 **/
        String URL =  commonUtils.getZFBinfoValue("open_api_domain");

        /** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/
        String APP_ID = commonUtils.getZFBinfoValue("appid");

        /** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/
        String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");

        /** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/
        String ALIPAY_PUBLIC_KEY =commonUtils.getZFBinfoValue("alipay_public_key");

        /** 初始化 **/
        AlipayClient alipayClient = new DefaultAlipayClient(URL,APP_ID,APP_PRIVATE_KEY,"json","UTF-8",ALIPAY_PUBLIC_KEY,"RSA2");

        /** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.precreate(统一收单线下交易预创建(扫码支付)) **/
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

        /** 设置业务参数  **/
        AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();

        /** 商户订单号,商户自定义,需保证在商户端不重复,如:20200612000001 **/
        model.setOutTradeNo("20200612000005");

        /**订单标题 **/
        model.setSubject("毛毛测试");

        /** 订单金额,精确到小数点后两位 **/
        model.setTotalAmount("0.01");

        /** 订单描述 **/
        model.setBody("");

        /** 业务扩展参数 **/
        //ExtendParams extendParams = new ExtendParams();

        /** 系统商编号,填写服务商的PID用于获取返佣,返佣参数传值前提:传值账号需要签约返佣协议,用于isv商户。 **/
        //extendParams.setSysServiceProviderId("2088511****07846");

        /** 花呗分期参数传值前提:必须有该接口花呗收款准入条件,且需签约花呗分期 **/
        /** 指定可选期数,只支持3/6/12期,还款期数越长手续费越高 **/
        // extendParams.setHbFqNum("3");

        /** 指定花呗分期手续费承担方式,手续费可以由用户全承担(该值为0),也可以商户全承担(该值为100),但不可以共同承担,即不可取0和100外的其他值。 			**/
        //extendParams.setHbFqSellerPercent("0");

        //model.setExtendParams(extendParams);

        /** 将业务参数传至request中 **/
        request.setBizModel(model);

        /** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:			https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/
        request.setNotifyUrl("");

        /**第三方调用(服务商模式),传值app_auth_token后,会收款至授权app_auth_token对应商家账号,如何获传值app_auth_token请参考文档:				https://opensupport.alipay.com/support/helpcenter/79/201602494631 **/
        //request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");

        AlipayTradePrecreateResponse response = null;
        try {
    

            /** 通过alipayClient调用API,获得对应的response类  **/
            response = alipayClient.execute(request);

        } catch (AlipayApiException e) {
    

            e.printStackTrace();
        }

        /** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/
        System.out.println(response.getBody());
    }
}

在这里插入图片描述

图13

1.2结合前端扫码付款
1.2.1创建ZFBFaceToFaceModel实体对象
package com.test.model;

import com.alipay.api.domain.GoodsDetail;

import java.util.List;

/**
 * 支付宝当面付实体类
 * @author 20201217 sqy
 *
 */
public class ZFBFaceToFaceModel {
    
   private String outTradeNo;// (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成
   private String subject; // (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
   private String totalAmount;// (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
   private String undiscountableAmount;// (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
   private String sellerId;// 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
   private String body;// // 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
   private String operatorId; // 商户操作员编号,添加此参数可以为商户操作员做销售统计
   private String storeId; // (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
   private String timeoutExpress;//支付超时如:“120m”,定义为120分钟
   private List<GoodsDetail> goodsDetailList; //商品明细列表,需填写购买商品详细信息,
   private String NotifyUrl;// 支付成功之后 支付宝异步调用的接口地址;
   private String MoblieReturnUrl;//手机支付同步通知页面地址;
   /**
    * (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成
    * @return
    */
   public String getOutTradeNo() {
    
      return outTradeNo;
   }
   /**
    * (必填) 商户网站订单系统中唯一订单号 ,64个字符以内,只能包含字母、数字、下划线, 需保证商户系统端不能重复,建议通过数据库sequence生成
    * @param outTradeNo
    */
   public void setOutTradeNo(String outTradeNo) {
    
      this.outTradeNo = outTradeNo;
   }
   /**
    * (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
    * @return
    */
   public String getSubject() {
    
      return subject;
   }
   /**
    * (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
    * @param subject
    */
   public void setSubject(String subject) {
    
      this.subject = subject;
   }
   /**
    *  (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
    * @return
    */
   public String getTotalAmount() {
    
      return totalAmount;
   }
   /**
    *  (必填) 订单总金额单位为元,不能超过1亿元 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
    * @param totalAmount
    */
   public void setTotalAmount(String totalAmount) {
    
      this.totalAmount = totalAmount;
   }
   /**
    * (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
    * @return
    */
   public String getUndiscountableAmount() {
    
      return undiscountableAmount;
   }
   /**
    * (可选) 订单不可打折金额,可以配合商家平台配置折扣活动,如果酒水不参与打折,则将对应金额填写至此字段 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
    * @param undiscountableAmount
    */
   public void setUndiscountableAmount(String undiscountableAmount) {
    
      this.undiscountableAmount = undiscountableAmount;
   }
   /**
    * // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
    * @return
    */
   public String getSellerId() {
    
      return sellerId;
   }
   /**
    * // 卖家支付宝账号ID,用于支持一个签约账号下支持打款到不同的收款账号,(打款到sellerId对应的支付宝账号)// 如果该字段为空,则默认为与支付宝签约的商户的PID,也就是appid对应的PID
    * @param sellerId
    */
   public void setSellerId(String sellerId) {
    
      this.sellerId = sellerId;
   }
   /**
    * 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
    * @return
    */
   public String getBody() {
    
      return body;
   }
   /**
    * 订单描述,可以对交易或商品进行一个详细地描述,比如填写"购买商品2件共15.00元"
    * @param body
    */
   public void setBody(String body) {
    
      this.body = body;
   }
   /**
    *商户操作员编号,添加此参数可以为商户操作员做销售统计
    * @return
    */
   public String getOperatorId() {
    
      return operatorId;
   }
   /**
    *商户操作员编号,添加此参数可以为商户操作员做销售统计
    * @param operatorId
    */
   public void setOperatorId(String operatorId) {
    
      this.operatorId = operatorId;
   }
   /**
    * (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
    * @return
    */
   public String getStoreId() {
    
      return storeId;
   }
   /**
    * (必填) 商户门店编号,通过门店号和商家后台可以配置精准到门店的折扣信息,详询支付宝技术支持
    * @param storeId
    */
   public void setStoreId(String storeId) {
    
      this.storeId = storeId;
   }
   /**
    *支付超时如:“120m”,定义为120分钟
    * @return
    */
   public String getTimeoutExpress() {
    
      return timeoutExpress;
   }
   /**
    *支付超时如:“120m”,定义为120分钟
    * @param timeoutExpress
    */
   public void setTimeoutExpress(String timeoutExpress) {
    
      this.timeoutExpress = timeoutExpress;
   }
   /**
    *商品明细列表,需填写购买商品详细信息,
    * @return
    */
   public List<GoodsDetail> getGoodsDetailList() {
    
      return goodsDetailList;
   }
   /**
    *商品明细列表,需填写购买商品详细信息,
    * @param goodsDetailList
    */
   public void setGoodsDetailList(List<GoodsDetail> goodsDetailList) {
    
      this.goodsDetailList = goodsDetailList;
   }
   /**
    *支付成功之后 支付宝异步调用的接口地址;
    * @return
    */
   public String getNotifyUrl() {
    
      return NotifyUrl;
   }
   /**
    *支付成功之后 支付宝异步调用的接口地址;
    * @param notifyUrl
    */
   public void setNotifyUrl(String notifyUrl) {
    
      NotifyUrl = notifyUrl;
   }
   /**
    * 手机支付后跳转的页面地址
    * @return
    */
   public String getMoblieReturnUrl() {
    
      return MoblieReturnUrl;
   }
   /**
    * 手机支付后跳转的页面地址
    * @return
    */
   public void setMoblieReturnUrl(String moblieReturnUrl) {
    
      MoblieReturnUrl = moblieReturnUrl;
   }
}
1.2.2编写AlipayFaceToFace类

控制层调用ZFBPreorder方法支付宝预下单 返回json字符串其中包括二维码地址

package Alipay;

import Alipay.util.CommonUtils;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.test.model.ZFBFaceToFaceModel;

/**
 * 支付宝面对面付款
 * 20201217 毛毛哇
 */
public class AlipayFaceToFace {
    
    /**
     * 支付宝预下单
     * @param zfbFaceToFaceModel
     * @return
     */
    public static String ZFBPreorder(ZFBFaceToFaceModel zfbFaceToFaceModel) {
    
        try {
    
            CommonUtils commonUtils = new CommonUtils();
            /** 支付宝网关 **/
            String URL = commonUtils.getZFBinfoValue("open_api_domain");

            /** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/
            String APP_ID = commonUtils.getZFBinfoValue("appid");

            /** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/
            String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");

            /** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/
            String ALIPAY_PUBLIC_KEY = commonUtils.getZFBinfoValue("alipay_public_key");

            /** 初始化 **/
            AlipayClient alipayClient = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, "json", "UTF-8", ALIPAY_PUBLIC_KEY, "RSA2");

            /** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.precreate(统一收单线下交易预创建(扫码支付)) **/
            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

            /** 设置业务参数  **/
            AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();

            /** 商户订单号,商户自定义,需保证在商户端不重复,如:20200612000001 **/
            model.setOutTradeNo(zfbFaceToFaceModel.getOutTradeNo());

            /**订单标题 **/
            model.setSubject(zfbFaceToFaceModel.getSubject());

            /** 订单金额,精确到小数点后两位 **/
            model.setTotalAmount(zfbFaceToFaceModel.getTotalAmount());

            /** 订单描述 **/
            model.setBody(zfbFaceToFaceModel.getBody());

            /** 业务扩展参数 **/
            //ExtendParams extendParams = new ExtendParams();

            /** 系统商编号,填写服务商的PID用于获取返佣,返佣参数传值前提:传值账号需要签约返佣协议,用于isv商户。 **/
            //extendParams.setSysServiceProviderId("2088511****07846");

            /** 花呗分期参数传值前提:必须有该接口花呗收款准入条件,且需签约花呗分期 **/
            /** 指定可选期数,只支持3/6/12期,还款期数越长手续费越高 **/
            // extendParams.setHbFqNum("3");

            /** 指定花呗分期手续费承担方式,手续费可以由用户全承担(该值为0),也可以商户全承担(该值为100),但不可以共同承担,即不可取0和100外的其他值。 **/
            //extendParams.setHbFqSellerPercent("0");

            //model.setExtendParams(extendParams);

            /** 将业务参数传至request中 **/
            request.setBizModel(model);

            /** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/
            request.setNotifyUrl(zfbFaceToFaceModel.getNotifyUrl());

            /**第三方调用(服务商模式),传值app_auth_token后,会收款至授权app_auth_token对应商家账号,如何获传值app_auth_token请参考文档:https://opensupport.alipay.com/support/helpcenter/79/201602494631 **/
            //request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");

            /** 通过alipayClient调用API,获得对应的response类  **/
            AlipayTradePrecreateResponse response = alipayClient.execute(request);

            /** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/
            System.out.println(response.getBody());
            return response.getBody();
        } catch (Exception e) {
    
            e.printStackTrace();
            return null;
        }
    }
}
1.2.3编写ZFBPreorderAction类
package com.test.controller;

import Alipay.AlipayFaceToFace;
import Alipay.util.CommonUtils;
import com.alibaba.fastjson.JSONObject;
import com.test.model.ZFBFaceToFaceModel;
import com.test.service.TestService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/AlipayFaceToFaceController")
public class AlipayFaceToFaceController {
    

    /**
     * 支付宝预约下单
     * 用于接受前端请求 返回给前端二维码地址和商户唯一订单编号
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/ZFBPreorderAction")
    @ResponseBody
    public Map<String,Object> ZFBPreorderAction(HttpServletRequest request,HttpServletResponse response){
    
        Map<String,Object> resultMap=new HashMap<String, Object>();
        try {
    
            CommonUtils commonUtils=new CommonUtils();
            //(必填)商户唯一订单编号
            String outTradeNo= CommonUtils.getUuid();
            // (必填) 订单标题,粗略描述用户的支付目的。如“喜士多(浦东店)消费”
            String subject ="毛毛消费(中国)";
            // (必填) 订单总金额,单位为元,不能超过1亿元
            String totalAmount = "0.01";
            //(必填)支付成功支付支付宝异步通知的接口地址
            String NotifyUrl=commonUtils.getZFBinfoValue("NotifyUrl");
            //将参数放入实体对象中
            ZFBFaceToFaceModel zfbFaceToFaceModel=new ZFBFaceToFaceModel();
            zfbFaceToFaceModel.setOutTradeNo(outTradeNo);
            zfbFaceToFaceModel.setSubject(subject);
            zfbFaceToFaceModel.setTotalAmount(totalAmount);
            zfbFaceToFaceModel.setNotifyUrl(NotifyUrl);
            //支付宝预下单
            String json=AlipayFaceToFace.ZFBPreorder(zfbFaceToFaceModel);
            //解析json数据
            JSONObject jsonObject=JSONObject.parseObject(json);
            //得到alipay_trade_precreate_response数据后再强转JSONObject
            JSONObject jsonobj_two=(JSONObject)jsonObject.get("alipay_trade_precreate_response");
            //再通过jsonobj_two获取到二维码地址
            String qrcode=jsonobj_two.get("qr_code").toString();

            resultMap.put("qrcode",qrcode);
            resultMap.put("outTradeNo",outTradeNo);
        } catch (Exception e) {
    
            e.printStackTrace();
        }
        return resultMap;
    }
}
1.2.4前端页面调用ZFBPreorderAction方法生成付款二维码

请注意一定要引用jquery.qrcode.min.js 要不然生成不了二维码

运行项目点击页面中的“点击生成付款二维码”就可以啦,然后就可以使用手机进行扫码支付

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付宝二维码付款</title>
    <script type="text/javascript" src="js/jquery-1.11.2.min.js"></script>
    <script type="text/javascript" src="js/jquery.qrcode.min.js"></script>
</head>
<body>
    <a href="javascript:void(0);" onclick="Pay()">点击生成付款二维码</a>
    <div id="Orcode_div" style="height: 165px;width: 165px"></div>
</body>
<script type="application/javascript">
    //项目名称
    var basePath="/byyu";
    //商户订单编号
	var outTradeNo;
    //预下单
    function Pay(){
      
        $.ajax({
      
            url: basePath + '/AlipayFaceToFaceController/ZFBPreorderAction.action',
            method: 'post',
            async: false,  //是否异步请求  默认truefalse为同步请求 ,true为异步请求)
            dataType: 'JSON',
            success: function (res) {
      
                //商户订单编号
                outTradeNo=res.outTradeNo;
                //创建订单二维码
                createQrcode(res.qrcode);
            }
        })
    }
    var zfbQRCode;
    //生成付款二维码
    function createQrcode(url){
      
        if (zfbQRCode!=undefined && zfbQRCode!='') {
      
            //清空之前的二维码
            $("#Orcode_div canvas").remove()
            $("#yes_qrcode").hide();
        }
        //生成二维码放入”Orcode_div“ div
        zfbQRCode=$('#Orcode_div').qrcode({
      
            width: 168, //宽度
            height: 168, //高度
            text:url
        });
    }
</script>
</html>
1.2.5效果图 如:图14

在这里插入图片描述

图14

2.交易状态查询

​ 前言:上方我们已经调用了支付宝的预下单功能生成了二维码,接下来需要进行查询交易状态然后提示用户支付成功

注意:查询交易状态不要做任何系统业务逻辑,就算是如果用户支付成功也不要做业务逻辑处理,业务逻辑处理需要再异步回调中处理,下面会讲的

2.1创建findZFB_trade方法

将下列方法复制到AlipayFaceToFace类中

public static String findZFB_trade(ZFBFaceToFaceModel zfbFaceToFaceModel) throws Exception{
    
    CommonUtils commonUtils = new CommonUtils();
    /** 支付宝网关 **/
    String URL = commonUtils.getZFBinfoValue("open_api_domain");

    /** 应用id,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/190/201602493024 **/
    String APP_ID = commonUtils.getZFBinfoValue("appid");

    /** 应用私钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602469554 **/
    String APP_PRIVATE_KEY = commonUtils.getZFBinfoValue("private_key");

    /** 支付宝公钥,如何获取请参考:https://opensupport.alipay.com/support/helpcenter/207/201602487431 **/
    String ALIPAY_PUBLIC_KEY = commonUtils.getZFBinfoValue("alipay_public_key");

    /** 初始化 **/
    AlipayClient alipayClient = new DefaultAlipayClient(URL,APP_ID,APP_PRIVATE_KEY,"json","UTF-8",ALIPAY_PUBLIC_KEY,"RSA2");

    /** 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.query(统一收单线下交易查询) **/
    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();

    /** 设置业务参数 **/
    AlipayTradeQueryModel model = new AlipayTradeQueryModel();

    /** 注:交易号(TradeNo)与订单号(OutTradeNo)二选一传入即可,如果2个同时传入,则以交易号为准 **/
    /** 支付接口传入的商户订单号。如:2020061601290011200000140004 **/
    model.setOutTradeNo(zfbFaceToFaceModel.getOutTradeNo());

    /** 异步通知/查询接口返回的支付宝交易号,如:2020061622001473951448314322 **/
    //model.setTradeNo("2020061622001473951448314322");

    /** 将业务参数传至request中 **/
    request.setBizModel(model);

    /** 第三方调用(服务商模式),必须传值与支付接口相同的app_auth_token **/
    //request.putOtherTextParam("app_auth_token", "传入获取到的app_auth_token值");

    /** 通过alipayClient调用API,获得对应的response类  **/
    AlipayTradeQueryResponse response = alipayClient.execute(request);

    /** 获取接口调用结果,如果调用失败,可根据返回错误信息到该文档寻找排查方案:https://opensupport.alipay.com/support/helpcenter/101 **/
    return response.getBody();
}
2.2创建findZFB_tradeAction方法

将下列方法复制到AlipayFaceToFaceController类中

@RequestMapping("/findZFB_tradeAction")
@ResponseBody
public Map<String,Object> findZFB_tradeAction(HttpServletRequest request,HttpServletResponse response){
    
    Map<String,Object> resultMap=new HashMap<String, Object>();
    try {
    
        //(必填)商户唯一订单编号
        String outTradeNo=request.getParameter("outTradeNo");
        ZFBFaceToFaceModel zfbFaceToFaceModel=new ZFBFaceToFaceModel();
        zfbFaceToFaceModel.setOutTradeNo(outTradeNo);
        //查询交易状态
        String json=AlipayFaceToFace.findZFB_trade(zfbFaceToFaceModel);
        System.out.println(json);
        JSONObject jsonObject=JSONObject.parseObject(json);
        JSONObject jsonobj_two=(JSONObject)jsonObject.get("alipay_trade_query_response");
        //网关返回码,详见文档 https://opendocs.alipay.com/open/common/105806
        String ZFBCode=(String)jsonobj_two.get("code");
        //业务返回码
        String ZFBSubCode=(String)jsonobj_two.get("sub_code");
        //业务返回码描述
        String sub_msg=(String)jsonobj_two.get("sub_msg");
        //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款)
        String trade_status=(String)jsonobj_two.get("trade_status");
        if (ZFBCode.equals("40004") && ZFBSubCode.equals("ACQ.TRADE_NOT_EXIST")) {
    
            //订单未创建(用户未扫码)
            resultMap.put("code", ZFBCode);
            resultMap.put("data", "用户未扫码");
        } else if (ZFBCode.equals("10000") && trade_status.equals("WAIT_BUYER_PAY")) {
    
            //订单已经创建但未支付(用户扫码后但是未支付)
            resultMap.put("code", ZFBCode);
            resultMap.put("data", "non-payment");
        } else if (ZFBCode.equals("10000") && (trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED"))) {
    
            //判断ZFBCode是否等于”10000“ 并且 trade_status等于TRADE_SUCCESS(交易支付成功)或者 trade_status等于TRADE_FINISHED(交易结束,不可退款)
            //订单已支付(用户扫码完成并且支付成功之后)
            resultMap.put("code", ZFBCode);
            resultMap.put("data", "yes-payment");
        } else {
    
            resultMap.put("code", ZFBCode);
            resultMap.put("data", sub_msg);
        }
    } catch (Exception e) {
    
        e.printStackTrace();
    }
    return resultMap;
}
2.2HTML定时调用

将下方代码复制到AlipaQrcodePayment.html页面中

需要再二维码创建完成之后调用该方法。此方法会定时查询交易状态,然后根据自己业务要求编写后面的代码,如跳转页面等。。。

//定时任务
var trade;
//记录是否通知页面“用户已扫码”
var findNumber=true
/**
 * 查询交易状态
 */
function findZFB_trade(){
    
    trade = setInterval(function(){
    
        console.log("每3秒执行一次");
        $.ajax({
    
            url: basePath +'/AlipayFaceToFaceController/findZFB_tradeAction.action',
            method: 'post',
            async: false,  //是否异步请求  默认truefalse为同步请求 ,true为异步请求)
            data: {
    
                "outTradeNo": outTradeNo
            },
            dataType: 'JSON',
            success: function (res) {
    
                if (res.code=='10000' && res.data=='non-payment'){
    
                    //订单已经创建但未支付(用户扫码后但是未支付)
                    if (findNumber){
    
                        console.log("用户已扫码但是未支付");
                        findNumber=false;
                    }
                }else if (res.code=='10000' && res.data=='yes-payment'){
    
                    //阻止定时
                    window.clearInterval(trade);
                    alert("订单已支付,感谢支持。。。");
                }
            }
        })
    },3000);
}
2.4最终效果图

在这里插入图片描述

3.支付宝异步通知

注意:这里再声明一下,为了系统的安全性考虑所有的业务逻辑处理,都需要再异步回调中处理不能再查询交易状态中处理。

异步通知作用:文章上方有说过再查询交易状态不做任何 业务逻辑处理,接下来就需要靠此方法进行业务逻辑代码处理。下面的ZFBcallback支付宝会再用户扫码成功之后会自动调用该方法会传输一些参数告诉服务器这个订单付款成功了然后系统就需要做出一些列的业务,如修改用户的订单状态等等。。

3.1内网穿透

由于我们需要接受支付宝到回调,所以我们的电脑就需要让外网可以访问,所以我们的电脑需要做内网穿透让外网的机子可以访问到我们的接口

具体操作方法各位码农可以百度一下

3.2设置回调接口地址

AlipayFaceToFace类中的ZFBPreorder方法加上下放代码,setNotifyUrl()中的参数填写你的项目地址,如:https://test.domainName.com/project/AlipayFaceToFaceController/ZFBcallback.action 我下方代码写的是zfbFaceToFaceModel.getNotifyUrl(),是从zfbinfo.properties文件中获取的,此地址必须是外网可以正常访问到的,并且接受POST请求(GET不可以)。

 	/** 异步通知地址,以http或者https开头的,商户外网可以post访问的异步地址,用于接收支付宝返回的支付结果,如果未收到该通知可参考该文档进行确认:			https://opensupport.alipay.com/support/helpcenter/193/201602475759 **/
    request.setNotifyUrl(zfbFaceToFaceModel.getNotifyUrl());
3.3编写ZFBcallback回调函数

将下面的方法拷贝到AlipayFaceToFaceController

需要注意的是,在这个方法中你一定一定需要做异步验签,简单来说你就是需要验证这个接口是不是支付宝在调用的,主要是为了安全为了防止不法分子调用你这个方法伪造数据,所以在此方法调用的时候需要做验签。

//异步验签:切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
boolean flag = AlipaySignature.rsaCheckV1(params, alipay_public_key, "utf-8","RSA2");

注意:支付宝再用户支付成功之后支付宝会自动调用下方的方法(前提是你设置了回调接口地址,具体操作请看上方代码),调用完成之后你需要返回“success”支付宝,如果不返回支付宝就会定时调用该方法所以你需要在接收到此方法之后完成一系列您的业务操作然后需要返回“success”字符串。并且注意拦截器是否拦截(如果被被您的登录拦截器拦截了,支付宝就无法访问此方法了)

/**
 * 支付回调函数(当用户支付成功之后,支付宝会自动调用该方法)
 * 此接口需要可以被外网访问而且必须是POST请求,并且注意拦截器是否拦截(如果被被您的登录拦截器拦截了,支付宝就无法访问此方法了)
 * @param request
 * @param response
 */
@RequestMapping("/ZFBcallback")
public void ZFBcallback(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
    try {
    
        CommonUtils commonUtils=new CommonUtils();
        //支付宝公钥
        String alipay_public_key=commonUtils.getZFBinfoValue("alipay_public_key");
        PrintWriter out;
        out = response.getWriter();
        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        //循环遍历支付宝请求过来的参数存入到params中
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
    
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
    
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //异步验签:切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
        boolean flag = AlipaySignature.rsaCheckV1(params, alipay_public_key, "utf-8","RSA2");
        if (flag){
    
            //说明是支付宝调用的本接口
            if (params.get("trade_status").equals("TRADE_SUCCESS") || params.get("trade_status").equals("TRADE_FINISHED")) {
    
                System.out.println("收到回调结果,用户已经完成支付");
                /***
                 * 这里写您的业务逻辑代码
                 */
                out.write("success");
            }
        }else {
    
           	//验签失败该接口被别人调用
            out.write("支付宝异步回调验签失败,请留意");
        }
        out.flush();
        out.close();
    } catch (Exception e) {
    
        e.printStackTrace();
    }
}
3.4最终效果图

走到这里就说明用户支付成功了,然后就可以根据自己的业务需求做相应的业务逻辑处理了
在这里插入图片描述

源码下载

源码获取:点击获取源码
本文介绍了“二维码付款”的代码。其他支付方式的代码都在源码中。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44004020/article/details/111472797

智能推荐

Eclipse中配置WebMagic(已配置好Maven)_使用eclipse搭建webmagic工程-程序员宅基地

文章浏览阅读364次。1.WebMagicWebMagic是一个简单灵活的Java爬虫框架。基于WebMagic,你可以快速开发出一个高效、易维护的爬虫。2.在Eclipse中配置WebMagic1.首先需要下载WebMagic的压缩包官网地址为:WebMagic官网最新版本为:WebMagic-0.7.3,找到对应版本,打开下载界面,注意,下载要选择Source code(zip)版本,随便下载到哪里都可以;2.下载好的压缩包需要解压,此时解压到的位置即为后续新建的Eclipse的project位置,比如我的Ecli_使用eclipse搭建webmagic工程

linux启动mysql_linux如何启动mysql服务_linux启动mysql服务命令是什么-系统城-程序员宅基地

文章浏览阅读1.9k次。mysql数据库是一种开放源代码的关系型数据库管理系统,有很多朋友都在使用。一些在linux系统上安装了mysql数据库的朋友,却不知道该如何对mysql数据库进行配置。那么linux该如何启动mysql服务呢?接下来小编就给大家带来linux启动mysql服务的命令教程。具体步骤如下:1、首先,我们需要修改mysql的配置文件,一般文件存放在/etc下面,文件名为my.cnf。2、对于mysql..._linux中 mysql 启动服务命令

php实现在线oj,详解OJ(Online Judge)中PHP代码的提交方法及要点-程序员宅基地

文章浏览阅读537次。详解OJ(Online Judge)中PHP代码的提交方法及要点Introduction of How to submit PHP code to Online Judge SystemsIntroduction of How to commit submission in PHP to Online Judge Systems在目前常用的在线oj中,codeforces、spoj、uva、zoj..._while(fscanf(stdin, "%d %d", $a, $b) == 2)

java快捷键调字体_设置MyEclipse编码、补全快捷键、字体大小-程序员宅基地

文章浏览阅读534次。一、设置MyEclipse编码(1)修改工作空间的编码方式:Window-->Preferences-->General-->Workspace-->Text file encoding(2)修改一类文件的编码方式:Window-->Preferences-->General-->content Types-->修改default Encoding(..._java修改快捷缩写内容

解析蓝牙原理_蓝牙原理图详解-程序员宅基地

文章浏览阅读1.4w次,点赞19次,收藏76次。1.前言市面上关于Android的技术书籍很多,几乎每本书也都会涉及到蓝牙开发,但均是上层应用级别的,而且篇幅也普遍短小。对于手机行业的开发者,要进行蓝牙模块的维护,就必须从Android系统底层,至少框架层开始,了解蓝牙的结构和代码实现原理。这方面的文档、网上的各个论坛的相关资料却少之又少。分析原因,大概因为虽然蓝牙协议是完整的,但是并没有具体的实现。蓝牙芯片公司只负责提供最底层的API_蓝牙原理图详解

从未在一起更让人遗憾_“从未在一起和最终没有在一起哪个更遗憾”-程序员宅基地

文章浏览阅读7.7k次。图/源于网络文/曲尚菇凉1.今天早上出门去逛街,在那家冰雪融城店里等待冰淇淋的时候,听到旁边两个女生在讨论很久之前的一期《奇葩说》。那期节目主持人给的辩论题是“从未在一起和最终没有在一起哪个更遗憾”,旁边其中一个女生说,她记得当时印象最深的是有个女孩子说了这样一句话。她说:“如果我喜欢一个人呢,我就从第一眼到最后一眼,把这个人爱够,把我的感觉用光,我只希望那些年让我成长的人是他,之后的那些年他喝过..._从未在一起更遗憾

随便推点

Spring Cloud Alibaba 介绍_sprngcloud alba-程序员宅基地

文章浏览阅读175次。Spring Cloud Alibaba 介绍Sping体系Spring 以 Bean(对象) 为中心,提供 IOC、AOP 等功能。Spring Boot 以 Application(应用) 为中心,提供自动配置、监控等功能。Spring Cloud 以 Service(服务) 为中心,提供服务的注册与发现、服务的调用与负载均衡等功能。Sping Cloud介绍官方介绍​ Tools for building common patterns in distributed systems_sprngcloud alba

测试 数据类型的一些测试点和经验_基础字段的测试点-程序员宅基地

文章浏览阅读3.2k次,点赞4次,收藏21次。我这里是根据之前在测试数据类项目过程中的一些总结经验和掉过个坑,记录一下,可以给其他人做个参考,没什么高深的东西,但是如果不注意这些细节点,后期也许会陷入无尽的扯皮当中。1 需求实现的准确度根据产品需求文档描述发现不明确不详细的或者存在歧义的地方一定要确认,例如数据表中的一些字段,与开发和产品确认一遍,如有第三方相关的,要和第三方确认,数据类项目需要的是细心,哪怕数据库中的一个字段如果没有提前对清楚,后期再重新补充,会投入更大的精力。2 数据的合理性根据业务场景/常识推理,提..._基础字段的测试点

一文看懂:行业分析怎么做?_码工小熊-程序员宅基地

文章浏览阅读491次。大家好,我是爱学习的小xiong熊妹。在工作和面试中,很多小伙伴会遇到“对XX行业进行分析”的要求。一听“行业分析”四个字,好多人会觉得特别高大上,不知道该怎么做。今天给大家一个懒人攻略,小伙伴们可以快速上手哦。一、什么是行业?在做数据分析的时候,“行业”两个字,一般指的是:围绕一个商品,从生产到销售相关的全部企业。以化妆品为例,站在消费者角度,就是简简单单的从商店里买了一支唇膏回去。可站在行业角度,从生产到销售,有相当多的企业在参与工作(如下图)在行业中,每个企业常常扮._码工小熊

LLaMA 简介:一个基础的、650 亿参数的大型语言模型_llma-程序员宅基地

文章浏览阅读1.6w次,点赞2次,收藏2次。还需要做更多的研究来解决大型语言模型中的偏见、有毒评论和幻觉的风险。我们在数万亿个令牌上训练我们的模型,并表明可以仅使用公开可用的数据集来训练最先进的模型,而无需诉诸专有和不可访问的数据集。在大型语言模型空间中训练像 LLaMA 这样的小型基础模型是可取的,因为它需要更少的计算能力和资源来测试新方法、验证他人的工作和探索新的用例。作为 Meta 对开放科学承诺的一部分,今天我们公开发布 LLaMA(大型语言模型元 AI),这是一种最先进的基础大型语言模型,旨在帮助研究人员推进他们在 AI 子领域的工作。_llma

强化学习在制造业领域的应用:智能制造的未来-程序员宅基地

文章浏览阅读223次,点赞3次,收藏5次。1.背景介绍制造业是国家经济发展的重要引擎,其产能和质量对于国家经济的稳定和发展具有重要意义。随着工业技术的不断发展,制造业的生产方式也不断发生变化。传统的制造业通常依赖于人工操作和手工艺,这种方式的缺点是低效率、低产量和不稳定的质量。随着信息化、智能化和网络化等新技术的出现,制造业开始向智能制造迈出了第一步。智能制造的核心是通过大数据、人工智能、计算机视觉等技术,实现制造过程的智能化、自动化...

ansible--安装与使用_pip安装ansible-程序员宅基地

文章浏览阅读938次。系列文章目录文章目录系列文章目录 前言 一、ansible是什么? 二、使用步骤 1.引入库 2.读入数据 总结前言菜鸟一只,刚开始使用,仅作以后参考使用。边学习,边记录,介绍一下最基础的使用,可能会有理解不到位的地方,可以共同交流,废话不多说,走起。一、ansible 简介?ansible是自动化运维工具的一种,基于Python开发,可以实现批量系统配置,批量程序部署,批量运行命令,ansible是基于模块工作的,它本身没有批量部署的能力,真正.._pip安装ansible

推荐文章

热门文章

相关标签