基于Django项目的Python版微信公众号支付-JSAPI支付方式

本文详细讲解Python语言进行公众号开发时,参考开发者文档进行JSAPI支付,并给出具体的代码:

一、开发流程

基于Django项目的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/"  # 回调函数,完整路由,服务器要带上域名
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

基于Django项目的Python版微信公众号支付-JSAPI支付方式

(0)
(0)
   
举报
评论 一句话评论(0
上一篇:微信小程序反编译~2020年


下一篇:微信小程序自定义弹框禁止页面滚动