本文是【淺析微信支付】系列文章的第五篇,主要講解如何調用統一下單接口生成預支付單及調起支付頁面。
淺析微信支付系列已經更新四篇了喲~,沒有看過的朋友們可以看一下哦。
上面是本文的前置文章,有前面幾篇文章的基礎以后看會更加明了,如果已經看過的小伙伴可以忽略。
1、什么是[統一下單接口]?
首先我們要明白這個問題,需要先行看一下微信的官方文檔:
https://pay.weixin.qq.com/wik...
官方解釋如下:
除被掃支付場景以外,商戶系統先調用該接口在微信支付服務后台生成預支付交易單,返回正確的預支付交易會話標識后再按掃碼、JSAPI、APP等不同場景生成交易串調起支付。
什么意思?簡單理解:就是說我們要在調起微信支付窗口之前,需要先生成一個 預支付交易單
,這個單子相當於和我們自身系統的 支付交易單
一一對應,也就是我們每次支付需要記錄的訂單支付交易單。
從上面我們可以得到,在調用此接口之前,首先,我們系統中肯定已經需要有以下步驟:訂單提交 -> 生成訂單 -> 生成訂單對應的支付單 -> 調用統一下單接口
好了,假設系統現在已經生成支付交易單,准備調用統一下單接口,我們來看一下具體的實現方式。
PS:調用統一下單接口時,需要注意的是必須傳入異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數。示例如下:
https://xxx.com/v1/weixin/pay/wxnotify
2、調用接口
示例代碼:
/**
* [微信支付統一下單] - 保存調用的相關記錄 <p>
* [微信支付小程序] - 返回支付喚醒參數
* @param payment 付款對象
* @param user 當前用戶
* @return map
* @throws Exception e
*
* @author yclimb
* @date 2018/6/15
*/
public Map<String, String> saveWxPayUnifiedOrder(Payment payment, User user) throws Exception {
if (payment == null || user == null) {
return null;
}
// 1.調用微信統一下單接口
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
Map<String, String> resultMap = wxPay.unifiedOrder(...);
// 1.1.記錄付款流水
...
// 下單失敗,進行處理
if (WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RETURN_CODE)) ||
WXPayConstants.FAIL.equals(resultMap.get(WXPayConstants.RESULT_CODE))) {
// 處理結果返回,無需繼續執行
resultMap.put(WXPayConstants.RESULT_CODE, WXPayConstants.FAIL);
resultMap.put(WXPayConstants.ERR_CODE_DES, resultMap.get(WXPayConstants.RETURN_MSG));
return resultMap;
}
// 1.2.獲取prepay_id、nonce_str
String prepay_id = resultMap.get("prepay_id");
String nonce_str = resultMap.get("nonce_str");
// 2.根據微信統一下單接口返回數據組裝微信支付參數,返回結果
return wxPay.chooseWXPayMap(prepay_id, nonce_str);
}
以上為一個調用統一下單的接口代碼,主要在於以下兩句:
// 實例化一個微信支付對象,使用單例配置的方式
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
// 直接調用統一下單接口
Map<String, String> resultMap = wxPay.unifiedOrder(...);
微信支付對象WXPay
統一下單接口:
/**
* 作用:統一下單<br>
* 場景:商戶在小程序中先調用該接口在微信支付服務后台生成預支付交易單,返回正確的預支付交易后調起支付。
* 接口鏈接:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
* 是否需要證書:否
* 接口文檔地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
*
* @param notify_url 公眾號用戶openid
* @param body 商品簡單描述,該字段請按照規范傳遞,例:騰訊充值中心-QQ會員充值
* @param out_trade_no 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一
* @param total_fee 訂單總金額,傳入參數單位為:元
* @param spbill_create_ip APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP
* @param goods_tag 訂單優惠標記,用於區分訂單是否可以享受優惠
* @param detail 商品詳情 ,單品優惠活動該字段必傳
* @param timeStart 訂單生成時間,格式為yyyyMMddHHmmss
* @param timeExpire 訂單失效時間,格式為yyyyMMddHHmmss,如2009年12月27日9點10分10秒表示為20091227091010
* @return API返回數據
* @throws Exception e
*/
public Map<String, String> unifiedOrder(String notify_url, String openid, String body, String out_trade_no, String total_fee, String spbill_create_ip, String goods_tag, String detail, Date timeStart, Date timeExpire) throws Exception {
/** 構造請求參數數據 **/
Map<String, String> data = new HashMap<>();
// 字段名 變量名 必填 類型 示例值 描述
// 標價幣種 fee_type 否 String(16) CNY 符合ISO 4217標准的三位字母代碼,默認人民幣:CNY,詳細列表請參見貨幣類型
data.put("fee_type", WXPayConstants.FEE_TYPE_CNY);
// 通知地址 notify_url 是 String(256) http://www.weixin.qq.com/wxpay/pay.php 異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數。
data.put("notify_url", notify_url);
// 交易類型 trade_type 是 String(16) JSAPI 小程序取值如下:JSAPI,詳細說明見參數規定
data.put("trade_type", WXPayConstants.TRADE_TYPE);
// 用戶標識 openid 否 String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI,此參數必傳,用戶在商戶appid下的唯一標識。openid如何獲取,可參考【獲取openid】。
data.put("openid", openid);
// 商品描述 body 是 String(128) 騰訊充值中心-QQ會員充值 商品簡單描述,該字段請按照規范傳遞,具體請見參數規定
data.put("body", body);
// 商戶訂單號 out_trade_no 是 String(32) 20150806125346 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一。詳見商戶訂單號
data.put("out_trade_no", out_trade_no);
// 標價金額 total_fee 是 Int 88 訂單總金額,單位為分,詳見支付金額
// 默認單位為分,系統是元,所以需要*100
data.put("total_fee", String.valueOf(new BigDecimal(total_fee).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue()));
// 終端IP spbill_create_ip 是 String(16) 123.12.12.123 APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP。
data.put("spbill_create_ip", spbill_create_ip);
/** 以下參數為非必填參數 **/
// 訂單優惠標記 goods_tag 否 String(32) WXG 訂單優惠標記,使用代金券或立減優惠功能時需要的參數,說明詳見代金券或立減優惠
if (StringUtils.isNotBlank(goods_tag)) {
data.put("goods_tag", goods_tag);
}
// 商品詳情 detail 否 String(6000) 商品詳細描述,對於使用單品優惠的商戶,改字段必須按照規范上傳,詳見“單品優惠參數說明”
if (StringUtils.isNotBlank(detail)) {
data.put("detail", detail);
// 接口版本號 新增字段,接口版本號,區分原接口,默認填寫1.0。入參新增version后,則支付通知接口也將返回單品優惠信息字段promotion_detail,請確保支付通知的簽名驗證能通過。
data.put("version", "1.0");
}
// 設備號 device_info 否 String(32) 013467007045764 自定義參數,可以為終端設備號(門店號或收銀設備ID),PC網頁或公眾號內支付可以傳"WEB"
data.put("device_info", "WEB");
// 交易起始時間 time_start 否 String(14) 20091225091010 訂單生成時間,格式為yyyyMMddHHmmss,如2009年12月25日9點10分10秒表示為20091225091010。其他詳見時間規則
data.put("time_start", DateTimeUtil.getTimeShortString(timeStart));
// 交易結束時間 time_expire 否 String(14) 20091227091010 訂單失效時間,格式為yyyyMMddHHmmss,如2009年12月27日9點10分10秒表示為20091227091010。
// 訂單失效時間是針對訂單號而言的,由於在請求支付的時候有一個必傳參數prepay_id只有兩小時的有效期,所以在重入時間超過2小時的時候需要重新請求下單接口獲取新的prepay_id。其他詳見時間規則,建議:最短失效時間間隔大於1分鍾
data.put("time_expire", DateTimeUtil.getTimeShortString(timeExpire));
/*// 商品ID product_id 否 String(32) 12235413214070356458058 trade_type=NATIVE時(即掃碼支付),此參數必傳。此參數為二維碼中包含的商品ID,商戶自行定義。
data.put("product_id", null);
// 指定支付方式 limit_pay 否 String(32) no_credit 上傳此參數no_credit--可限制用戶不能使用信用卡支付
data.put("limit_pay", null);
// 附加數據 attach 否 String(127) 深圳分店 附加數據,在查詢API和支付通知中原樣返回,可作為自定義參數使用。
data.put("attach", null);*/
/** 以下五個參數,在 this.fillRequestData 方法中會自動賦值 **/
/*// 小程序ID appid 是 String(32) wxd678efh567hg6787 微信分配的小程序ID
data.put("appid", WXPayConstants.APP_ID);
// 商戶號 mch_id 是 String(32) 1230000109 微信支付分配的商戶號
data.put("mch_id", WXPayConstants.MCH_ID);
// 隨機字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 隨機字符串,長度要求在32位以內。推薦隨機數生成算法
data.put("nonce_str", nonce_str);
// 簽名類型 sign_type 否 String(32) MD5 簽名類型,默認為MD5,支持HMAC-SHA256和MD5。
data.put("sign_type", WXPayConstants.MD5);
// 簽名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 通過簽名算法計算得出的簽名值,詳見簽名生成算法
data.put("sign", sign);*/
// 微信統一下單接口請求地址
Map<String, String> resultMap = this.unifiedOrder(data);
WXPayUtil.getLogger().info("wxPay.unifiedOrder:" + resultMap);
return resultMap;
}
以上代碼詳細說明了每個字段的含義,具體的代碼可以看一下作者的github,文末有對應的地址。
下面說一個特殊情況,在我們支付的時候,有時候用戶會取消支付,等一段時間再重新調起,這時候,需要用到另外一個方法,那就是二次支付,所以,在我們數據庫中,必須保存兩個字段,用於二次支付時使用:預支付IDprepay_id
、隨機字符串nonce_str
,此兩個參數可以生成微信支付調起時需要的驗證簽名。
以下為二次調用時的代碼:
/**
* 根據付款單號查詢微信付款參數
* @param relationId 交易編號
* @param type 支付類型
* @return payment
*
* @author yclimb
* @date 2018/6/28
*/
public Map<String, String> queryChooseWXPayMapByRelationId(Integer relationId, String type) throws Exception {
// 微信支付對象
WXPay wxPay = new WXPay(WXPayConfigImpl.getInstance());
// 1.查詢付款對象
Payment payment = this.queryPaymentByRelationId(relationId, type);
// 2.根據微信統一下單接口返回數據組裝微信支付參數,返回結果
return wxPay.chooseWXPayMap(payment.getPrepayId(), payment.getNonceStr());
}
此時我們已經調用微信統一下單接口成功,並為我們返回了需要的參數,下一步需要組裝為微信支付調起時前端需要的參數。
生成支付簽名
下面為組裝支付簽名的代碼:
/**
* 作用:生成微信支付所需參數,微信支付二次簽名<br>
* 場景:根據微信統一下單接口返回的 prepay_id 生成微信支付所需的參數
* 接口文檔地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
*
* @param prepay_id 預支付id
* @param nonce_str 隨機字符串
* @return 支付方法調用所需參數map
* @throws Exception e
*/
public Map<String, String> chooseWXPayMap(String prepay_id, String nonce_str) throws Exception {
// 支付方法調用所需參數map
Map<String, String> chooseWXPayMap = new HashMap<>();
chooseWXPayMap.put("appId", config.getAppID());
chooseWXPayMap.put("timeStamp", String.valueOf(WXPayUtil.getCurrentTimestamp()));
chooseWXPayMap.put("nonceStr", nonce_str);
chooseWXPayMap.put("package", "prepay_id=" + prepay_id);
chooseWXPayMap.put("signType", WXPayConstants.MD5);
WXPayUtil.getLogger().info("wxPay.chooseWXPayMap:" + chooseWXPayMap.toString());
// 生成支付簽名
String paySign = WXPayUtil.generateSignature(chooseWXPayMap, config.getKey());
chooseWXPayMap.put("paySign", paySign);
WXPayUtil.getLogger().info("wxPay.paySign:" + paySign);
return chooseWXPayMap;
}
組裝好需要的參數以后,就可以調起微信支付窗口了,如果是微信公眾號支付,需要使用以下的方式調起微信支付:
function weixinConfig(appid, timestamp, noncestr, signature) {
wx.config({
debug : false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。
// debug : true,
appId :appid, // 必填,公眾號的唯一標識
timestamp : timestamp, // 必填,生成簽名的時間戳
nonceStr : noncestr, // 必填,生成簽名的隨機串
signature : signature,// 必填,簽名,見附錄1
jsApiList: [
'checkJsApi',
'chooseWXPay' // 必須增加此參數,用戶微信支付功能
]
// 必填,需要使用的JS接口列表,所有JS接口列表見附錄2
});
}
wx.chooseWXPay({
appId: appId,
timestamp: timeStamp, // 支付簽名時間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后台生成簽名使用的timeStamp字段名需大寫其中的S字符
nonceStr: nonceStr, // 支付簽名隨機串,不長於 32 位
package: package, // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=\*\*\*)
signType: signType, // 簽名方式,默認為'SHA1',使用新版支付需傳入'MD5'
paySign: paySign, // 支付簽名
success: function (res) {
// 支付成功后的回調函數
alert('支付成功!');
},
cancel: function (res) {
// 支付取消
},
fail: function (res) {
// 支付失敗
alert("支付失敗!");
}
});
PS:小程序調用方法類似,參數一致。
結語
以上就是微信支付統一下單接口的調用方式了,具體的源碼可以參考作者github,最好在開發之前先通讀一遍微信官方文檔,此時再使用作者源碼開發事半功倍,更易理解。
預告:下一篇文章,作者將講 支付結果通知
,敬請期待!!!
如果想要提前一覽源碼的小伙伴,可以先看看我的 github,地址如下:
https://github.com/YClimb/wxpay-sdk/blob/master/README.md
加作者私人微信,作者微信號如下 yclimb
,標明 微信支付
可拉入微信支付討論群與小伙伴一起探討哦,一定要標明 微信支付
哦~
到此本文就結束了,關注公眾號查看更多推送!!!
本文轉載於:猿2048▶https://www.mk2048.com/blog/blog.php?id=hic0jckaaaa