社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

微信二次开发案例,python制作微信支付小程序!

python • 6 年前 • 747 次点击  


    由于最近自己在做小程序的支付,就在这里简单介绍一下讲一下用python做小程序支付这个流程。当然在进行开发之前还是建议读一下具体的流程,清楚支付的过程。

1.支付交互流程


当然具体的参数配置可以参考官方文档

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1

2.获取openid(微信用户标识)

 1 import requests
2
3 from config import APPID, SECRET
4
5
6 class OpenidUtils(object):
7
8 def __init__(self, jscode):
9 self.url = "https://api.weixin.qq.com/sns/jscode2session"
10 self.appid = APPID # 小程序id
11 self.secret = SECRET # 不要跟后面支付的key搞混
12 self.jscode = jscode # 前端传回的动态jscode
13
14 def get_openid(self):
15 # url一定要拼接,不可用传参方式
16 url = self.url + "?appid=" + self.appid + "&secret=" + self.secret + "&js_code=" + self.jscode + "&grant_type=authorization_code"
17 r = requests.get(url)
18 print(r.json())
19 openid = r.json()['openid']
20
21 return openid


3.支付请求

 1 # -*- coding:utf-8 -*-
2 import requests
3 import hashlib
4 import xmltodict
5 import time
6 import random
7 import string
8 import urllib2
9 import sys
10
11
12 class WX_PayToolUtil():
13 """ 微信支付工具 """
14
15 def __init__(self, APP_ID, MCH_ID, API_KEY, NOTIFY_URL):
16 self._APP_ID = APP_ID # 小程序ID
17 self._MCH_ID = MCH_ID # # 商户号
18 self._API_KEY = API_KEY
19 self._UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # 接口链接
20 self._NOTIFY_URL = NOTIFY_URL # 异步通知
21
22 def generate_sign(self, param):
23 '''生成签名'''
24 stringA = ''
25 ks = sorted(param.keys())
26 # 参数排序
27 for k in ks:
28 stringA += (k + '=' + param[k] + '&')
29 # 拼接商户KEY
30 stringSignTemp = stringA + "key=" + self._API_KEY
31 # md5加密,也可以用其他方式
32 hash_md5 = hashlib.md5(stringSignTemp.encode('utf8'))
33 sign = hash_md5.hexdigest().upper()
34 return sign
35
36 '''
37 # python2另外一种实现方法
38 def generate_sign(self, params):
39 ret = []
40 for k in sorted(params.keys()):
41 if (k != 'sign') and (k != '') and (params[k] is not None):
42 ret.append('%s=%s' % (k, params[k]))
43 params_str = '&'.join(ret)
44 params_str = '%(params_str)s&key=%(partner_key)s' % {'params_str': params_str, 'partner_key': key}
45
46 reload(sys)
47 sys.setdefaultencoding('utf8')
48
49 params_str = hashlib.md5(params_str.encode('utf-8')).hexdigest()
50 sign = params_str.upper()
51 return sign
52 '''
53
54 def getPayUrl(self, orderid, openid, goodsPrice, **kwargs):
55 """向微信支付端发出请求,获取url"""
56 key = self._API_KEY
57 nonce_str = ''.join(random.sample(string.letters + string.digits, 30)) # 生成随机字符串,小于32位
58 params = {
59 'appid': self._APP_ID, # 小程序ID
60 'mch_id': self._MCH_ID, # 商户号
61 'nonce_str': nonce_str, # 随机字符串
62 "body": '测试订单', # 支付说明
63 'out_trade_no': orderid, # 生成的订单号
64 'total_fee': str(goodsPrice), # 标价金额
65 'spbill_create_ip': "127.0.0.1", # 小程序不能获取客户ip,web用socekt实现
66 'notify_url': self._NOTIFY_URL,
67 'trade_type': "JSAPI", # 支付类型
68 "openid": openid, # 用户id
69 }
70 # 生成签名
71 params['sign'] = self.generate_sign(params)
72
73 # python3一种写法
74 param = {'root': params}
75 xml = xmltodict.unparse(param)
76 response = requests.post(self._UFDODER_URL, data=xml.encode('utf-8'), headers={'Content-Type': 'text/xml'})
77 # xml 2 dict
78 msg = response.text
79 xmlmsg = xmltodict.parse(msg)
80 # 4. 获取prepay_id
81 if xmlmsg['xml']['return_code'] == 'SUCCESS':
82 if xmlmsg['xml']['result_code'] == 'SUCCESS':
83 prepay_id = xmlmsg['xml']['prepay_id']
84 # 时间戳
85 timeStamp = str(int(time.time()))
86 # 5. 五个参数
87 data = {
88 "appId": self._APP_ID,
89 "nonceStr": nonce_str,
90 "package": "prepay_id=" + prepay_id,
91 "signType": 'MD5',
92 "timeStamp": timeStamp,
93 }
94 # 6. paySign签名
95 paySign = self.generate_sign(data)
96 data["paySign"] = paySign # 加入签名
97 # 7. 传给前端的签名后的参数
98 return data
99
100 # python2一种写法
101 '''
102 request_xml_str = ''
103 for key, value in params.items():
104 if isinstance(value, str):
105 request_xml_str = '%s%s>' % (request_xml_str, key, value, key,)
106 else:
107 request_xml_str = '%s%s%s>' % (request_xml_str, key, value, key,)
108 request_xml_str = '%s
' % request_xml_str
109
110 # 向微信支付发出请求,并提取回传数据
111 res = urllib2.Request(self._UFDODER_URL, data=request_xml_str.encode("utf-8"))
112 res_data = urllib2.urlopen(res)
113 res_read = res_data.read()
114 doc = xmltodict.parse(res_read)
115 return_code = doc['xml']['return_code']
116 if return_code == "SUCCESS":
117 result_code = doc['xml']['result_code']
118 if result_code == "SUCCESS":
119 doc = doc['xml']
120 data = {
121 "appId": self._APP_ID,
122 "nonceStr": nonce_str,
123 "package": "prepay_id=" + doc["prepay_id"],
124 "signType": 'MD5',
125 "timeStamp": str(int(time.time())),
126 }
127 # paySign签名
128 paySign = self.generate_sign(data)
129 data["paySign"] = paySign # 加入签名
130 return data
131 else:
132 err_des = doc['xml']['err_code_des']
133 return err_des
134 else:
135 fail_des = doc['xml']['return_msg']
136 return fail_des
137 '''

当然你可能会遇到的错误有签名错误,一般的情况是你的appSecret和商户号的API密钥两个弄错了,当然如果不是还有可能是其他问题 。

其他的支付方式获取用户的ip地址可以通过 socket.gethostbyname(socket.gethostname()) 方法来获取。

4.支付回调

 1 # 统一下单回调处理
2
3 import xmltodict
4
5 from django.http import HttpResponse
6
7 def payback(request):
8 msg = request.body.decode('utf-8')
9 xmlmsg = xmltodict.parse(msg)
10
11 return_code = xmlmsg['xml']['return_code']
12
13 if return_code == 'FAIL':
14 # 官方发出错误
15 return HttpResponse("""
16
""",
17 content_type='text/xml', status=200)
18 elif return_code == 'SUCCESS':
19 # 拿到这次支付的订单号
20 out_trade_no = xmlmsg['xml']['out_trade_no']
21
22 # 根据需要处理业务逻辑
23
24 return HttpResponse("""
25
""",
26 content_type='text/xml', status=200)

当然微信回调的参数有很多详细可以参考 、https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8

5.安全问题

在使用的过程中 商户系统对于支付结果通知的内容一定要做 签名验证,并校验返回的订单金额是否与商户侧的订单金额一致 ,防止数据泄漏导致出现“假通知”,造成资金损失。

我在开发过程中的解决方式是在向微信支付端发起请求的时候, 把订单号,金额,签名等存入数据库,然后在回调函数那里进行校验判断 。在确认跟前面订单情况一样的情况下,才进行后续一系列的操作。

最后送给大家一段祝福

# _oo8oo_
# o8888888o
# 88" . "88
# (| -_- |)
# 0 = /0
# ___/'==='___
# .' \| |# '.
# / \||| : |||#
# / _||||| -:- |||||_
# | | \ - #/ | |
# | _| ''---/'' |_/ |
# .-__ '-' __/-. /
# ___'. .' /--.-- '. .'___
# ."" '< '._____/___.' >' "".
# | | : `- `.:` _ /`:.`/ -` : | |
# `-. _ __ /__ _/ .-` / /
# =====`-.____`.___ _____/ ___.`____.-`=====
# `=---=`
#
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# 强大爷保佑 永不宕机/永无bug
啦啦啦



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/QuLFpSvZuP
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/26442
 
747 次点击