小程序支付流程
- 用户发起请求下单支付
- 我们要保证用是登入状态。
- 组织数据,请求统一下单接口,微信官方会同步返回一个prepay_id
- 重新组织数据,进行签名,将重新组织的数据返回给小程序,小程序在吊起支付
- 用户就可以进行支付,支付结果会同步返回给小程序
- 后台修改订单支付状态是通过微信官方服务器的异步通知
wxml
<button bindtap="pay">下单支付</button>
js
// pages/test3/test3.js
const app = getApp()
Page({
pay:function(){
wx.request({
url: app.globalData.baseurl+"pay/",
data:{"money":1,token:wx.getStorageSync('token')},
method:"POST",
success (e) {
console.log("支付数据",e)
wx.requestPayment(
{
'timeStamp':e.data.data.timeStamp,
'nonceStr': e.data.data.nonceStr,
'package': e.data.data.package,
'signType': e.data.data.signType,
'paySign': e.data.data.paySign,
'success':function(res){
console.log("成功",res)
},
'fail':function(res){
console.log("失败",res)
},
})
}
})
}
})
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.core.cache import cache
from app01.wx import settings
import hashlib,requests,time
class Pay(APIView):
def post(self,request):
param = request.data
if param.get("token") and param.get("money"):
openid_session_key = cache.get(param.get("token"))
if openid_session_key:
# 获取客户端ip,如果是负载均衡,就用HTTP_X_FORWARDED_FOR,如果不是就用下面的
# nginx 转发:--》访问是nginx,->nginx -> uwsgi
if request.META.get('HTTP_X_FORWARDED_FOR'):
#有负载均衡就用这个
self.ip = request.META['HTTP_X_FORWARDED_FOR']
else:
#没有负载均衡就用这个
self.ip = request.META['REMOTE_ADDR']
self.openid =openid_session_key.split("&")[1]
self.money =param.get("money")
data = self.get_pay_data()
return Response({"code":0,"msg":"ok","data":data})
else:
return Response({"code": 2, "msg": "token无效"})
else:
return Response({"code":1,"msg":"缺少参数"})
def get_nonce_str(self):
import random
data = "123456789abcdefghijklmn"
nonce_str = "".join(random.sample(data,10))
#random.sample(从哪里取,取多小个),变成列表
return nonce_str
def get_order_id(self):
import time
import random
data = "123456789abcdefghijklmn"
order_no = str(time.strftime("%Y%m%d%H%M%S"))+"".join(random.sample(data, 5))
return order_no
def get_sign(self):
data_dict ={
"appid" : self.appid,
"mch_id":self.mch_id,
"nonce_str" : self.nonce_str,
"body" : self.body,
"out_trade_no" : self.out_trade_no,
"total_fee" : self.total_fee,
"spbill_create_ip" : self.ip,
"notify_url" : self.notify_url,
"trade_type" : self.trade_type,
"openid" : self.openid,
}
sign_str = "&".join([f"{k}={data_dict[k]}" for k in sorted(data_dict)])
sign_str = f"{sign_str}&key={settings.pay_apikey}"
print("sign_str", sign_str)
md5 = hashlib.md5()
md5.update(sign_str.encode("utf-8"))
sign = md5.hexdigest()
return sign.upper()
def xml_to_dict(self,xml_data):
import xml.etree.ElementTree as ET
xml_dict ={}
root = ET.fromstring(xml_data)
for child in root:
xml_dict[child.tag]= child.text
return xml_dict
def get_two_sign(self,data):
data_dict = {
"appId":settings.AppId,
"timeStamp":str(int(time.time())),
"nonceStr":data['nonce_str'],
"package":f"prepay_id={data['prepay_id']}",
"signType":"MD5"
}
sign_str = "&".join([f"{k}={data_dict[k]}" for k in sorted(data_dict)])
sign_str = f"{sign_str}&key={settings.pay_apikey}"
md5 = hashlib.md5()
md5.update(sign_str.encode("utf-8"))
sign = md5.hexdigest()
return sign.upper() , data_dict['timeStamp']
def get_pay_data(self):
self.appid = settings.AppId
self.mch_id = settings.pay_mchid
self.nonce_str = self.get_nonce_str()
self.body = "老男孩学费"
self.out_trade_no = self.get_order_id()
self.total_fee =self.money
self.spbill_create_ip =self.ip
self.notify_url = "htttp://www.test.com"
self.trade_type ="JSAPI"
self.openid = self.openid
self.sign = self.get_sign()
body_data = f'''
<xml>
<appid>{self.appid}</appid>
<mch_id>{self.mch_id}</mch_id>
<nonce_str>{self.nonce_str}</nonce_str>
<body>{self.body}</body>
<out_trade_no>{self.out_trade_no}</out_trade_no>
<total_fee>{self.total_fee}</total_fee>
<spbill_create_ip>{self.spbill_create_ip}</spbill_create_ip>
<notify_url>{self.notify_url}</notify_url>
<trade_type>{self.trade_type }</trade_type>
<openid>{self.openid }</openid>
<sign>{self.sign}</sign>
</xml>
'''
url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
# 如果发送的xml数据要把数据转化二进制。body_data.encode("utf-8")
# request
response = requests.post(url,data=body_data.encode("utf-8"),headers = {"content-type":"application/xml"} )
#接收一个二进制的响应
data_dict = self.xml_to_dict(response.content)
pay_sign,timeStamp = self.get_two_sign(data_dict)
data = {
"timeStamp": timeStamp,
"nonceStr": data_dict['nonce_str'],
"package": f"prepay_id={data_dict['prepay_id']}",
"signType": "MD5",
"paySign":pay_sign
}
return data
xml解析模块
<xml>
<appid name="属性值" >{.child.text}</appid>
child.tag表示appid
</xml>
import xml.etree.ElementTree as ET
如果我们要解析一个xml文件
tree = ET.parse('country_data.xml')
root = tree.getroot()
如果解析字符串
root = ET.fromstring(country_data_as_string)
这个root是 Element
for child in root:
print(child.tag, child.attrib)
#child.tag表是标签名,child.attrib表示获取属性
#child.text就表示获取内容
小程序支付总结
1 接收到支付请求。我们先组织数据,然后进行统一下单前的签名
- 请求的数据与响应的数据都是xml.请求的时候,xml数据要变成二进制,heards中的content-type:"application/xml"
-响应的数据也是xml,我要用xml.etree.ElementTree将他转化为字典
2 拿到统一下单数据,最重要的prepay_id,进行再次签名。把一下数据发送给小程序。
"timeStamp": 时间戳
"nonceStr":随机字符串
"package": f"prepay_id={data_dict['prepay_id']}",统一下单中的到的prepay_id
"signType": "MD5",
"paySign":通过上面数据进行加密的结果
3 小程序掉用wx.resquestPayment()吊起支付
以后再遇支付文档
1 统一下单
签名:80%。
签名方式没有搞懂。用了哪些数据,传过去的数据,和签名的数据不一致。
appid = 123 传过去,appid =456
appid 123 --->apid
每一个数据的类型,长短,意义是什么?一定要搞清楚,
2异步回调