• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

微信小程序支付接口

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

微信小程序支付

登录

登录

openid:

相对于当个微信小程序是唯一的id,但是多个小程序无效

unionid:

对于多个小程序,统一用户的id是相同的(比方说一家公司有多个小程序,那么这个用户在这些小程序的id相同)

登录

登录1,2步实现

一步

小程序登录就执行,写在app的生命周期的onLaunch: function () {}

    wx.login({
      success: res => {
        wx.request({
          url: that.globalData.URL+\'login/\',
          data:{
            code:res.code
          },
          header:{
            \'content-type\':\'application/json\'
          },
          method:\'post\',
          success:function(e){
            console.log(e)
            wx.setStorageSync("token", e.data.data.token)
          }
        })
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })

二步

const为配置一些常量的文件,注意session_key不可泄露

import requests
from settings import const

#调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key
def login(code):
    #向url填入参数
    url = const.code2Session.format(const.AppId, const.AppSecret, code)
    reponse = requests.get(url=url)
    #获取响应的json数据
    data = reponse.json()
    if data.get(\'session_key\'):
        return data
    else:
        return False

from django.shortcuts import render
from rest_framework.views import APIView
from django.core.cache import cache
from libs import wx_login
import hashlib
import time
from . import models
from rest_framework.response import Response


class login(APIView):
    def post(self,request):
        #获取请求参数,code
        param = request.data
        if param.get("code"):
            #调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key
            data = wx_login.login(param.get(\'code\'))
            if data:
                #由于不应该把会话密钥下发到小程序,也不应该对外提供这个密钥,所以进行加密处理
                # 1 session_key+时间戳等到一个key.(md5
                md5 = hashlib.md5()
                md5.update(data.get("session_key").encode("utf8"))
                md5.update(str(time.time()).encode("utf8"))
                key = md5.hexdigest()

                # 2 session_key与openid做绑定等到val
                val = data.get("session_key") + \'&\' + data.get("openid")

                # 3key->val存到redis,
                cache.set(key, val)

                # 4把openid存到数据库。用于后续的登录校验
                user_data = models.Wxuser.objects.filter(openid=data.get("openid")).first()
                if not user_data:
                    models.Wxuser.objects.create(openid=data.get("openid"))

                # 5把key返回给小程序
                return Response({"code": 200, "msg": "suc", "data": {"token": key}})
            else:
                return Response({"code": 202, "msg": "code无效"})
        else:
            return Response({"code": 201, "msg": "缺少参数"})

授权

授权

录音授权案例

  <button bind:tap="luying">录音</button>
  
  
  luying:function(){
    wx.getSetting({
      success(res) {
        console.log("res", res.authSetting[\'scope.record\'])
        #判断是否已经授权过
        if (!res.authSetting[\'scope.record\']) {
          #弹窗
          wx.authorize({
            scope: \'scope.record\',
            #同意授权
            success() {
              // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
              wx.startRecord()
            },
            #不同意授权
            fail(){
              console.log("你没有授权")
            }
          })
        }else{
          wx.startRecord()
        }
      }
    })
  },

用户信息授权+后端获取用户信息

文档

注意

用户授权和其他授权不一样,他只能通过按钮点击触发

微信不允许明文传输用户信息到后台,必须通过微信提供的sdk解密获取用户的敏感信息

   <button open-type="getUserInfo" bindgetuserinfo="info1">获取用户信</button>
 
 info1:function(res){
    console.log(res,"按钮")
    #授权成功后,可以随时使用这个获取用户信息
    // wx.getUserInfo({
    //   success: function (res) {
    //     console.log(res, "用户信息")
    //   }
    // })
    var that=this
    //检查session_key是否过期 
    wx.checkSession({
      success() {
        //session_key 未过期,并且在本生命周期一直有效
        wx.request({
          url: app.globalData.URL + "userinfo/",
          data: {
            #包括敏感数据在内的完整用户信息的加密数据
            encryptedData: res.detail.encryptedData,
            #加密算法的初始向量
            iv: res.detail.iv,
            #用户token
            token:wx.getStorageSync("token")
          },
          header: {
            "content-type": "application/json"
          },
          method: "POST",
          success: function (e) {
            console.log(e)
          
          }
        })
      },
      fail() {
        // session_key 已经失效,需要重新执行登录流程
       // wx.login() //重新登录
      }
    })
  },

解密SDK

下载官方demo,简单封装,pip install pycryptodome

import base64
import json
from Crypto.Cipher import AES
from settings import const

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 get_info(cls,sessionKey,encryptedData,iv):

         return cls(const.AppId, sessionKey).decrypt(encryptedData, iv)

解密-存入数据库

from libs.WXBizDataCrypt import WXBizDataCrypt
from . import serializers
class UserInfo(APIView):
    def post(self,request):
    	#获取请求参数
        param = request.data
        if param.get(\'token\') and  param.get("encryptedData") and param.get("iv"):
            cache_data=cache.get(param.get(\'token\'))
            if cache_data:
                # 获取session_key
                session_key,openid=cache_data.split("&")

                #数据解密
                user_info=WXBizDataCrypt.get_info(session_key,  param.get("encryptedData"),param.get("iv"))
                print(user_info)
                #存入数据库
                user_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\'],
                }
                #更新数据库
                models.Wxuser.objects.filter(openid=openid).update(**user_data)
                data = models.Wxuser.objects.filter(openid=openid).first()
                data = serializers.Wxuser_ser(data,many=False).data
                return Response({"code": 200, "msg": "suc", "data": data})

            else:
                return Response({"code": 202, "msg": "token无效"})
        else:
            return Response({"code": 201, "msg": "缺少参数"})

支付开发

文档

交互图

登录部分,之前的就是,获取openid

  pay:function(){
    wx.request({
      url: app.globalData.URL + "pay/",
      data: {
        token: wx.getStorageSync("token")
      },
      header: {
        "content-type": "application/json"
      },
      method: "POST",
      success: function (e) {
        console.log("pay_data",e)
        #收到响应的支付参数
        wx.requestPayment(
          {
            \'timeStamp\': e.data.data.timeStamp,
            \'nonceStr\': e.data.data.nonceStr,
            \'package\': e.data.data.package,
            \'signType\': \'MD5\',
            \'paySign\': e.data.data.paySign,
            \'success\': function (res) {
                console.log("支付成功",res)
             },
            \'fail\': function (res) { 
              console.log("支付失败", res)
            }         
          })
      }
    })
  }

后端支付接口

class Pay(APIView):
    def post(self,request):
        param = request.data
        #是否登录,确认用户身份
        if param.get("token"):
        	#获取openid
            cache_data = cache.get(param.get("token"))
            if cache_data:
                # 获取客户端ip,如果是负载均衡,就用HTTP_X_FORWARDED_FOR,如果不是就用下面的
                if request.META.get(\'HTTP_X_FORWARDED_FOR\'):
                    self.ip = request.META[\'HTTP_X_FORWARDED_FOR\']
                else:
                    self.ip = request.META[\'REMOTE_ADDR\']
                    
                session_key,self.openid=cache_data.split("&")
                #5个参数和sign
                data=self.get_pay_data()
                return  Response({"code": 200, "msg": "suc","data":data})
            else:
                return Response({"code": 202, "msg": "token无效"})
    def get_nonce_str(self,num=30):
        # strs = ""
        # for i in range(30):
        #     strs += str(random.randint(0,9))
        all_str = "0123456789abcdefghijklmnopqrstuvwxyz"
        strs = "".join(random.sample(all_str,num))
        return  strs

    def get_out_trade_no(self):
        import time
        strs = str(int(time.time()))+self.get_nonce_str(5)
        return  strs


    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",
            "sign_type": "MD5",
            "appid": self.appid,
            "total_fee": self.total_fee,
            "mch_id": self.mch_id
        }
        str_a = "&".join([ f"{i}={data_dic[i]}" for i in sorted(data_dic)])
        str_b = f"{str_a}&key={settings.pay_apikey}"
        md5 = hashlib.md5()
        md5.update(str_b.encode("utf8"))
        return  md5.hexdigest().upper()



    def xml_to_dic(self,xml_data):
        import xml.etree.ElementTree as ET
        xml_data = ET.fromstring(xml_data)
        dic = {}
        for child in xml_data:
            dic[child.tag] =child.text
        return  dic




    def get_prepay_data(self):
        url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
        response = requests.post(url=url,data=self.body_data.encode("utf8"),headers={"content-type":"application/xml"})
        xml_data = response.content
        dic_data = self.xml_to_dic(xml_data)
        return  dic_data

    def get_second_sign(self):
        self.second_nonceStr = self.get_nonce_str()
        self.timeStamp = str(int(time.time()))
        data_dic = {
            "appId":settings.AppId,
            "timeStamp":self.timeStamp,
            "nonceStr":self.second_nonceStr,
            "package":f"prepay_id={self.prepay_data.get(\'prepay_id\')}",
            "signType":"MD5"
        }
        print(data_dic)
        str_a = "&".join([f"{i}={data_dic[i]}" for i in sorted(data_dic)])
        str_b = f"{str_a}&key={settings.pay_apikey}"
        md5 = hashlib.md5()
        md5.update(str_b.encode("utf8"))
        return  md5.hexdigest().upper()




    def get_pay_data(self):
    	#小程序id
        self.appid = settings.AppId
        #商户id
        self.mch_id = settings.pay_mchid
        self.nonce_str = self.get_nonce_str()
        self.sign_type = "MD5"
        self.body = "商品描述信息"
        self.out_trade_no = self.get_out_trade_no()
        self.total_fee = 1
        self.spbill_create_ip = self.ip
        self.notify_url = "http://www.weixin.qq.com/wxpay/pay.php"
        self.trade_type = "JSAPI"
        self.sign = self.get_sign()
        
        #数据封装成xml格式
        self.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>1</total_fee>
                <sign_type>MD5</sign_type>
                <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip>
                <notify_url>{self.notify_url}</notify_url>
                <openid>{self.openid}</openid>
                <trade_type>JSAPI</trade_type> 
                </xml>"""
        
        #获取prepay_id
        self.prepay_data=self.get_prepay_data()
		
		#数据再次签名
        second_sign=self.get_second_sign()
        data = {
            "timeStamp":self.timeStamp,
            "nonceStr":self.second_nonceStr,
            "package":f"prepay_id={self.prepay_data.get(\'prepay_id\')}",
            "paySign":second_sign
        }
        return data


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
微信小程序调用微信支付接口发布时间:2022-07-18
下一篇:
java微信小程序调用支付接口(转)发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap