微信支付接入指引 ( 基于AWS云服务 ) (上)

微信支付接入指引 ( 基于AWS云服务 )

作者: 狠人王某

Email: henrenx@outlook.com

Blog: https://henrenx.cn

一、WEB网站接入

  1. 获取 lambda 函数的动态出口 IP

    • 新建 lambda 函数, 命名为 getLambdaExportIP, 内部具体实现代码如下:
    exports.handler = (event, context, callback) => {
        console.log(JSON.stringify(event));
        const response = {
            statusCode: 200,
            body: event.exportIP
        };
        callback(null, response);
    };
    • 打开 API Gateway, 选择已创建的 API ,新建资源 lambda, 同时创建类型为 GET 的方法

    1571032795890.png

    (注意: 在创建方法时, 需要绑定之前创建的名为 getLambdaExportIP 的函数),

    并启用 CORS, 在方法请求处, 添加名为 x-forwarded-for 的标头,结果如下

    1571032262600.png

    • 在集成请求处, 选择映射模板, 请求正文传递选择未定义模板时, 新建模板, 并输入 application/json, 在模板正文中, 填入以下信息

      {
          "exportIP" : "$input.params('x-forwarded-for')"
      }
 结果如下图所示

1571032893118.png

  1. 调用微信统一下单接口的准备

    • 统一下单接口参数 (只考虑 WEB 接入且只考虑必要的参数)

    详细请参考 => => 微信支付官方文档

    变量名必填类型示例值
    appidString(32)wxd678efh567hg6787
    mch_idString(32)1230000109
    device_infoString(32)013467007045764
    nonce_strString(32)5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    signString(32)C380BEC2BFD727A4B6845133519F3AD6
    sign_typeString(32)MD5
    bodyString(128)腾讯充值中心-QQ会员充值
    detailString(6000)
    out_trade_noString(32)20150806125346
    total_feeInt88
    spbill_create_ipString(64)123.12.12.123
    notify_urlString(256)http://www.weixin.qq.com/wxpay/pay.php
    trade_typeString(16)JSAPI
    product_idString(32)12235413214070356458058

    对于一些固定的参数就不再赘述, 例如 (appid, mch_id)

    nonce_str, 是类型为String的32位以下的随机字符串, 关于其生成可以自己封装也可以采用别人开发好的包, 这里采用 string-random, 使用 npm 安装即可, 下面的包也是用同样的方法。

    out_trade_no, 订单 id, 可以采用 uuid.v4().substring(0, 32) 来生成, 由于官方要求位数小于 32, uuid 生成的字符串大于32位, 可以采用 substring()方法进行截取, 同样的, uuid 也需要安装。

    spbill_create_ip, 也就是一开始费尽波折得到的 lambda 函数的出口 IP, 微信支付后台在得到我们的请求后会校验请求标头 x-forwarded-for 与 这里的 spbill_create_ip,防止请求被劫持修改。

    notify_url, 用户支付成功后, 微信支付后台会异步推送此结果到这个 url, 后面会介绍具体的步骤。

    trade_type, WEB开发采用 NATIVE, 小程序或其他采用 JSAPI,具体需要可以参考微信官方文档主页的 => => 具体介绍

    product_id, 这个是产品 id, 此参数需要你的数据库拥有产品表, 此参数一般由前端传过来。

    sign, 签名验证, 需要根据微信的要求进行 MD5 运算, 具体实现后面介绍。

    • sign签名验证

    我已经实现好了, 代码可以复制即用, 如果想自己实现, 请参考 => => 官方说明

    请注意, 需要提前安装 md5

    let MD5 = require("md5");
    /**
     * 签名算法
     *
     * @param {Object} params
     * @returns
     */
    function sign(params, key) {
      let result = Object.keys(params).sort();
      let len = result.length;
      let stringA = result.reduce((temp, item, index) => {
        if (!params[item]) { return temp }
        return temp += `${item}=${params[item]}${index === len ? '' : '&'}`;
      }, "");
      return MD5(`${stringA}key=${key}`).toUpperCase();
    }
 参数`key`是商户的秘钥,  `params`是前面表格内的参数 
  • notify_url 实现

    打开 API Gateway, 在之前创建的资源 lambda 下再次新建资源, 名为 tenpay, 同时新建POST 方法, 绑定函数 acceptTencentPayResult (函数提前建一个即可), 启用 CORS,

    打开 集成请求, 找到映射模板, 新建 text/xml, 内容如下:

    {
        "xml" : "$util.escapeJavaScript($input.path('$'))"
    }
 关于API Gateway 设置完记得部署, 否则设置不会生效(这一点针对本文所有操作 !!! )

1571036741255.png

  1. 调用统一下单接口

    • 使用 axios, x2js, md5

    axios 是第三方包, 用于发送 httphttps 请求, 使用前需要自行安装

    x2js 是第三方包, 用于把对象转换为 xml, 使用前需安装

    使用方法为

    'use strict';
    let axios = require("axios");
    let x2js = require("x2js");
    let convert = new x2js(); // 这里需要 new 请注意
    let random = require("string-random");
    • 开始调用, 这里使用

      /**
       * 获取统一支付订单
       *
       * @param {Object} event
       * @returns
       */
      function getUnifiedOrder(event) {
        let params = {
          "appid": event.appid,
          "body": "**网页-课程购买",
          "detail": "该笔订单用于购买**课程",
          "device_info": "WEB",
          "mch_id": event.mchId,
          "nonce_str": random(32),
          "notify_url": event.notifyUrl,
          "out_trade_no": event.orderId,
          "product_id": event.productId,
          "sign_type": "MD5",
          "spbill_create_ip": event.ip,
          "total_fee": event.fee,
          "trade_type": "NATIVE",
        };
        params.sign = sign(params, event.key);
        return new Promise((resolve, reject) => {
          let data = convert.js2xml({ xml: params });
          axios.post("https://api.mch.weixin.qq.com/pay/unifiedorder", data)
            .then(res => { resolve(res.data) })
            .catch(err => { reject(err) });
        });
      }
    • 返回结果(Content-Type 类型为 text/xml

    1571037265291.png

    由于涉及隐私, 我已对敏感信息打码。

    • 使用 x2js 将返回的信息进行转换, 获得对象, 把 code_url 返回给前端渲染即可
    • 数据库插入预订单
  2. 微信支付后台推送支付结果

    • 推送内容

    实际内容

    1571038518271.png

    格式化后的内容

    1571038333948.png

    推送的内容是 text/xml 格式, 由于我在 API Gateway的设置, 返回的结果是字符串,需要你使用 x2js 转换为对象格式

    • 修改数据库订单状态
  3. 订单状态获取

由于微信不保证推送结果能推送到我们自己的服务器, 所以不能依赖此消息来维持订单状态

比较好的一种办法如下:

用户支付完成之后点击 我已支付 检查数据库, 如果已支付, 打开相关资源的锁, 如果数据库没有支付, 则调用微信支付订单状态获取API, 获取订单支付结果

具体细节前面有类似步骤, 读者可以自行实现, 这里给出微信官方文档 => => 点我前往

沙发被抢

  1. 表格的语法写错了,建议修改一下,文章不错,赞一个

添加新评论