• 基于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/
  • 相关阅读:
    如何判断某个设备文件是否存在
    shell中export理解误区
    linux命令之tail
    国内较快的gnu镜像:北京交通大学镜像
    Cmake的交叉编译
    linux 命令之grep
    makefile之变量赋值
    makefile之VPATH和vpath的使用
    arm汇编进入C函数分析,C函数压栈,出栈,传参,返回值
    Jlink 软件断点和硬件断点
  • 原文地址:https://www.cnblogs.com/liujiajia_me/p/13341323.html
Copyright © 2020-2023  润新知