day88 微信小程序支付功能

出用户信息以外的授权

地址: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


原文地址:https://www.cnblogs.com/zqfzqf/p/12392196.html