前言
微信支付官方文檔: 小程序開發與支付、服務商的關系,參考這個文檔
1、第三方(服務商)自己申請賬號,自己開發,生成指定內頁給特約商戶用,該模式簡稱中心化模式。
2、以特約商戶身份申請小程序appId,第三方完成開發,該模式簡稱外包模式。 3、通過開放平台第三方開發者代特約商戶進行小程序的開發,該模式簡稱第三方模式。
本文適用於中心化模式,服務商自己開發一個小程序,但是收款是直接受到對應的特約商戶賬戶中,不收到服務商自己賬戶中。
基本流程
- 服務商再微信后台申請微信小程序
- 小程序開通微信支付或綁定已開通微信支付的商戶號
特約商戶需要操作的流程
- 服務商再微信支付商戶后台,為特約商戶開通微信服務商模式下的微信支付賬戶
- 特約商戶在收到微信郵件發送的登錄賬號和密碼,登錄自己的微信支付商戶后台,綁定小程序的appId
- 提交審核,被拒絕的話再次提交審核,直到審核通過
- 服務商管理后台中找到"待關聯商戶號"並確認
- 登錄微信支付服務商商戶后台,手動為特約商戶綁定與服務商主體或特約商戶主體一致的公眾號,APP或小程序的appId。最多配置5個,手工配置路徑:"服務商商戶平台-服務商功能-子商戶管理-開發配置-特約商戶APPID配置"。
支付部分
- appid:注意這里是服務號的appid,不是小程序的
- mch_id:這里是用服務商的id 在我的賬號一欄可以找到
- sub_appid: 這里才是小程序的appid
- sub_mch_id: 這里對應特約商戶號id 付款到對應商戶的憑證就是這個 在注冊特約商戶的時候郵件里可以找到 這里建議配置到數據庫動態傳遞
- nonce_str: 隨機字符串
- body: 這里隨意填寫,也可以填寫商品名稱
- out_trade_no: 訂單號
- total_fee: 這里必須是整數,單位是分
- trade_type: 公眾平台支付或小程序支付填寫:JSAPI,如果是APP的填寫:APP
- sub_openid: 此參數是在發起支付前在小程序內調起wx.login 方法獲得code 然后后台通過置換 獲得用戶openid
- spbill_create_ip:這里可以隨意填寫
- notify_url: 支付回調的地址
- sign: 此參數為簽名參數 需要將需要傳遞的參數進行排序並且進行md5簽名,需要注意的是需添加參數key 即之前修改的服務商api密鑰
好了 參數分析完畢 在后台調用統一下單方法 不出意外是成功的,下單代碼如下:
Controller部分
@RestController @RequestMapping("/payment") public class WxPayController { private Logger logger = LoggerFactory.getLogger(WxLoginController.class); @Autowired private AppletOrderService appletOrderService; @Autowired private WxPayProperties wxPayProperties; @ResponseBody @PostMapping(value = "/appletWxPay", produces = "application/json;charset=UTF-8") public Map<Object, Object> appletWxPay(@RequestParam String openId, String totalFee) throws Exception { logger.info("[WxPayController].appletWxPay...openId:{}", openId); SortedMap<Object, Object> resultMap = new TreeMap<Object, Object>(); String body = "測試"; String out_trade_no = String.valueOf(IdWorker.getInstance().nextId()); PreOrderResult preOrderResult = appletOrderService.placeOrder(body, out_trade_no, totalFee, openId); System.out.println(preOrderResult); if(WxContants.SUCCESS.equals(preOrderResult.getReturn_code()) && WxContants.SUCCESS.equals(preOrderResult.getResult_code())){ resultMap.put("appId", wxPayProperties.getApp_id()); resultMap.put("timeStamp", Long.toString(System.currentTimeMillis()/1000)); resultMap.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()); resultMap.put("package", "prepay_id="+preOrderResult.getPrepay_id()); resultMap.put("signType", "MD5"); resultMap.put("sign", SignUtils.createSignByMd5(resultMap, wxPayProperties.getKey())); resultMap.put("returnCode", "SUCCESS"); resultMap.put("returnMsg", "OK"); logger.info("【小程序支付】統一下單成功,返回參數:"+resultMap); }else{ resultMap.put("returnCode", preOrderResult.getReturn_code()); resultMap.put("returnMsg", preOrderResult.getReturn_msg()); logger.info("【小程序支付】統一下單失敗,失敗原因:{}" + preOrderResult.getReturn_msg()); } logger.info("[WxPayController].appletWxPay...CodeUrl:{}", preOrderResult.getCode_url()); return resultMap; } }
Serivice部分
@Service public class AppletOrderServiceImpl implements AppletOrderService{ @Autowired private WxPayProperties wxPayProperties; @Override public PreOrderResult placeOrder(String body, String out_trade_no, String total_fee, String openId) throws Exception { // 生成預付單對象 PreOrder preOrder = new PreOrder(); preOrder.setAppid(wxPayProperties.getApp_id()); preOrder.setMch_id(wxPayProperties.getMch_id()); preOrder.setSub_appid(wxPayProperties.getSub_app_id()); preOrder.setSub_mch_id(wxPayProperties.getSub_mch_id()); String nonce_str = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(); preOrder.setNonce_str(nonce_str); preOrder.setBody(body); preOrder.setOut_trade_no(out_trade_no); preOrder.setTotal_fee(new BigDecimal(total_fee)); preOrder.setSpbill_create_ip(wxPayProperties.getSpbill_create_ip()); preOrder.setNotify_url(wxPayProperties.getNotify_url()); preOrder.setTrade_type(WxContants.TRADE_TYPE); preOrder.setSub_openid(openId); SortedMap<Object, Object> p = new TreeMap<Object, Object>(); p.put("appid", wxPayProperties.getApp_id()); p.put("mch_id", wxPayProperties.getMch_id()); p.put("sub_appid", wxPayProperties.getSub_app_id()); p.put("sub_mch_id", wxPayProperties.getSub_mch_id()); p.put("body", body); p.put("nonce_str", nonce_str); p.put("out_trade_no", out_trade_no); p.put("total_fee", total_fee); p.put("spbill_create_ip", wxPayProperties.getSpbill_create_ip()); p.put("notify_url", wxPayProperties.getNotify_url()); p.put("trade_type", WxContants.TRADE_TYPE); p.put("sub_openid", openId); // 簽名 String sign = SignUtils.createSignByMd5(p, wxPayProperties.getKey()); preOrder.setSign(sign); String xml = XmlUtil.object2Xml(preOrder, PreOrder.class); // 調用下單地址 String returnXml = HttpUtil.sendPost(WxContants.pay_url, xml); System.out.println(returnXml); // XML轉換為Object PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class); // XML轉換為Object // 一般企業開發中支付流水入庫,支付狀態更新這些都需要做,此出省略 return preOrderResult; } @Override public PayResult getWxPayResult(HttpServletRequest request) throws Exception { InputStream inStream = request.getInputStream(); BufferedReader in = null; String result = ""; in = new BufferedReader( new InputStreamReader(inStream)); String line; while ((line = in.readLine()) != null) { result += line; } PayResult pr = (PayResult)XmlUtil.xml2Object(result, PayResult.class); System.out.println(pr.toString()); return pr; } }
小程序配置部分
@Component public class WxPayProperties { @Value("${wxpay.app_id}") private String app_id; @Value("${wxpay.sub_app_id}") private String sub_app_id; @Value("${wxpay.spbill_create_ip}") private String spbill_create_ip; @Value("${wxpay.key}") private String key; @Value("${wxpay.mch_id}") private String mch_id; @Value("${wxpay.sub_mch_id}") private String sub_mch_id; @Value("${wxpay.notify_url}") private String notify_url; // 此處省略get/set方法 ... }
返回數據
{
"appId": "wxe670bb9ea4775345", "nonceStr": "536D9056202D4292A909392320E2E5BB", "package": "prepay_id=wx13143641616855cfa3275610dd2a070000", "returnCode": "SUCCESS", "returnMsg": "OK", "sign": "C512D4025134C356BFA58A2F5699E198", "signType": "MD5", "timeStamp": "1610519802" }
小程序端根據后台返回的參數,拉起支付,代碼如下:
wx.requestPayment({
'timeStamp': res.data.timeStamp, 'nonceStr': res.data.nonceStr, 'package': res.data.package, 'signType': res.data.signType, 'paySign': res.data.sign, 'success':function(res){}, 'fail':function(res){}, 'complete':function(res){} })
點擊支付,總算是來到了這一步:
過程中,可能會遇到如下問題:
出現這個錯誤的原因是簽名不正確,多檢查檢查是哪一步出現了問題。
最后
一路踩了不少坑,總算還是成功了,因此將解決方法記錄下來,后面做小程序支付功能的小伙伴可以避免踩坑。
__EOF__
作 者:Jerry
出 處:https://www.cnblogs.com/jerry0612/p/14272073.html
關於博主:編程路上的小學生,熱愛技術,喜歡專研。評論和私信會在第一時間回復。或者直接私信我。
版權聲明:署名 - 非商業性使用 - 禁止演繹,協議普通文本 | 協議法律文本。
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是博主的最大動力!