基於Django項目的Python版微信公眾號支付-JSAPI支付方式


本文詳細講解Python語言進行公眾號開發時,參考開發者文檔進行JSAPI支付,並給出具體的代碼:

一、開發流程

業務流程說明:

1、商戶server調用統一下單接口請求訂單,api參見公共api【統一下單API

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

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

二.具體代碼

1.需准備的參數

import time
import json
import hashlib
from random import Random
import requests
from django.http import HttpResponse


notify_url = "....../wx_result_js/"  # 回調函數,完整路由,服務器要帶上域名,對應的視圖是下面4中的回調函數
trade_type = 'JSAPI'  # 交易方式
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

2.調用支付接口

def wx_pay_js(request):

    # data = json.loads(request.body)
    # print(request.body)
    total_price = 0.01   # 訂單總價
    order_name = '商品費用'   # 訂單名字
    order_detail = '商品費用'   # 訂單描述
    order_id = 20200411234567    # 自定義的訂單號
    openid = "......"  # 用戶的openid,在這種類型中支付必傳
    data_dict = wxpay_js(order_id, order_name, order_detail, total_price, openid)
    # 如果請求成功
    if data_dict.get('return_code') == 'SUCCESS':

        prepay_id = data_dict.get('prepay_id', "")
        nonce_str = data_dict.get('nonce_str', "")
        data = {}    # 前端需要這些參數才能調用微信支付頁面
        data['appId'] = APP_ID
        data['timeStamp'] = int(time.time())  # 必填,生成簽名的時間戳
        data['nonceStr'] = nonce_str
        data['package'] = "prepay_id=" + prepay_id
        data['signType'] = "MD5"  # 添加簽名加密類型

        sign = get_sign(data, API_KEY)  # 獲取簽名
        data['paySign'] = sign  # 添加簽名到參數字典

        if prepay_id:
            s = {
                "code": 1000,
                "msg": "獲取成功",
                "data": data
            }
            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.前端調用方法

function onBridgeReady(){
   WeixinJSBridge.invoke(
      'getBrandWCPayRequest', {
         "appId":"wx2421b1c4370ec43b",     //公眾號名稱,由商戶傳入     
         "timeStamp":"1395712654",         //時間戳,自1970年以來的秒數     
         "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //隨機串     
         "package":"prepay_id=u802345jgfjsdfgsdg888",     
         "signType":"MD5",         //微信簽名方式:     
         "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 
      },
      function(res){
      if(res.err_msg == "get_brand_wcpay_request:ok" ){
      // 使用以上方式判斷前端返回,微信團隊鄭重提示:
            //res.err_msg將在用戶支付成功后返回ok,但並不保證它絕對可靠。
      } 
   }); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

4.支付后回調接口

def wx_result_js(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']
        print('微信支付成功會回調!')
        # 處理支付成功邏輯,根據訂單號修改后台數據庫狀態
        # 返回接收結果給微信,否則微信會每隔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'}))

5.工具函數

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



# 請求統一支付接口,多一個openid,JSAPI方式請求時必須帶上這個openid參數
def wxpay_js(order_id, order_name, order_price_detail, order_total_price, openid=''):
    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,  # 掃碼支付類型
        'openid': openid
    }

    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

總結

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

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

 

作者: 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/


免責聲明!

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



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