出用户信息以外的授权
地址:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html
test.wxml
<button bindtap="shou">录音授权</button>
test.js
shou:function(){
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope
wx.getSetting({
success(res) {
console.log(res)
if (!res.authSetting['scope.record']) {//判断有没有scope.record
wx.authorize({//进行授权
scope: 'scope.record',
success() {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord()//使用接口
}
})
}
}
})
},
用户信息授权
地址:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html
test.wxml
<button open-type="getUserInfo" bindgetuserinfo="get_user">用户信息</button>
test.js
get_user:function(res){
console.log(res)
wx.checkSession({//判断登录是否有效
success:function(){
wx.getUserInfo({//获取用户信息
success:function(e){
wx.request({
url: 'http://127.0.0.1:8000/UserInfo/',
method:"POST",
data:{
"iv":e.iv,
"encryptedData":e.encryptedData,
"token":wx.getStorageSync("token")
},
success:function(e){
console.log(e)
}
})
}
})
},
fail:function(){
//重新登录
}
})
},
user.py
class Info(APIView):
def post(self,request):
param=request.data
print("param",param)
if param.get("iv") and param.get("encryptedData") and param.get("token"):
login_data=cache.get(param.get("token"))
print("login_data",login_data)
if login_data:
openid,session_key=login_data.split("&")
user_info=WXBizDataCrypt.WXBizDataCrypt.main(session_key,param.get("encryptedData"),param.get("iv"))
print("user_info",user_info)
save_data={
'name': user_info['nickName'],
'avatar': user_info['avatarUrl'],
'language': user_info['language'],
'province': user_info['province'],
'city': user_info['city'],
'country': user_info['country'],
'gender': user_info['gender'],
}
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
re_data={
'name': data['name'],
'avatar': data['avatar'],
'language': data['language'],
}
return Response({"code":200,"msg":"ok","data":re_data})
else:
return Response({"code": 201, "msg": "错误的token"})
else:
return Response({"code": 202, "msg": "缺少参数"})
wx-->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 main(cls,sessionKey,encryptedData,iv):
appId = settings.AppId
return cls(appId, sessionKey).decrypt(encryptedData, iv)
my_ser-->User_ser.py
from rest_framework import serializers
from app01 import models
class User_ser(serializers.ModelSerializer):
class Meta:
model = models.Wxuser
fields = "__all__"
支付接口
地址:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/payment/wx.requestPayment.html
'''
1 接收到用对哪个订单进行支付
2 通过订单号,查询订单信息,拿到订单金额,这些数据
3 吊起统一下单接口(如果不是小程序支付,其实到这里已经结束)
3.1 组织统一下单的数据,认真阅读文档每个字段代表什么意思?类型是什么类型,长度限制,重复性限制
3.2 签名,签名是最难的,他难在细心和代码功底。
3.3 如果发现一致是签名错误,而且对接的公司是大公司,你不要怀疑你别人的错,如果是文档是小公司,你是第一个用户
你就要想一想,是不是对方接口的问题。
小程序支付,他就多了一个再次签名,因为统一接口无法直接吊起小程序的支付界面,所以把数据要再次签名发送
到小程序。让小程序自己吊起支付
4 写支付回调
4.1接收回调的数据,有时候你发现无法接收到回调,你要注意的点,
4.1.1你的路由是否符合别人的规范
4.1.2 该路由是否需要后台添加白名单
4.2 接收到数据以后做验签
验签就是把接收到的数据做一个加密,然后与他发过来的签名做比较,注意,发送过来的数据中签名字段是不参与加密的
# ":key"
data ={
"order_money":100
"order_id":123456
"status":"success"
}
sign=data.pop("sign")
get_sign=md5(data+key="123")
if sign==get_sign:
update订单的支付状态
'''
test.js
pay: function () {
wx.request({
url: 'http://127.0.0.1:8000/pay/',
data: { "token": wx.getStorageSync('token') },
method: "POST",
success: function (res) {
console.log(res)
wx.requestPayment({
timeStamp: res.data.data.timeStamp,
nonceStr: res.data.data.nonceStr,
package: res.data.data.package,
signType: res.data.data.signType,
paySign: res.data.data.paySign,
success(res) {
console.log("支付成功", res)
},
fail(res) {
console.log("支付失败", res)
}
})
}
})
},
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,time
class Pay(APIView):
def post(self,request):
param=request.data
if param.get("token"):
#根据token来获取session_key与open_id的字符串
user_data=cache.get(param.get("token"))
if user_data:
#获取openid
self.open_id,session_key=user_data.split("&")
#如果是负载均衡就要用HTTP_X_FORWARDED_FOR来获取客户端的IP
if request.META.get("HTTP_X_FORWARDED_FOR"):
self.ip=request.META.get("HTTP_X_FORWARDED_FOR")
else:
#如果不是负载均衡就直接用REMOTE_ADDR
self.ip = request.META.get("REMOTE_ADDR")
#关于支付的所有逻辑我们在one_pay中完成,最终返回给小程序
data=self.one_pay()
return Response({"code": 200, "msg": "ok", "data": data})
else:
return Response({"code": 201, "msg": "无效的token"})
else:
return Response({"code": 202, "msg": "缺少参数"})
def get_random(self):
import random
data="123456789abcdefghijklmnopqrstuywxyz"
nonce_str="".join(random.sample(data,30))
return nonce_str
def get_order_id(self):
import random
data = "123456789abcdefghijklmnopqrstuywxyz"
nonce_str = "".join(random.sample(data, 5))
order_id=str(time.strftime("%Y%m%d%H%M%S"))+nonce_str
return order_id
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.open_id,
"body": self.body,
"trade_type": self.trade_type,
"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"))
sign=md5.hexdigest()
return sign.upper()
def xml_to_dic(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_pay_sign(self,res_dict,timeStamp):
data_dic = {
"appId": res_dict["appid"],
"timeStamp":timeStamp,
"nonceStr":res_dict['nonce_str'],
"package" : f"prepay_id={res_dict['prepay_id']}",
"signType":"MD5"
}
sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)])
sign_str = f"{sign_str}&key={settings.pay_apikey}"
import hashlib
md5 = hashlib.md5()
md5.update(sign_str.encode("utf-8"))
sign = md5.hexdigest()
return sign.upper()
def one_pay(self):
#统一下单 ,统一订单
data_body=self.get_body_data()
import requests
#统一下单的路由
url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
#将上面组织好的数据发送给url
response=requests.post(url,data_body.encode("utf-8"),headers={"content-type":"application/xml"})
#response是该接口返回的数据,但是是xml的,然后我们用xmL_to_dic自己写的函数,将xml转字典
res_dict=self.xml_to_dic(response.content)
print("res_dic",res_dict)
#当前时间的时间戳
timeStamp=str(int(time.time()))
#你加密的值是用的什么在下面的data_dic就返回什么值
paySign=self.get_pay_sign(res_dict,timeStamp)
data_dic = {
"timeStamp": timeStamp,
"nonceStr": res_dict['nonce_str'],
"package": f"prepay_id={res_dict['prepay_id']}",
"signType": "MD5",
"paySign": paySign
}
return data_dic
def get_body_data(self):
self.appid=settings.AppId
self.mch_id=settings.pay_mchid
self.nonce_str=self.get_random()
self.body="老男孩学费"
self.out_trade_no=self.get_order_id()
self.total_fee=1
self.notify_url="www.test.com"
self.trade_type="JSAPI"
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>
<sign>{self.sign}</sign>
<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.ip}</spbill_create_ip>
<notify_url>{self.notify_url}</notify_url>
<openid>{self.open_id}</openid>
<trade_type>{self.trade_type }</trade_type>
</xml>"""
return body_data