Win10環境先後端分離項目基於Vue.js+Django+Python3實現微信掃碼支付流程(2021年最新攻略)

原文轉載自「劉悅的技術博客」https://v3u.cn/a_id_182php

以前的一篇文章:mpvue1.0+python3.7+Django2.0.4實現微信小程序的支付功能,主要介紹了微信小程序內部支付的流程,然而實際上微信小程序有必定的侷限性,也就是用戶範圍僅限於小程序內部生態圈,在生活中真正具備普遍性、高效性、使用方便性的支付方式還得是掃碼支付,掃碼的優勢在於推廣成本低,上至釣魚臺國賓館,下至髮廊地攤都能用,打印出來就完事了,而相比其餘支付方式,現金的找零及假鈔問題,信用卡的辦理門檻、pos機的沉沒成本,就算微信可集成的h5支付和小程序支付,奈何不少老年人根本不會用小程序和手機瀏覽器,更別說再進行支付操做了,因此基於二維碼的掃碼支付的確是很是符合國情的。html

本次咱們使用先後端分離項目Vue.js+Django來集成微信的掃碼支付功能,體驗一下21世紀泛用性最高的支付方式,首先註冊微信公衆平臺:https://mp.weixin.qq.com前端

得到開發者id和祕鑰(appid & appsecret)vue

同時確保獲取微信支付接口的權限:python

隨後註冊微信支付商戶平臺:https://pay.weixin.qq.com/ios

獲取微信支付的商戶號(在帳戶信息頁面):django

獲取微信支付接口的祕鑰(帳戶中心->api安全):json

同時在產品中心->開發配置頁面,將支付域名配置好:axios

這裏不像微信小程序,小程序只能容許https協議接口,而掃碼支付域名既支持https也支持http,很是方便,同時注意域名必須是一個備案域名。小程序

至此,微信支付的前置操做就搞定了,下面咱們來編寫後臺接口wx\_pay.py,首先導入依賴的庫和一些工具方法:

import requests  
from django.http import HttpResponse, HttpResponseRedirect  
  
import random  
import time  
import hashlib  
  
import qrcode  
from bs4 import BeautifulSoup  
  
def trans_xml_to_dict(data_xml):  
    soup = BeautifulSoup(data_xml, features='xml')  
    xml = soup.find('xml')  # 解析XML  
    if not xml:  
        return {}  
    data_dict = dict([(item.name, item.text) for item in xml.find_all()])  
    return data_dict  
  
def trans_dict_to_xml(data_dict):  # 定義字典轉XML的函數  
    data_xml = []  
    for k in sorted(data_dict.keys()):  # 遍歷字典排序後的key  
        v = data_dict.get(k)  # 取出字典中key對應的value  
        if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML標記  
            v = '<![CDATA[{}]]>'.format(v)  
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))  
    return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回XML  
  
def get_sign(data_dict, key):  # 簽名函數,參數爲簽名的數據和密鑰  
    params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 參數字典倒排序爲列表  
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key  
    # 組織參數字符串並在末尾添加商戶交易密鑰  
    md5 = hashlib.md5()  # 使用MD5加密模式  
    md5.update(params_str.encode())  # 將參數字符串傳入  
    sign = md5.hexdigest().upper()  # 完成加密並轉爲大寫  
    return sign

qrcode模塊用來生成二維碼,bs4模塊用來將微信接口返回的xml解析成json,在21世紀的第二十個年頭,微信接口竟然還在使用原始的xml,這種反人類行爲實在不能理解。

接下來咱們來編寫支付邏輯,參考微信官方文檔:https://pay.weixin.qq.com/wik...\_5&index=3

業務流程說明:

(1)商戶後臺系統根據用戶選購的商品生成訂單。

(2)用戶確認支付後調用微信支付【統一下單API】生成預支付交易;

(3)微信支付系統收到請求後生成預支付交易單,並返回交易會話的二維碼連接code\_url。

(4)商戶後臺系統根據返回的code\_url生成二維碼。

(5)用戶打開微信「掃一掃」掃描二維碼,微信客戶端將掃碼內容發送到微信支付系統。

(6)微信支付系統收到客戶端請求,驗證連接有效性後發起用戶支付,要求用戶受權。

(7)用戶在微信客戶端輸入密碼,確認支付後,微信客戶端提交受權。

(8)微信支付系統根據用戶受權完成支付交易。

(9)微信支付系統完成支付交易後給微信客戶端返回交易結果,並將交易結果經過短信、微信消息提示用戶。微信客戶端展現支付交易結果頁面。

(10)微信支付系統經過發送異步消息通知商戶後臺系統支付結果。商戶後臺系統需回覆接收狀況,通知微信後臺系統再也不發送該單的支付通知。

(11)未收到支付通知的狀況,商戶後臺系統調用【查詢訂單API】。

(12)商戶確認訂單已支付後給用戶發貨。

一望而知,咱們須要調用微信的統一下單接口,文檔:https://pay.weixin.qq.com/wik...\_1

編寫邏輯:

def wx_pay(request):  
  
    url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'  # 微信掃碼支付接口  
    key = '945bec********a8fbf7d7'  #商戶api祕鑰  
    total_fee = 1 #支付金額,單位分  
    body = '123123'  # 商品描述  
    out_trade_no = 'order_%s' % random.randrange(100000, 999999)  # 訂單編號  
    params = {  
        'appid': 'wx09*****f',  # APPID  
        'mch_id': '16****08',  # 商戶號  
        'notify_url': 'http://wxpay.v3u.cn/wx_back/',  # 支付域名回調地址  
        'product_id': 'goods_%s' % random.randrange(100000, 999999),  # 商品編號  
        'trade_type': 'NATIVE',  # 支付類型(掃碼支付)  
        'spbill_create_ip': '114.254.176.137',  # 發送請求服務器的IP地址  
        'total_fee': total_fee,  # 訂單總金額  
        'out_trade_no': out_trade_no,  # 訂單編號  
        'body': body,  # 商品描述  
        'nonce_str': 'ibuaiVcKdpRxkhJA'  # 字符串  
    }  
    sign = get_sign(params, key)  # 獲取簽名  
    params.setdefault('sign', sign)  # 添加簽名到參數字典  
    xml = trans_dict_to_xml(params)  # 轉換字典爲XML  
    response = requests.request('post', url, data=xml)  # 以POST方式向微信公衆平臺服務器發起請求  
    data_dict = trans_xml_to_dict(response.content)  # 將請求返回的數據轉爲字典  
    print(data_dict)  
    qrcode_name = out_trade_no + '.png'  # 支付二維碼圖片保存路徑  
    if data_dict.get('return_code') == 'SUCCESS':  # 若是請求成功  
        img = qrcode.make(data_dict.get('code_url'))  # 建立支付二維碼片  
        img.save('./' + qrcode_name)  # 保存支付二維碼  
    return HttpResponse(qrcode_name)

隨後配置路由:

from myapp.wx_pay import wx_pay  
from django.contrib.staticfiles.urls import staticfiles_urlpatterns  
  
# ... the rest of your URLconf goes here ...  
urlpatterns = [  
    #定義超連接路由  
    re_path('^static/upload/(?P<path>.*)$',serve,{'document_root':'/static/upload/'}),  
    path('wx_pay/', wx_pay),  
]

啓動django服務:

python manage.py runserver

訪問http://localhost:8000/wx\_pay/

沒有問題,查看後臺日誌:

{'return_code': 'SUCCESS', 'return_msg': 'OK', 'appid': 'wx092344a76b9979ff', 'mch_id': '1602932608', 'nonce_str': 'bnJwGlXZ3eDSNgjs', 'sign': '2D81402DABEDF75E9A58F200FE7B6775', 'result_code': 'SUCCESS', 'prepay_id': 'wx1816114416896958d6f84177bd71da0000', 'trade_type': 'NATIVE', 'code_url': 'weixin://wxpay/bizpayurl?pr=JgBYgTS00'}

能夠看到已經下單成功,不過訂單狀態處於預支付狀態,同時檢查二維碼圖片是否生成:

至此,後臺邏輯基本搞定,下面就是如何在前端進行調用,同時讓用戶進行掃描操做,編寫wx\_pay.vue組件:

<template>  
  
  <div>  
  
    <center><h1>掃碼支付</h1></center>  
  
   
    <a-form-item v-bind="formItemLayout" label="金額">  
      <a-input v-model="money"/>  
    </a-form-item>  
      
     
    <a-form-item v-bind="tailFormItemLayout">  
      <a-button type="primary" html-type="submit" @click="submit">  
        生成二維碼  
      </a-button>  
    </a-form-item>  
  
  
    <a-form-item v-bind="formItemLayout" label="二維碼">  
      
      <img :src="src" />  
  
    </a-form-item>  
  
  
   </div>  
  
</template>  
  
  
  
<script>  
  
export default {  
  data() {  
    return {  
      money:"1",  
      src:"",  
      formItemLayout: {  
        labelCol: {  
          xs: { span: 24 },  
          sm: { span: 8 },  
        },  
        wrapperCol: {  
          xs: { span: 24 },  
          sm: { span: 16 },  
        },  
      },  
      tailFormItemLayout: {  
        wrapperCol: {  
          xs: {  
            span: 24,  
            offset: 0,  
          },  
          sm: {  
            span: 16,  
            offset: 8,  
          },  
        },  
      },  
      dataSource: [  
        {  
          key: '0',  
          name: 'Edward King 0',  
          age: '32',  
          address: 'London, Park Lane no. 0',  
        },  
        {  
          key: '1',  
          name: 'Edward King 1',  
          age: '32',  
          address: 'London, Park Lane no. 1',  
        },  
      ],  
      columns: [  
        {  
          title: 'name',  
          dataIndex: 'name',  
        },  
        {  
          title: 'age',  
          dataIndex: 'age',  
        },  
        {  
          title: 'address',  
          dataIndex: 'address',  
        },  
        {  
          title: 'operation',  
          dataIndex: 'operation',  
          scopedSlots: { customRender: 'operation' },  
        },  
      ],  
    };  
  },  
  methods: {  
  
    submit:function(){  
  
       this.axios.get('http://localhost:8000/wx_pay/').then((result) =>{  
      
            console.log(result.data.img);  
  
            this.src = "http://localhost:8000/static/upload/"+result.data.img  
    
  });  
  
    },  
    onDelete(key) {  
      console.log(this.dataSource[key]);  
    }  
  
  },  
};  
</script>

當用戶點擊按鈕以後,旋即請求後端支付接口,將接口生成的二維碼返回給前端,效果是這樣的:

隨後使用微信掃一掃功能進行掃碼支付,須要注意的是,該二維碼有效期只有五分鐘,因此最好加上刷新功能。

支付成功以後,咱們還須要對交易進行確認,因此根據微信官方文檔,調用統一查詢接口:

https://pay.weixin.qq.com/wik...\_2,根據接口文檔編寫邏輯:

def wx_check(request):  
  
    #統一訂單查詢接口  
  
    url = "https://api.mch.weixin.qq.com/pay/orderquery"  
  
    out_trade_no = "order_537236" #支付後的商戶訂單號  
    key = '945b******d7'  # 商戶api密鑰  
  
    params = {  
        'appid': 'wx0*****ff',  # APPID  
        'mch_id': '16*****08',  # 商戶號  
        'out_trade_no': out_trade_no,  # 訂單編號  
        'nonce_str': 'ibuaiVcKdpRxkhJA'  # 隨機字符串  
    }  
    sign = get_sign(params, key)  # 獲取簽名  
    params.setdefault('sign', sign)  # 添加簽名到參數字典  
    xml = trans_dict_to_xml(params)  # 轉換字典爲XML  
    response = requests.request('post', url, data=xml)  # 以POST方式向微信公衆平臺服務器發起請求  
    data_dict = trans_xml_to_dict(response.content)  # 將請求返回的數據轉爲字典  
    print(data_dict)  
  
    return HttpResponse('ok')

這裏須要注意的是,查詢的訂單編號能夠使商戶本身的訂單編號,也能夠是微信訂單號,兩者必取其一:

訪問接口 http://localhost:8000/wx\_check/

返回結果:

{'return_code': 'SUCCESS', 'return_msg': 'OK', 'appid': 'wx092344a76b9979ff', 'mch_id': '1602932608', 'nonce_str': 'BVoaDmxxADkpSFEl', 'sign': '23A86EB406B743E0C2C61C7E78DC9373', 'result_code': 'SUCCESS', 'openid': 'oy9q36f9Dpeokj9FWyN3j0znpIqE', 'is_subscribe': 'N', 'trade_type': 'NATIVE', 'bank_type': 'OTHERS', 'total_fee': '1', 'fee_type': 'CNY', 'transaction_id': '4200000806202012174121934231', 'out_trade_no': 'order_537236', 'attach': ' ', 'time_end': '20201217231553', 'trade_state': 'SUCCESS', 'cash_fee': '1', 'trade_state_desc': '支付成功', 'cash_fee_type': 'CNY'}

能夠看到沒有問題,可是因爲涉及金錢業務,爲了養成良好的測試習慣,最好登陸商戶後臺再次確認:

結語:至此,整個微信掃碼支付流程所有跑通,流程上比微信小程序支付邏輯要簡單一些,同時因爲不須要在線用戶的openid,因此像微信小程序獲取不到openid這樣的大坑並不存在,後續會分享一些關於微信掃碼訂單退款的邏輯,搞笑的是,統一下單和查詢接口沒有併發限制,而申請退款竟然有qps上的限制,因此退款流程應該會須要消息隊列的介入。

原文轉載自「劉悅的技術博客」 https://v3u.cn/a_id_182

相關文章
相關標籤/搜索