• 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
    
    
    
  • 相关阅读:
    .NET总结
    gzip压缩输出
    矩阵 快速幂
    Mock单元测试
    mysql乱码原因之一
    写个android下倒计时软件
    linux gcc下实现简单socket套接字小程序
    gcc下MD5破解
    #1045 Access denied for user 'root'@'localhost'
    转android四种动画
  • 原文地址:https://www.cnblogs.com/zqfzqf/p/12392196.html
Copyright © 2020-2023  润新知