小程序支付


一、小程序支付官方文檔

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1 # 接口

二、小程序支付流程

1 用戶發起請求下單支付
2 我們要保證用是登入狀態。
3 組織數據,請求統一下單接口,微信官方會同步返回一個prepay_id
4 重新組織數據,進行簽名,將重新組織的數據返回給小程序,小程序在吊起支付。
5 用戶就可以進行支付,支付結果會同步返回給小程序
6 后台修改訂單支付狀態是通過微信官方服務器的異步通知

三、支付流程圖(下方有接口)

業務流程時序圖

小程序支付的交互圖如下:

小程序支付時序圖

商戶系統和微信支付系統主要交互:

1、小程序內調用登錄接口,獲取到用戶的openid,api參見公共api【小程序登錄API

2、商戶server調用支付統一下單,api參見公共api【統一下單API

3、商戶server調用再次簽名,api參見公共api【再次簽名

4、商戶server接收支付通知,api參見公共api【支付結果通知API

5、商戶server查詢支付結果,api參見公共api【查詢訂單API

四、簽名

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3

# 簽名
    def get_sign(self):
        data_dict = {
            "appid": self.appid,
            "mch_id": self.mch_id,
            "nonce_str": self.nonce_str,
            "body": self.body,
            "out_trade_no": self.out_trade_no,
            "total_fee": self.total_fee,
            "spbill_create_ip": self.ip,
            "notify_url": self.notify_url,
            "trade_type": self.trade_type,
            "openid": self.openid,
        }
        # 列表推導式,
        sign_str = "&".join([f"{k}={data_dict[k]}" for k in sorted(data_dict)])
        sign_str = f"{sign_str}&key={settings.pay_apikey}"
        print("sign_str", sign_str)
        md5 = hashlib.md5()
        md5.update(sign_str.encode("utf-8"))
        sign = md5.hexdigest()
        return sign.upper()

五、xml解析模塊

<xml>
  <appid name="屬性值" >{.child.text}</appid>
   child.tag表示appid   
</xml> 

import xml.etree.ElementTree as ET

如果我們要解析一個xml文件
tree = ET.parse('country_data.xml')
root = tree.getroot()

如果解析字符串
root = ET.fromstring(country_data_as_string)

這個root是 Element 
for child in root:
	 print(child.tag, child.attrib)
	 #child.tag表是標簽名,child.attrib表示獲取屬性
	 #child.text就表示獲取內容

# 拼接的xml數據
body_data = f'''
    <xml>
    <appid>{self.appid}</appid>
    <mch_id>{self.mch_id}</mch_id>
    <nonce_str>{self.nonce_str}</nonce_str>
    <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.spbill_create_ip}</spbill_create_ip>
    <notify_url>{self.notify_url}</notify_url>
    <trade_type>{self.trade_type }</trade_type>
    <openid>{self.openid }</openid>      
    <sign>{self.sign}</sign>      
    </xml> 
'''

接收xml二進制數據,轉換為字典

# 接收xml二進制數據,轉換為字典
    def xml_to_dict(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

六、再次簽名

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3

# 再次簽名
    def get_two_sign(self, data):
        data_dict = {
            "appId": settings.AppId,
            "timeStamp": str(int(time.time())),
            "nonceStr": data['nonce_str'],
            "package": f"prepay_id={data['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()
        return sign.upper(), data_dict['timeStamp']

七、前台吊起支付接口

https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3

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)
    },

})

八、小程序支付總結

1 接收到支付請求。我們先組織數據,然后進行統一下單前的簽名
- 請求的數據與響應的數據都是xml.請求的時候,xml數據要變成二進制,heards中的content-type:"application/xml"
-響應的數據也是xml,我要用xml.etree.ElementTree將他轉化為字典
2 拿到統一下單數據,最重要的prepay_id,進行再次簽名。把一下數據發送給小程序。
			"timeStamp": 時間戳
            "nonceStr":隨機字符串
            "package": f"prepay_id={data_dict['prepay_id']}",統一下單中的到的prepay_id
            "signType": "MD5",
            "paySign":通過上面數據進行加密的結果
 3 小程序掉用wx.resquestPayment()吊起支付

案例

后台

from rest_framework.views import APIView
from rest_framework.response import Response
from django.core.cache import cache
from api.wx import settings
import hashlib
import requests
import time


class Pay(APIView):
    def post(self, request):
        param = request.data
        if param.get("token") and param.get("money"):
            # 根據傳過來的token,從緩存中拿出openid_session_key
            openid_session_key = cache.get(param.get("token"))

            # 如果存在,證明傳過來的token合法有效
            if openid_session_key:
                if request.META.get('HTTP_X_FORWARDED_FOR'):
                    # 有負載均衡就用這個
                    self.ip = request.META['HTTP_X_FORWARDED_FOR']
                else:
                    # 沒有負載均衡就用這個
                    self.ip = request.META['REMOTE_ADDR']

                self.openid = openid_session_key.split("&")[1]
                self.money = param.get("money")
                data = self.get_pay_data()  # 調用支付接口
                return Response({"code": 0, "msg": "ok", "data": data})
            else:
                return Response({"code": 2, "msg": "token無效"})

        else:
            return Response({"code": 1, "msg": "缺少參數"})

    # 支付接口
    def get_pay_data(self):
            self.appid = settings.AppId
            self.mch_id = settings.pay_mchid  # 公司商戶號,選喲公司三證申請
            self.nonce_str = self.get_nonce_str()  # 生成一個隨機數
            self.body = "老男孩學費"  # 商品描述
            self.out_trade_no = self.get_order_id()  # 商戶訂單號,模擬
            self.total_fee = self.money  # 支付金額
            self.spbill_create_ip = self.ip  # 調用微信支付API的機器IP
            self.notify_url = "htttp://www.test.com"  # 通知地址
            self.trade_type = "JSAPI"  # 交易類型
            self.openid = self.openid  # 用戶標識
            self.sign = self.get_sign()  # 簽名

            # 拼接的xml數據
            body_data = f'''
                <xml>
                    <appid>{self.appid}</appid>
                    <mch_id>{self.mch_id}</mch_id>
                    <nonce_str>{self.nonce_str}</nonce_str>
                    <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.spbill_create_ip}</spbill_create_ip>
                    <notify_url>{self.notify_url}</notify_url>
                    <trade_type>{self.trade_type }</trade_type>
                    <openid>{self.openid }</openid>      
                    <sign>{self.sign}</sign>      
                </xml> 
                '''
            url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
            # 如果發送的xml數據要把數據轉化二進制。body_data.encode("utf-8")
            # headers={"content-type": "application/json"}   json數據
            # headers={"content-type": "application/xml"}    xml數據
            response = requests.post(url, data=body_data.encode("utf-8"), headers={"content-type": "application/xml"})
            # 接收一個二進制的響應,調接口
            data_dict = self.xml_to_dict(response.content)
            print('data_dict:', data_dict)
            # 再次簽名
            pay_sign, timeStamp = self.get_two_sign(data_dict)
            data = {
                "timeStamp": timeStamp,
                "nonceStr": data_dict['nonce_str'],
                "package": f"prepay_id={data_dict['prepay_id']}",
                "signType": "MD5",
                "paySign": pay_sign
            }
            return data

    # 隨機字符串
    def get_nonce_str(self):
        import random
        data = "123456789abcdefghijklmn"
        nonce_str = "".join(random.sample(data, 10))
        # random.sample(從哪里取,取多小個),變成列表
        return nonce_str

    # 模擬訂單號
    def get_order_id(self):
        import time
        import random
        data = "123456789abcdefghijklmn"
        order_no = str(time.strftime("%Y%m%d%H%M%S")) + "".join(random.sample(data, 5))
        return order_no

    # 簽名
    def get_sign(self):
        data_dict = {
            "appid": self.appid,
            "mch_id": self.mch_id,
            "nonce_str": self.nonce_str,
            "body": self.body,
            "out_trade_no": self.out_trade_no,
            "total_fee": self.total_fee,
            "spbill_create_ip": self.ip,
            "notify_url": self.notify_url,
            "trade_type": self.trade_type,
            "openid": self.openid,
        }
        # 列表推導式,
        sign_str = "&".join([f"{k}={data_dict[k]}" for k in sorted(data_dict)])
        sign_str = f"{sign_str}&key={settings.pay_apikey}"
        print("sign_str", sign_str)
        md5 = hashlib.md5()
        md5.update(sign_str.encode("utf-8"))
        sign = md5.hexdigest()
        return sign.upper()

    # 接收xml二進制數據,轉換為字典
    def xml_to_dict(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
        print('xml_dict:',xml_dict)
        return xml_dict

    # 再次簽名
    def get_two_sign(self, data):
        data_dict = {
            "appId": settings.AppId,
            "timeStamp": str(int(time.time())),
            "nonceStr": data['nonce_str'],
            "package": f"prepay_id={data['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()
        return sign.upper(), data_dict['timeStamp']


前台

 pay:function(){
    wx.request({
      url: app.globalData.baseurl + "pay/",
      //支付的錢,和攜帶的token,1代表1分錢
      data: { "money": 1, token: wx.getStorageSync('token') },
      method: "POST",
      success(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)
            },

          })

      }
    })
  }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM