• 基于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/
  • 相关阅读:
    matlab cell
    matlab linux 快捷键设置——有问题还是要解决
    latex 小结
    TOJ 1258 Very Simple Counting
    TOJ 2888 Pearls
    HDU 1248 寒冰王座
    TOJ 3486 Divisibility
    TOJ 3635 过山车
    TOJ 1840 Jack Straws
    HDU 4460 Friend Chains
  • 原文地址:https://www.cnblogs.com/liujiajia_me/p/13341278.html
Copyright © 2020-2023  润新知