小程序的支付流程
1.用戶發起請求下單支付
2.保證用戶是登入狀態
3.組織數據,請求統一下單接口,微信官方會同步返回一個prepay_id
4.重新組織數據,進行簽名,將重新組織的數據返回給小程序,小程序再吊起支付
5.用戶可以進行支付,支付結果會同步返回給小程序
6.后台修改訂單支付狀態是通過微信官方服務器的異步通知
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()吊起支付
后端的pay.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
小程序中的pay方法:
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)
},
})
}
})
}