Reading / Coding / Hacking
为了应付一个奇葩的审核需求,给公司官网集成了网页版的微信扫码支付,这里记录一下大致流程。
根据官方文档的描述,我选了看起来最方便的对接流程模式二:
code_url
.code_url
对应的二维码。注意:该模式的预付订单有效期为 2 小时,过期后无法支付。以及每个二维码只能被扫码支付一次。
所谓「统一下单 API」,就是 POST https://api.mch.weixin.qq.com/pay/unifiedorder
, 只不过跟所有微信 API 一样,调用时要给 request payload 签名,而且只能用 XML 编码 🙄️。
计算 request payload 签名:
def calc_signature(data: dict) -> str:
api_key = '<微信商户 API Key>'
pairs = sorted(order.items())
pairs.append(('key', api_key))
encoded = urlencode(pairs, quote_via=no_quote)
return md5(encoded.encode('utf-8')).hexdigest().upper()
将 request payload 编码为 XML:
def dump_as_xml(data) -> str:
root = Element('xml')
for key, value in data.items():
elem = Element(key)
elem.text = str(value)
root.append(elem)
buffer = StringIO()
ElementTree(root).write(buffer, encoding='unicode', xml_declaration=True)
return buffer.getvalue()
用 urlencode()
拼接请求参数时,要指定 quote_via
为一个 stub 函数(原样返回字符串),因为在 XML 里传递的参数并没有被 quote, 需要保持两边的值一致才能通过签名校验。
微信返回的 code_url
是类似 weixin://wxpay/bizpayurl?pr=<ID>
这样的 URL, 跟官方文档示例里的有一点不一样,URL 参数名并不是 sr
而是 pr
.
支付完成后,在「微信商户平台」里能看到订单信息,但是公众号管理员并没有收到通知。
微信 API response header 里没有指定编码, 导致 HTTP client 认为 response body 编码是 ISO-8859-1
, 解析出来的 XML 会有乱码,解决方法是强行指定 resp.encoding = 'utf-8
.