一。登录接口。
官方文档https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
在登录接口中,有以下步骤:
1.通过微信函数wx获取code,request到后端。
2.获取开发者的appid和secret和code传输到开发者url
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
3.获得openid和session_key自关联到数据库(open随着用户信息保存到数据库,session_key返回到前端保存)
4。在随后的业务中带上openid获取用户,返回业务数据。
用户视图:
from rest_framework.views import APIView from rest_framework.response import Response from django.core.cache import cache from app01 import models import hashlib,time from app01.wx import wx_Login class Login(APIView): def post(self,request): param=request.data if param.get("code"): data=wx_Login.login(param.get("code")) if data: val=data['openid']+"&"+data["session_key"] key=str(int(time.time()))+data['openid'] md5=hashlib.md5() md5.update(key.encode("utf-8")) key=md5.hexdigest() cache.set(key,val) has_user=models.Wxuser.objects.filter(openid=data['openid']).first() if not has_user: models.Wxuser.objects.create(openid=data['openid']) return Response({"code":200,"msg":"ok",'data':{"login_key":key}}) else: return Response({"code":200,"msg":"code错误"}) else: return Response({"code": 200, "msg": "缺少参数"})
import requests from app01.wx import settings def login(code): code_url = settings.code2Session.format(settings.AppId, settings.AppSecret, code) response = requests.get(code_url) json_response = response.json() if json_response.get('session_key'): return json_response else: return False
AppId="" AppSecret="" code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code" pay_mchid ='' pay_apikey = ''
二。授权流程
在这个授权流程中,session_key是会过期的,所以要通过checksession来判断session是否过期,
通过getsession和getuserinfo来获取用户当前设置,和用户的信息。
通过登录的session_key,前端传输的encryptedData,iv,login_key来解密出个人信息
pip3 install pycryptodome
import base64 import json from Crypto.Cipher import AES from . 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,sessionKey): return cls(settings.AppId, sessionKey).decrypt(encryptedData, iv)
class getInfo(APIView): def post(self,request,*args,**kwargs): param = request.data print(request.data) if param['encryptedData'] and param['iv'] and param['login_key']: print(cache.get(param['login_key'])) openid,session_key = cache.get(param['login_key']).split('&') data = WXBizDataCrypt.WXBizDataCrypt.getInfo(param['encryptedData'],param['iv'],session_key) save_data = { "name": data['nickName'], "avatar": data['avatarUrl'], "language": data['language'], "province": data['province'], "city": data['city'], "country": data['country'], } models.Wxuser.objects.filter(openid=openid).update(**save_data) data = models.Wxuser.objects.filter(openid=openid).first() data = User_ser.User_ser(instance=data,many=False).data return Response({"code":200,"msg":"成功","data":data}) return Response({"code":200,"msg":"缺少参数"})
三。支付流程
https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html
在开发文档中的数据
pay:function(){ wx.request({ url: "http://127.0.0.1:8000/pay/", method: "POST", data:{"login_key":wx.getStorageSync("login_key")}, header: { "content-type": "application/json" }, success: function (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) }, ``` }) } }) ``` },
<button bind:tap="pay"> 支付</button>
import hashlib import random import time from django.core.cache import cache from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response # Create your views here. from .wx import settings from .wx.wx_login import login as logins from . import models from .wx import WXBizDataCrypt from app01.my_ser import User_ser import requests class test(APIView): def post(self,request,*args,**kwargs): print(request) return Response(data={'msg':'ok'},status='200') class login(APIView): def post(self,request,*args,**kwargs): param = request.data print(param.get('code')) if param.get('code'): data = logins(param.get('code')) if data: val = data['openid'] + '&' + data['session_key'] print("openid"+data['openid'],"session_key"+ data['session_key']) key = data['openid'] + str(int(time.time())) md5 = hashlib.md5() md5.update(key.encode('utf-8')) key = md5.hexdigest() cache.set(key,val) has_user = models.Wxuser.objects.filter(openid=data['openid']).first() if not has_user: models.Wxuser.objects.create(openid=data['openid']) return Response({ "code": 200, "msg": "ok", "data": {"login_key": key} }) else: return Response({'code':404,'msg':'code无效'}) else: return Response({'code':200,'msg':'没有参数'}) class getInfo(APIView): def post(self,request,*args,**kwargs): param = request.data print(request.data) if param['encryptedData'] and param['iv'] and param['login_key']: print(cache.get(param['login_key'])) openid,session_key = cache.get(param['login_key']).split('&') data = WXBizDataCrypt.WXBizDataCrypt.getInfo(param['encryptedData'],param['iv'],session_key) save_data = { "name": data['nickName'], "avatar": data['avatarUrl'], "language": data['language'], "province": data['province'], "city": data['city'], "country": data['country'], } models.Wxuser.objects.filter(openid=openid).update(**save_data) data = models.Wxuser.objects.filter(openid=openid).first() data = User_ser.User_ser(instance=data,many=False).data return Response({"code":200,"msg":"成功","data":data}) return Response({"code":200,"msg":"缺少参数"}) class pay(APIView): def post(self,request,*args,**kwargs): param = request.data if param.get("login_key"): openid, session_key = cache.get(param.get("login_key")).split("&") self.openid = openid 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'] data = self.pay() return Response({"code":200,"msg":"ok","data":data}) else: return Response({"code": 400, "msg": "缺少参数"}) def get_str(self): str_all = "1234567890abcdefghijklmnopqrstuvwxyz" nonce_str = "".join(random.sample(str_all,20)) return nonce_str 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 get_order(self): order_id = str(time.strftime("%Y%m%d%H%M%S")) return order_id def pay(self): self.appid = settings.AppId self.mch_id = settings.pay_mchid self.nonce_str = self.get_str() self.body = "lzx" self.out_trade_no = self.get_order() self.total_fee = 1 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 = requests.post(url,data.encode("utf-8"),headers={"content-type":"application/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
需要二次生成签名,并以xml的方式提交url。还需要payid。
在django中,可以通过request中的HTTP_X_FORWARDED_FOR,进行获取,用以共给支付接口。
if request.META.get("HTTP_X_FORWARDED_FOR"): host_ip = request.META["HTTP_X_FROWARDED_FOR"]
requests的使用:
get:
import requests response = requests.get("http://www.baidu.com/") # 也可以这么写 # response = requests.request("get", "http://www.baidu.com/")
post:
import requests kw = {'wd':'苍井空'} headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} # params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode() response = requests.get("http://www.baidu.com/s?", params = kw, headers = headers) # 查看响应内容,response.text 返回的是Unicode格式的数据 print response.text # 查看响应内容,response.content返回的字节流数据 print respones.content # 查看完整url地址 print response.url # 查看响应头部字符编码 print response.encoding # 查看响应码 print response.status_code