找回密码
 立即注册
首页 业界区 业界 微信支付集成_JSAPI

微信支付集成_JSAPI

计海龄 6 小时前
微信支付集成_JSAPI

0.背景

产品接入微信支付,需要实现PC端扫码支付,移动端公众号支付,以及小程序支付.经过调研统一采用微信的JSAPI实现.主要过程分两个大步骤:

  • 下单接口(/v3/pay/transactions/jsapi),获取预付单号
  • 切换到微信环境(公众号,小程序)并结合预付单号,唤起支付界面,用户完成支付
需要实际在手机完成支付,本次测试代码全部通过前端实现.针对访问微信下单接口,通过ngin代理避免前端跨域问题.
有完整测试代码,若需要请关注公众号小满小慢回复wepay获取.获取代码后,你只需把src部署到你服务器,然后在微信中访问
即可.如果域名能映射到你本地开发机上,直接npm run server运行也是不错的选择.
这种方式运行,已经内置代理.不需要单独再配置nginx
1.集成前置准备

需要在微信支付平台申请商户号.申请好以后需要在商户号上关联对应的公众号,小程序等.涉及微信支付平台,需要提前准备一下信息

  • 微信支付地址: https://api.mch.weixin.qq.com
  • 商户号ID (mchid)
  • 商户API证书 (apiclient_key.pem)
  • 商户API证书序列号(merchant_serial_no)
  • 公众号或小程序的appid (appid)
  • 测试用户对应公众或小程序的openid (openid)
系统集成需要准备以下信息

  • 备案通过的域名
  • 微信支付回调地址 (notify_url)
商户平台配置

  • 产品中心/AppID账号管理 关联小程序或者公众号
  • 产品中心/开发配置 注册唤起支付url必须一样
2. 测试页面开发

测试页面方便不同的商户号测试,整体分两部分内容

  • 全局配置,主要配置 mchid,apiclient_key.pem,merchant_serial_no,appid,openid,notify_url 配置好以后,信息存储在localStorage中
  • 支付信息,主要设置 支付金额, 支付说明,是否分账.支付金额最多两位小数
整体测试页面如下
1.jpeg

3. JSAPI下单接口

支付接口需要做签名处理,签名采用RAS算法,使用forge.js提供的算法
官方参考文档 https://pay.weixin.qq.com/doc/v3/merchant/4012791856
下单接口的签名串规则如下:
  1. HTTP请求方法\n
  2. URL\n
  3. 请求时间戳\n
  4. 请求随机串\n
  5. 请求报文主体\n
复制代码
实际代码体现如下:
  1. const message =
  2.   "POST\n" +
  3.   "/v3/pay/transactions/jsapi\n" +
  4.   timeStamp + "\n" +
  5.   nonceStr +"\n" +
  6.   JSON.stringify(payData) +"\n";
复制代码
在前端针对timeStamp有些特殊处理

  • 参数要求到秒,前端通过new Date().getTime()是到毫秒的,需要除1000
  • 必须转换成字符串,否则微信接口会报错
生成签名的主要代码如下:
  1. // 小游戏 地心侠士
  2. function getSign(message, privateKeyStr) {
  3.   const privateKey = forge.pki.privateKeyFromPem(privateKeyStr);
  4.   const sha256 = forge.md.sha256.create();
  5.   sha256.update(forge.util.encodeUtf8(message));
  6.   const signature = forge.util.encode64(privateKey.sign(sha256));
  7.   return signature
  8. }
复制代码
最终完整的认证信息如下:
  1. WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",serial_no="${serialNo}",nonce_str="${nonceStr}",timestamp="${timeStamp}",signature="${signature}"
复制代码
前端完整下单代码如下:
  1. async function processPayinfo(cfgInfo, payInfo) {
  2.   const payData =
  3.   {
  4.       "appid": cfgInfo.appid,
  5.       "mchid": cfgInfo.mchid,
  6.       "description": payInfo.description,
  7.       "out_trade_no": payInfo.out_trade_no || new Date().getTime() + "",
  8.       "attach": payInfo.attach,
  9.       "notify_url": cfgInfo.callback_url,
  10.       "support_fapiao": false,
  11.       "amount": {
  12.           "total": Math.round(payInfo.amount * 100),
  13.           "currency": "CNY"
  14.       },
  15.       "payer": {
  16.           "openid": cfgInfo.openid
  17.       },
  18.       "settle_info": {
  19.           "profit_sharing": payInfo.split_payment
  20.       }
  21.   };
  22.   const nonceStr = generateNonceStr();
  23.   const timeStamp = getTimeStamp();
  24.   const mchid = cfgInfo.mchid;
  25.   const method = "POST";
  26.   const payUri = "/v3/pay/transactions/jsapi";
  27.   // 小游戏 地形侠士 签名原始串
  28.   const message =method +"\n" +payUri +"\n" +timeStamp +
  29.                 "\n" +nonceStr +"\n" +JSON.stringify(payData) +"\n";
  30.   // 小游戏 地心侠士 生成签名   
  31.   const serialNo = cfgInfo.merchant_serial_no
  32.   const privateKeyStr = cfgInfo.apiclient_key;
  33.   const signature = getSign(message, privateKeyStr);
  34.   // 小游戏 地心侠士 完整认证串
  35.   let auth = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",serial_no="${serialNo}",nonce_str="${nonceStr}",timestamp="${timeStamp}",signature="${signature}`
  36.   const response = await fetch(payUri, {
  37.       method: method,
  38.       mode: 'cors',
  39.       headers: {
  40.           'Content-Type': 'application/json',
  41.           'Authorization': auth
  42.       },
  43.       body: JSON.stringify(payData)
  44.   });
  45.   if (!response.ok) {
  46.       throw new Error(`HTTP error! status: ${response.status}`);
  47.   }
  48.   const result = await response.json();
  49.   console.log(result)
  50.   return result;
  51. }
复制代码
以上代码,执行成功就会返回预付订单号,类似如下的响应正文
  1. {
  2.     "prepay_id": "wx132023572988633421178322a067e20000"
  3. }
复制代码
4. 唤起微信支付

在上边拿到预付单号以后,就可以在微信环境唤起微信支付界面了.唤起微信支付通用涉及到签名,这里的签名方法一样,但是签名串规则略有变化.签名串规则如下:
  1. appId\n
  2. 时间戳\n
  3. 随机字符串\n
  4. prepay_id=\n
复制代码
这需要注意的是,package并不是下单接口返回的JSON串,需要转换成key=value的形式
完成唤起支付代码如下:
  1. function onBridgeReady(cfgInfo, prepayinfo, cb) {
  2.   const nonceStr = generateNonceStr();
  3.   const timeStamp = getTimeStamp();
  4.   // 小游戏 地心侠士 转换预付单号
  5.   const package = "prepay_id=" + prepayinfo.prepay_id;
  6.   const message =cfgInfo.appid +"\n" +timeStamp +"\n" +
  7.                  nonceStr +"\n" + package +"\n";
  8.   // 小游戏 地心侠士 生成签名   
  9.   const privateKeyStr = cfgInfo.apiclient_key;
  10.   const signature = getSign(message, privateKeyStr);
  11.   WeixinJSBridge.invoke('getBrandWCPayRequest', {
  12.       "appId": cfgInfo.appid,
  13.       "timeStamp": timeStamp,
  14.       "nonceStr": nonceStr,
  15.       "package": package,
  16.       "signType": "RSA",
  17.       "paySign": signature,
  18.   }, function (res) {
  19.       cb && cb(res);
  20.       if (res.err_msg == "get_brand_wcpay_request:ok") {
  21.           console.log("支付成功")
  22.       }
  23.   });
  24. }
复制代码
运行成功后,就会弹出微信支付的页面,测试界面显示如下
2.jpeg

5. nginx跨域配置

整个测试代码完全通过前端JS实现,在访问微信下单接口时会有跨域限制,在实际服务器中,需要通过nginx对微信接口实现代理.关键配置如下
  1. # === 小游戏 地心侠士 微信支付代理配置 ===
  2. location ^~ /v3/pay/ {
  3.   # 目标服务器地址(替换为实际地址)
  4.   proxy_pass https://api.mch.weixin.qq.com;
  5.   
  6.   # 关键头设置
  7.   proxy_set_header Host api.mch.weixin.qq.com;
  8.   proxy_set_header X-Real-IP $remote_addr;
  9.   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  10.   proxy_set_header X-Forwarded-Proto $scheme;
  11.   proxy_set_header User-Agent "WeChat-Pay-Proxy/1.0";
  12.   
  13.   # 超时设置
  14.   proxy_connect_timeout 30s;
  15.   proxy_send_timeout 30s;
  16.   proxy_read_timeout 30s;
  17.   
  18.   # 处理CORS
  19.   add_header 'Access-Control-Allow-Origin' '*' always;
  20.   add_header 'Access-Control-Allow-Methods' '*' always;
  21.   add_header 'Access-Control-Allow-Headers' '*' always;
  22.   
  23.   if ($request_method = 'OPTIONS') {
  24.       add_header 'Access-Control-Allow-Origin' '*';
  25.       add_header 'Access-Control-Allow-Methods' '*';
  26.       add_header 'Access-Control-Allow-Headers' '*';
  27.       return 204;
  28.   }
  29. }
复制代码
6. 总结

微信交互常规的做法是通过后端发送请求到微信服务器,微信服务器返回数据,然后通过前端获取数据,再进行交互。在测试时,前端唤起支付,后端提供预定单接口.前后端都不方便测试支付情况.所有才结合微信提供POSTMAN中接口测试方法,完成这个纯前端的,通用的,微信支付测试页面.如需要完成源码,请在微信公众小满小慢回复wepay获取测试代码
原文地址:https://mp.weixin.qq.com/s/r8kAPXyuWZfN7wHXA7VZbw

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册