微信Native支付 商户端python SDK

最近接触到了微信支付,主要做的是用django实现PC端的Native支付模式,即商户系统页面生成支付二维码,用户用“扫一扫”完成支付的模式,下面简单了解一下微信支付的开发文档。

文章目录

      • 微信支付模式分类
      • Native支付模式
        • native模式二支付流程及核心代码(关键函数见尾部)
      • 自定义的部分关键函数
            • 随机字符串函数
            • 校验签名

微信支付模式分类

  • 付款码支付
    主要应用线下面对面收银的场景;
  • Native支付
    Native支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景;
  • JSAPI支付
    JSAPI支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付;
  • APP支付
    APP支付又称移动端支付,是商户通过在移动端应用APP中集成开放SDK调起微信支付模块完成支付的模式;
  • H5支付
    H5支付主要是在手机、ipad等移动设备中通过浏览器来唤起微信支付的支付产品;
  • 小程序支付
    小程序支付是专门被定义使用在小程序中的支付产品。目前在小程序中能且只能使用小程序支付的方式来唤起微信支付。

Native支付模式

Native支付有两种模式,我们可以根据支付场景选择相应的模式:

  • 模式一
    商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

  • 模式二
    商户后台系统调用微信支付统一下单API生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。该模式的预付单有效期为2小时,过期后无法支付。

相比之下,模式二较为简单,更容易上手,因此选择了模式二。

native模式二支付流程及核心代码(关键函数见尾部)

事先需获取到微信公众账号的appid和商户号mch_id

  1. 商户后台系统根据用户选购的商品生成订单;

    关键参数:生成订单时需提供product_id,商户订单号out_trade_no以及订单金额total_fee(单位分);
    
  2. 用户确认支付后调用微信支付统一下单API生成预支付交易;

    关键参数:UNIFIED_ORDER_URL统一下单地址,NOTIFY_URL自定义的支付回调地址,nonce_str随机字符串,sign签名,body商品描述,spbill_create_ip终端IP,trade_type此处为NATIVE。
    

    以上所有参数(包含appid、mch_id等)均用来构建xml请求参数,xml示例如下:

     
        xxxxxx
        NATIVE支付测试
        10000100
        1add1a30ac87aa2db72f57a2375d8fec
        xxx.com
        1415659990
        0.0.0.0
        1
        NATIVE
        0CB01533B8C1EF103065174F50BCA001
     
    

    代码实现

    # 构建原始参数dict,之后转成xml格式
    params = {
            "appid": APP_ID,
            "mch_id": MCH_ID,
            "nonce_str": nonce_str,
            "out_trade_no": out_trade_no,
            "body": 'xxxx',
            "total_fee": total_fee,
            "spbill_create_ip": spbill_create_ip,
            "notify_url": NOTIFY_URL,
            "trade_type": "NATIVE",
            "product_id": product_id,
        }
    sign = sign_func(params, API_KEY) # 调用签名生成函数
    params["sign"] = sign
    req_xml = re_dict_to_xml(params) # 调用函数将dict转为xml格式
    res_xml = requests.post(UNIFIED_ORDER_URL, req_xml, verify=False) # 调用统一下单API
    res_xml.encoding = 'utf-8'
    _, res_obj = xml_to_dict(res_xml.text) # 调用函数将结果转成dict 方便后续的解析
    code_url = res_obj["code_url"] # 此处获得二维码url
    

    关于接口其他返回参数的详细信息及使用方法,可参考官方文档
    https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1

  3. 微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url,然后商户后台系统根据返回的code_url生成二维码。

    此处返回的ur需自行调用JS(如QRCode)转成二维码图片;
    

    QRCode使用可参考:https://www.runoob.com/w3cnote/javascript-qrcodejs-library.html

  4. 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统, 微信支付系统根据用户授权完成支付交易,微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

  5. 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

    异步通知接口需要自行撰写,返回return_code和return_msg给微信即可;
    

    代码实现

    @csrf_exempt
    def wechatpay_notify(request):
        xml = request.body
        WECHAT_SUCCESS = re_dict_to_xml({
            'return_code': 'SUCCESS',
            'return_msg': 'OK'
        })
        WECHAT_FAIL = re_dict_to_xml({
            'return_code': 'FAIL',
            'return_msg': 'ERROR',
        })
        sign, xml_obj = xml_to_dict(xml)
        check_sign = sign_func(xml_obj, API_KEY)
        if sign != check_sign:   # 校验签名
            return HttpResponse(WECHAT_FAIL)
        if xml_obj['result_code'] != 'SUCCESS':
            return HttpResponse(WECHAT_FAIL)
        else:
            return HttpResponse(WECHAT_SUCCESS)
    
  6. 除了等待微信支付系统的通知,商户后台系统也可调用查询订单API来自主查询订单支付情况。

    关键参数:QUERY_ORDER_URL统一查询地址,nonce_str随机字符串,sign签名。
    

    代码实现

    def query_order(request):
    	data = {
    	        'out_trade_no': out_trade_no,
    	        'pay_status': 0,
    	        'err_msg': None
    	}
        nonce_str = generate_nonce_str()
        params = {
            "appid": APP_ID,
            "mch_id": MCH_ID,
            "nonce_str": nonce_str,
            "out_trade_no": out_trade_no,
        }
        sign = sign_func(params, API_KEY)
        params["sign"] = sign
        req_xml = re_dict_to_xml(params)
        res_xml = requests.post(QUERY_ORDER_URL, req_xml, verify=False)
        res_xml.encoding = 'utf8'
        _, res_obj = xml_to_dict(res_xml.text)
    
        if res_obj["return_code"] == "FAIL":
            return Response(res_obj["err_code_des"])
        else:
            result_code = res_obj["result_code"]
            trade_state = res_obj["trade_state"]
            if result_code == "SUCCESS":
                if trade_state == "SUCCESS":
                   	data['pay_status'] = 1
                else:
                    data['err_msg'] = res_obj['trade_state_desc']
            else:
                data['err_msg'] = res_obj['err_code_des']
        return Response(data)
    

    关于接口其他返回参数的详细信息及使用方法,可参考官方文档
    https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2


注意:微信API接口均采用POST

自定义的部分关键函数

随机字符串函数

微信支付API接口协议中使用的nonce_str,主要用来保证签名不可预测。

# 生成随机字符串
def generate_nonce_str(size=32):
    charsets = string.ascii_uppercase + string.digits
    result = []
    for index in range(0, size):
        result.append(random.choice(charsets))
    return "".join(result)
校验签名

关于生成签名的规则和参数拼接顺序可参考
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3

# 生成签名
def sign_func(payload, sign_key=None):
    params_list = []
    for key, value in payload.items():
        if value:
            params_list.append("%s=%s" % (key, value))
    params_list.sort()
    raw_str = "&".join(params_list)
    if sign_key:
        raw_str += "&key=%s" % sign_key
    md5 = hashlib.md5()
    md5.update(raw_str.encode('utf8'))
    return md5.hexdigest().upper()

微信支付API官方文档 https://pay.weixin.qq.com/wiki/doc/api/index.html

你可能感兴趣的:(django,微信支付,python)