# Django 如何对接公众号调起微信支付

我只讲重要的部分,其他像申请什么公众号资料、商户号等都不在这倾述,因为太多了,需要的自己去网上搜一搜别人的案例,看看需要什么

首先在 Django 可以安装 wechatpy 模块,由于微信支付的 sdk 方面并没有很好的支持 Python,至少我没看见(没查到),wechatpy 是第三方微信支付的 sdk,使用该模块可以忽略很多加密的部分,减少代码量,详细可以参考官方文档

https://github.com/wechatpy/wechatpy

另外,wechatpy 也使用到了 cryptography 模块,在 window 安装多半会报错,这时你可以升级一下 pip,在进行安装即可。

# 公众号及证书、域名配置

在公众号后台,找到设置与开发 -> 接口权限,找到 “网页授权获取用户基本信息”,点击设置

注意:如果不是企业号,是不支持微信支付的

前提你需要有能访问的外网,或者自己弄一个内网穿透,才能设置成功,在你点击设置域名时,在注意事项中,你需将提示中的 txt 文件下载,放到你服务器的根目录,确保能访问到即可,这时点击保存修改才能成功,否则会报错提示为找到 txt 文件

你的网页授权域名设置时,比如你的 H5,也要确保 txt 能访问到,如 vue3,直接放到 public 目录即可。对于 JS 接口安全域名,因为使用的是 Django,所以你也需要将 txt 文件放到 templates 目录,因为 Django 默认配置下是在 templates 目录里中查找,然后在 urls 中配置文件路径

from django.urls import path
from django.views.generic.base import TemplateView
urlpatterns = [
    path('MP_verify_3wHwPaaUMrpct6Fu.txt', TemplateView.as_view(template_name="MP_verify_3wHwPaaUMrpct6Fu.txt", content_type="text/plain")),
]

接口权限地址:https://mp.weixin.qq.com/advanced/advanced?action=table&token=162251921&lang=zh_CN

接下来找到设置与开发 -> 基本设置,找到 appId 和 appSecret

在你后端调用获取获取微信用户信息时需要用到,比如 openid,其他的数据看需不需要

获取授权文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

import requests
params = {
  "grant_type": "authorization_code",
  "appid": "appId",
  "secret": "appSecret",
  "code": "前端页面获取的code值,具体看下面"
}
response = requests.get(url="https://api.weixin.qq.com/sns/oauth2/access_token", params=params)

在你的公众号页面中,比如登录页,你要先获取 code 值传递到后端才能得到 access_token,然后根据该 access_token 值才能真正获取到微信用户的基本信息,最重要时 openid,在测试支付时,你必须使用的是正式环境下获取的用户 openid,因为你如果使用测试账户获取的 openid,实际是和正式环境不一样的,需要注意。

在网页中你可以使用跳转的方式获取到 code 值,比如

const redirectUrl = window.location.href
  if (redirectUrl.includes('code')) {
    this.code = redirectUrl.split('?')[1].split('&')[0].split('=')[1]
  } else {
    window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=appId&redirect_uri=${encodeURIComponent(redirectUrl)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect&forcePopup`
}
//snsapi_userinfo 参数会有弹窗提示用户是否授权,可以获取更多的用户信息,而 snsapi_base 是静默授权,只能获取 openid

对于 redirect_uri 参数重定向的链接,你需要在前面先设置好网页授权域名才可以,否则会有问题,我记得是。。。然后还有 appId 参数,比如与公众号的 appId 一致,还有后端。

# Django 生成预支付订单

前面的配置工作应该差不多了,这时回到 Django 中,使用 wechatpy 模块生成预支付订单参数,那么在次之前,你需要在微信商户平台中申请好 API 证书、V2、V3、平台证书,调用 wechatpy 时会用到

微信商户平台:https://pay.weixin.qq.com/index.php/core/home/login

然后将这些证书配置放到 Django 项目,首先我们需要导入 wechatpy 模块

from wechatpy.pay import WeChatPay
client = WeChatPay(
  appid="appId",
  mch_id="商户号ID",
  api_key="v2或v3的密钥值",
  mch_cert="apiclient_cert.pem证书路径",
  mch_key="apiclient_key.pem文件路径"
)
# apiclient_cert.pem 与 apiclient_key.pem 都在你下载下来的证书压缩包里面,解压就能找到

接着我们调用 client 生成预订单参数

create_result = client.order.create(
  trade_type="交易类型,取值的JSAPI", body="商品描述", total_fee="订单总金额,单位为分", notify_url="微信支付成功回调", user_id=openid, out_trade_no="商户订单号", fee_type="默认CNY"
)
# 还有其他参数可以看源码

调用成功的情况下你会得到一个 dict,最主要是里面的 prepay_id 值,我们将 prepay_id 值再传递到 client 里进行二次加密,一定要再做二次加密,不然调用支付弹窗会失败,提示【unable to verify signature】

params = client.jsapi.get_jsapi_params(create_result["prepay_id"])
# 加密成功后你会得到 params: {'appId': '123', 'timeStamp': '123', 'nonceStr': 'abc', 'package': 'prepay_id=123', 'signType': 'MD5', 'paySign': '123abc'}

这是我们才能拿到正确的微信支付参数,然后我们将得到的 params 返回给前端进行调用

// 在微信浏览器中调用,没问题得到调用后会弹出微信支付的窗口,说明成功了
// 但正常情况下你可能还是报错了,大概率提示【URL not registered http://www.xxx.com/xxx/xxx】,看下边
WeixinJSBridge.invoke('getBrandWCPayRequest',
  {
    appId: appId,
    timeStamp: timeStamp,
    nonceStr: nonceStr,
    package: package,
    signType: signType,
    paySign: paySign
  },
  function (res) {
    if (res.err_msg == "get_brand_wcpay_request:ok") {
      console.log("支付成功!")
    }
  }
)

这是因为我们忘记配置支付授权目录了,也是你当前支付页面的路由地址

我们需要在微信商户平台的产品中心 -> 开发配置中,修改支付配置的 JSAPI 支付,在支付授权目录中添加你当前支付页的路劲

要注意,添加路劲时比如加上斜杠 “/” 结尾,所以我们在调用微信支付前还需要修改一下路劲,不然还是会一直提示你地址未注册

history.pushState(null, null, "/xxx/")

那么基本上微信支付接口就没什么问题了,OK!