Java接入支付寶支付教程

毛毛哇發表於2020-12-21

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最終效果圖

走到這裡就說明使用者支付成功了,然後就可以根據自己的業務需求做相應的業務邏輯處理了
在這裡插入圖片描述

相關文章