微信:支付
一 支付
https://www.cnblogs.com/xiaoyuanqujing/protected/articles/11822104.html
商户系统和微信支付系统主要交互:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
下面的代码具体怎么用代码实现,我们再这里不讲,再后面文档中书写,我们先整理整体的业务逻辑
1小程序登入API解释
再之前的文档中我们已经对小程序的登入做了说明我们不在重复,如果有不懂的,可以参考前面的博客,如果不懂,可以再博客底下留言,我会第一时间回答
2统一下单接口解释
1当用户发起下单请求,且用户是登入状态的情况下,我们就可以创建我们商城的订单了。
2当订单创建完毕,我们应该发起支付,调用支付接口,也就是统一下单接口(这里的下单不是我们商城的下单,而是对微信官方进行支付下单),我们按照微信统一下单接口发送数据,微信下单接口会同步返回数据给我们,也就是上图中prepay_id
3再次签名
1当我们拿到统一下的那数据以后,我们要再次进行签名(对数据加密以及处理)。
2 然后将数据发送给我们小程序。
3 小程序拿到我们发送的数据后调起支付界面,这样用户就可以进行支付了,如果支付成功,微信会直接返回 结果给我们小程序
4异步通知接口
1在第三步再次签名中我们小程序已经知道用户是否支付成功,但是我们后端还不知道该订单是否支付成功
2 基于1的问题,微信会以异步的方式通知我们后端程序,我们拿到微信异步通知的数据,我们就可以对订单 进行修改。那这样就实现了,前后都知道用户的支付结果。
示例:
1.前端
2.后端
2.1目录
配置文件
1.settings.py AppId="wx29fad388b1f51644" AppSecret="d00c23ad3faf96ca218c20f6aaece7a7" code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code" #商户号 pay_mchid ='1415981402' pay_apikey = 'xi34nu5jn7x2uujd8u4jiijd2u5d6j8e' 2.wx_login.py from app01.wx import settings import requests def login(code): response=requests.get(settings.code2Session.format(settings.AppId,settings.AppSecret,code)) data=response.json() if data.get("openid"): return data else: return False 3.wxbizdatacrypt.py import base64 import json from Crypto.Cipher import AES from app01.wx import settings class WXBizDataCrypt: def __init__(self, appId, sessionKey): self.appId = appId self.sessionKey = sessionKey def decrypt(self, encryptedData, iv): # base64 decode sessionKey = base64.b64decode(self.sessionKey) encryptedData = base64.b64decode(encryptedData) iv = base64.b64decode(iv) cipher = AES.new(sessionKey, AES.MODE_CBC, iv) decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData))) if decrypted['watermark']['appid'] != self.appId: raise Exception('Invalid Buffer') return decrypted def _unpad(self, s): return s[:-ord(s[len(s)-1:])] @classmethod def getInfo(cls,encryptedData,iv,session_key): return cls(settings.AppId,session_key).decrypt(encryptedData, iv)
cbv
from rest_framework.views import APIView from rest_framework.response import Response from app01.wx import wx_login from django.core.cache import cache import hashlib,time import random from app01.wx import settings import requests class Pay(APIView): def post(self,request): param=request.data if param.get("login_key"): #缓存获取openid和session_key openid,session_key=cache.get(param.get("login_key")).split("&") self.openid=openid #如果是Nginx做的负载就要HTTP_X_FORWARDED_FOR if request.META.get('HTTP_X_FORWARDED_FOR'): self.ip =request.META['HTTP_X_FORWARDED_FOR'] else: #如果没有用Nginx就用REMOTE_ADDR self.ip = request.META['REMOTE_ADDR'] #调用自定义pay函数 data = self.pay() return Response({"code":200,"msg":"ok","data":data}) else: return Response({"code":200,"msg":"缺少参数"}) #获取随机字符串函数(根据业务场景来) def get_str(self): str_all="1234567890abcdefghjklmasdwery" nonce_str="".join(random.sample(str_all,20)) return nonce_str #获取订单号(根据实际场景来) def get_order(self): order_id=str(time.strftime("%Y%m%d%H%M%S")) return order_id # def xml_to_dict(self,data): import xml.etree.ElementTree as ET xml_dict={} data_dic=ET.fromstring(data) for item in data_dic: xml_dict[item.tag]=item.text return xml_dict #签名 def get_sign(self): data_dic = { "nonce_str": self.nonce_str, "out_trade_no": self.out_trade_no, "spbill_create_ip": self.ip, "notify_url": self.notify_url, "openid": self.openid, "body": self.body, "trade_type": "JSAPI", "appid": self.appid, "total_fee": self.total_fee, "mch_id": self.mch_id } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) return md5.hexdigest().upper() #支付函数 def pay(self): self.appid=settings.AppId #商户号(封装文件中获取) self.mch_id=settings.pay_mchid #随机字符串(上面自定义函数获取) self.nonce_str=self.get_str() #商品描述(实际情况来) self.body="饼哥生活费" #订单号(自定义函数获取) self.out_trade_no=self.get_order() #支付金额 self.total_fee=1 #终端ip self.spbill_create_ip=self.ip #异步回调地址,根据实际情况来 self.notify_url="http://www.baidu.com" #交易类型 self.trade_type="JSAPI" #签名(自定义函数) self.sign = self.get_sign() #数据格式替换 data=f''' <xml> <appid>{self.appid}</appid> <body>{ self.body}</body> <mch_id>{self.mch_id}</mch_id> <nonce_str>{self.nonce_str}</nonce_str> <notify_url>{self.notify_url}</notify_url> <openid>{self.openid}</openid> <out_trade_no>{self.out_trade_no}</out_trade_no> <spbill_create_ip>{self.spbill_create_ip}</spbill_create_ip> <total_fee>{self.total_fee}</total_fee> <trade_type>{self.trade_type}</trade_type> <sign>{self.sign}</sign> </xml> ''' url="https://api.mch.weixin.qq.com/pay/unifiedorder" #向微信官方服务接口发送数据,response为xml格式数据 response=requests.post(url,data.encode("utf-8"),headers={"content-type":"application/xml"}) #调用自定义函数将xml格式数据转成字典 res_data=self.xml_to_dict(response.content) #二次调用自定义签名 data=self.two_sign(res_data["prepay_id"]) return data #二次签名函数 def two_sign(self,prepay_id): timeStamp=str(int(time.time())) nonceStr=self.get_str() data_dict={ "appId":settings.AppId, "timeStamp":timeStamp, #时间戳 "nonceStr":nonceStr, #随机字符串 "package":f"prepay_id={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().upper() data_dict["paySign"]=sign data_dict.pop("appId") return data_dict def get_access_token(): accesston=requests.get()