1. 程式人生 > 實用技巧 >基於Django專案的Python版微信公眾號支付-Native支付方式

基於Django專案的Python版微信公眾號支付-Native支付方式

本文詳細講解Python語言進行公眾號開發時,參考開發者文件進行Native支付(模式二),並給出具體的程式碼:

一.開發流程

業務流程說明:

(1)商戶後臺系統根據微信支付規定格式生成二維碼(規則見下文),展示給使用者掃碼。

(2)使用者開啟微信“掃一掃”掃描二維碼,微信客戶端將掃碼內容傳送到微信支付系統。

(3)微信支付系統收到客戶端請求,發起對商戶後臺系統支付回撥URL的呼叫。呼叫請求將帶productid和使用者的openid等引數,並要求商戶系統返回交資料包,詳細請見"本節3.1回撥資料輸入引數"

(4)商戶後臺系統收到微信支付系統的回撥請求,根據productid生成商戶系統的訂單。

(5)商戶系統呼叫微信支付【統一下單API】請求下單,獲取交易會話標識(prepay_id)

(6)微信支付系統根據商戶系統的請求生成預支付交易,並返回交易會話標識(prepay_id)。

(7)商戶後臺系統得到交易會話標識prepay_id(2小時內有效)。

(8)商戶後臺系統將prepay_id返回給微信支付系統。返回資料見"本節3.2回撥資料輸出引數"

(9)微信支付系統根據交易會話標識,發起使用者端授權支付流程。

(10)使用者在微信客戶端輸入密碼,確認支付後,微信客戶端提交支付授權。

(11)微信支付系統驗證後扣款,完成支付交易。

(12)微信支付系統完成支付交易後給微信客戶端返回交易結果,並將交易結果通過簡訊、微信訊息提示使用者。微信客戶端展示支付交易結果頁面。

(13)微信支付系統通過傳送非同步訊息通知商戶後臺系統支付結果。商戶後臺系統需回覆接收情況,通知微信後臺系統不再發送該單的支付通知。

(14)未收到支付通知的情況,商戶後臺系統呼叫【查詢訂單API】。

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

二.具體程式碼

1.需準備的引數

import os
import qrcode
import json
import hashlib
from random import Random
import requests
from django.http import HttpResponse


notify_url = "....../wx_result_n/
" # 回撥函式,完整路由,部署在伺服器的話要帶上域名,對應的檢視為下面3中的回撥函式 trade_type = 'NATIVE' # 交易方式(掃碼模式二) APP_ID = "wx......" # 公眾賬號的appid MCH_ID = "......" # 商戶號 API_KEY = "......" # 微信商戶平臺(pay.weixin.qq.com) -->賬戶設定 -->API安全 -->金鑰設定,設定完成後把金鑰複製到這裡 UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" # 該url是微信下單api CREATE_IP = '......' # 伺服器IP path = "/opt/data/img/" # 伺服器中存放支付二維碼的路徑

2.呼叫支付介面

def wx_pay_n(request):
    # data = json.loads(request.body)
    # print(request.body)
    # order_id = data.get("order_id", 0)

    total_price = 0.01   # 訂單總價
    order_name = '商品費用'   # 訂單名字
    order_detail = '商品費用'   # 訂單描述
    order_id = 20200411234567    # 自定義的訂單號
    data_dict = wxpay(order_id, order_name, order_detail, total_price)   # 呼叫統一支付介面
    # 如果請求成功
    if data_dict.get('return_code') == 'SUCCESS':
        # 業務處理
        # 二維碼名字
        qrcode_name = str(order_id) + '.png'
        # 建立二維碼
        img = qrcode.make(data_dict.get('code_url'))
        img_url = os.path.join(path, qrcode_name)
        img.save(img_url)
        s = {
            "code": 1000,
            "msg": "獲取成功",
            "data": img_url     # 訪問路徑
        }
        s = json.dumps(s, ensure_ascii=False)
        return HttpResponse(s)
    s = {
                "code": 1001,
                "msg": "獲取失敗",
                "data": ""
            }
    s = json.dumps(s, ensure_ascii=False)
    return HttpResponse(s)

3.支付後回撥介面

def wx_result_n(request):
    data_dict = trans_xml_to_dict(request.body)  # 回撥資料轉字典
    print('支付回撥結果', data_dict)
    sign = data_dict.pop('sign')  # 取出簽名
    back_sign = get_sign(data_dict, API_KEY)  # 計算簽名
    # 驗證簽名是否與回撥簽名相同
    if sign == back_sign and data_dict['return_code'] == 'SUCCESS':
        order_no = data_dict['out_trade_no']
        # 處理支付成功邏輯,根據訂單號修改後臺數據庫狀態
        # 返回接收結果給微信,否則微信會每隔8分鐘傳送post請求
        return HttpResponse(trans_dict_to_xml({'return_code': 'SUCCESS', 'return_msg': 'OK'}))
    return HttpResponse(trans_dict_to_xml({'return_code': 'FAIL', 'return_msg': 'SIGNERROR'}))

4.工具函式

def random_str(randomlength=8):
    """
    生成隨機字串
    :param randomlength: 字串長度
    :return:
    """
    strs = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        strs += chars[random.randint(0, length)]
    print(strs)
    return strs


# 請求統一支付介面
def wxpay(order_id, order_name, order_price_detail, order_total_price):
    nonce_str = random_str()  # 拼接出隨機的字串即可,我這裡是用  時間+隨機數字+5個隨機字母
    total_fee = int(float(order_total_price) * 100)    # 付款金額,單位是分,必須是整數
    print(total_fee)
    params = {
        'appid': APP_ID,  # APPID
        'mch_id': MCH_ID,  # 商戶號
        'nonce_str': nonce_str,  # 隨機字串
        'out_trade_no': order_id,  # 訂單編號,可自定義
        'total_fee': total_fee,  # 訂單總金額
        'spbill_create_ip': CREATE_IP,  # 自己伺服器的IP地址
        'notify_url': notify_url,  # 回撥地址,微信支付成功後會回撥這個url,告知商戶支付結果
        'body': order_name,  # 商品描述
        'detail': order_price_detail,  # 商品描述
        'trade_type': trade_type,  # 掃碼支付型別
    }

    sign = get_sign(params, API_KEY)  # 獲取簽名
    params['sign'] = sign  # 添加簽名到引數字典
    xml = trans_dict_to_xml(params)  # 轉換字典為XML
    response = requests.request('post', UFDODER_URL, data=xml.encode())  # 以POST方式向微信公眾平臺伺服器發起請求
    data_dict = trans_xml_to_dict(response.content)  # 將請求返回的資料轉為字典
    print(data_dict)
    return data_dict




def get_sign(data_dict, key):
    """
    簽名函式
    :param data_dict: 需要簽名的引數,格式為字典
    :param key: 金鑰 ,即上面的API_KEY
    :return: 字串
    """
    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('utf-8'))  # 將引數字串傳入
    sign = md5.hexdigest().upper()  # 完成加密並轉為大寫
    print(sign)
    return sign


def trans_dict_to_xml(data_dict):
    """
    定義字典轉XML的函式
    :param data_dict:
    :return:
    """
    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 trans_xml_to_dict(data_xml):
    """
    定義XML轉字典的函式
    :param data_xml:
    :return:
    """
    data_dict = {}
    try:
        import xml.etree.cElementTree as ET
    except ImportError:
        import xml.etree.ElementTree as ET
    root = ET.fromstring(data_xml)
    for child in root:
        data_dict[child.tag] = child.text
    return data_dict

總結

按照上面四步走,一定可以將Native方式的支付做成功,具體的業務邏輯需要自己處理一下即可,

希望能夠提高大家的開發效率,如有不足請多多指教。

作者:E-QUAL
出處:https://www.cnblogs.com/liujiajia_me/
本文版權歸作者和部落格園共有,不得轉載,未經作者同意參考時必須保留此段宣告,且在文章頁面明顯位置給出原文連線。
本文內容參考如下網路文獻得來,用於個人學習,如有侵權,請您告知刪除修改。 參考連結:https://www.cnblogs.com/linhaifeng/ https://www.cnblogs.com/yuanchenqi/ https://www.cnblogs.com/Eva-J/ https://www.cnblogs.com/jin-xin/ https://www.cnblogs.com/liwenzhou/ https://www.cnblogs.com/wupeiqi/