由于最近自己在做小程序的支付,就在这里简单介绍一下讲一下用python做小程序支付这个流程。当然在进行开发之前还是建议读一下具体的流程,清楚支付的过程。
1.支付交互流程
当然具体的参数配置可以参考官方文档:
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
2.获取openid(微信用户标识)
import requestsfrom config import APPID, SECRETclass OpenidUtils(object):def __init__(self, jscode):self.url = "https://api.weixin.qq.com/sns/jscode2session"self.appid = APPID # 小程序idself.secret = SECRET # 不要跟后面支付的key搞混self.jscode = jscode # 前端传回的动态jscodedef get_openid(self):# url一定要拼接,不可用传参方式url = self.url + "?appid=" + self.appid + "&secret=" + self.secret + "&js_code=" + self.jscode + "&grant_type=authorization_code"r = requests.get(url)print(r.json())openid = r.json()['openid']return openid
3.支付请求
# -*- coding:utf-8 -*-import requestsimport hashlibimport xmltodictimport timeimport randomimport stringimport urllib2import sysclass WxPayService():""" 微信支付工具 """def __init__(self, APP_ID, MCH_ID, API_KEY, NOTIFY_URL):self._APP_ID = APP_ID # 小程序IDself._MCH_ID = MCH_ID # # 商户号self._API_KEY = API_KEYself._UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # 接口链接self._NOTIFY_URL = NOTIFY_URL # 异步通知def generateSign(self, param):'''生成签名'''stringA = ''ks = sorted(param.keys())# 参数排序for k in ks:stringA += (k + '=' + param[k] + '&')# 拼接商户KEYstringSignTemp = stringA + "key=" + self._API_KEY# md5加密,也可以用其他方式hash_md5 = hashlib.md5(stringSignTemp.encode('utf8'))sign = hash_md5.hexdigest().upper()return signdef getPayUrl(self, orderid, openid, goodsPrice, **kwargs):"""向微信支付端发出请求,获取url"""key = self._API_KEYnonce_str = ''.join(random.sample(string.letters + string.digits, 30)) # 生成随机字符串,小于32位params = {'appid': self._APP_ID, # 小程序ID'mch_id': self._MCH_ID, # 商户号'nonce_str': nonce_str, # 随机字符串"body": '测试订单', # 支付说明'out_trade_no': orderid, # 生成的订单号'total_fee': str(goodsPrice), # 标价金额'spbill_create_ip': "127.0.0.1", # 小程序不能获取客户ip,web用socekt实现'notify_url': self._NOTIFY_URL,'trade_type': "JSAPI", # 支付类型"openid": openid, # 用户id}# 生成签名params['sign'] = self.generate_sign(params)# python3一种写法param = {'root': params}xml = xmltodict.unparse(param)response = requests.post(self._UFDODER_URL, data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'})# xml 2 dictmsg = response.textxmlmsg = xmltodict.parse(msg)# 4. 获取prepay_idif xmlmsg['xml']['return_code'] == 'SUCCESS':if xmlmsg['xml']['result_code'] == 'SUCCESS':prepay_id = xmlmsg['xml']['prepay_id']# 时间戳timeStamp = str(int(time.time()))# 5. 五个参数data = {"appId": self._APP_ID,"nonceStr": nonce_str,"package": "prepay_id=" + prepay_id,"signType": 'MD5',"timeStamp": timeStamp,}# 6. paySign签名paySign = self.generate_sign(data)data["paySign"] = paySign # 加入签名# 7. 传给前端的签名后的参数return data
当然你可能会遇到的错误有签名错误,一般的情况是你的appSecret和商户号的API密钥两个弄错了,当然如果不是还有可能是其他问题,解决方案链接https://www.cnblogs.com/wanghuijie/p/wxpay_sign_error.html
其他的支付方式获取用户的ip地址可以通过socket.gethostbyname(socket.gethostname())方法来获取。
4.支付回调
import xmltodictfrom flask import request, g, make_responsefrom app.services import wxpay_serviceclass WxPayNotify():def post(self):data = """<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"""resp = make_response(data, 200)resp.headers["Content-Type"] = 'text/xml'try:msg = request.data# service_logger.info("回调信息1:{}".format(msg))xmlmsg = xmltodict.parse(msg)# service_logger.info("回调信息2:{}".format(xmlmsg))return_code = xmlmsg['xml']['return_code']logger.info("return_code:{}".format(return_code))if return_code == 'SUCCESS':# 处理支付成功后的业务TODOwxpay_service.wx_pay_notify(xmlmsg['xml'])except Exception as e:logger.info("微信支付回调通知失败:{}".format(e))returnreturn resp
当然微信回调的参数有很多详细可以参考https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8
在回调的时候可能遇到这样一个问题,支付成功以后没有调回调函数,有可能是回调地址是https然后改为http就行,遇到过这个坑,具体原因也不知道。服务器没有屏蔽https访问,https证书也没有问题,把https改为http最后就可以了。
5.安全问题
在使用的过程中商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
我在开发过程中的解决方式是在向微信支付端发起请求的时候,把订单号,金额,签名等存入数据库,然后在回调函数那里进行校验判断。在确认跟前面订单情况一样的情况下,才进行后续一系列的操作。
文章转载自Python效率工程,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




