微信支付(Native支付)接入流程

一、 常用支付方式

微信当前支持的支付产品有如下这些:

微信支付(Native支付)接入流程_第1张图片

可以根据自己的需要选择接入的支付方式。 接入指引

我们常用的支付方式:JSAPI支付 Native支付 APP支付

  • JSAPI支付: 商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款
  • Native支付: 商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款
  • APP 支付: 用户在商家的APP中下单,跳转到微信中完成支付,支付完后跳回到商家APP内,展示支付结果,随后通过微信支付公众号下发账单消息

二、接入准备

接入文档里面有各个接入方式的详细说明。这里只介绍Native支付

1.选择接入模式

商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。

● 直连模式:

信息、资金流:微信支付—>直连商户

直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户

● 服务商模式:

微信支付(Native支付)接入流程_第2张图片

                                                       **—— 信息流    —— 资金流**

服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商

请结合自身实际情况来选择接入模式。

服务商模式相关说明详见:服务商模式介绍

2.参数申请

商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户

  1. 申请APPID

    由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。

    对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。

    如申请社交载体为公众号,请前往 公众平台申请

    如申请社交载体为小程序,请前往 小程序平台 申请

    如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请

    商户可根据实际的业务需求来选择申请不同的社交载体。

    各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。

    不同载体可使用场景,见下图:
    微信支付(Native支付)接入流程_第3张图片

    流程:

    1. 注册微信开放平台账号,登录
      微信支付(Native支付)接入流程_第4张图片

    2. 创建移动应用。(直连商户,该社交载体只可以是公众号(什么是公众号),小程序(什么是小程序)或APP)

    3. 接下来走流程就好了

  2. 申请mchid

    申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
    微信支付(Native支付)接入流程_第5张图片

    申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。

    注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。

  3. 绑定APPID及mchid

    APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。

    直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。

    微信支付(Native支付)接入流程_第6张图片

3.配置API key

API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。

请根据以下步骤配置API key:

  • 1. 登录微信商户平台,进入【账户中心 > API安全 】目录,设置APIV3密钥。

    微信支付(Native支付)接入流程_第7张图片

  • 2. 在弹出窗口中点击“已沟通”。

    微信支付(Native支付)接入流程_第8张图片

  • 3. 输入API密钥,内容为32位字符,包括数字及大小写字母。点击获取短信验证码。

    微信支付(Native支付)接入流程_第9张图片

    生成密钥的方法有很多种。下面是一些常见的方法:

    1. 使用Python内置的secrets模块。这个模块专门用于生成高质量的随机数,适用于管理密码、帐户验证、安全令牌以及相关秘密的生成和管理。例如:
    import secrets
    print(secrets.token_hex(16)) # 生成一个32位的十六进制密钥
    
    1. 使用os模块的urandom函数和binascii模块的hexlify函数。例如:
    import os
    import binascii
    print(binascii.hexlify(os.urandom(24))) # 生成一个48位的十六进制密钥
    
    1. 使用uuid模块生成一个UUID。虽然UUID不是专为密码学而设计的,但它们是全球唯一的,并且在大多数情况下可以作为一个非常好的秘密密钥。例如:
    import uuid
    print(uuid.uuid4().hex) # 生成一个32位的十六进制密钥
    

    记得保存好生成的密钥

    微信支付(Native支付)接入流程_第10张图片

  • 4. 输入短信验证码,点击“确认”即设置成功。

  • 5. 完成

4.下载并配置商户证书

商户API证书具体使用说明可参见接口规则文档中私钥和证书章节

商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书

以下为具体下载步骤:

  • 1. 从2018年底开始,微信支付新入驻机构及商户都将使用CA签发证书,在证书申请页面上点击“申请证书”。

    微信支付(Native支付)接入流程_第11张图片

  • 2. 在弹出窗口中点击“确定”。

    微信支付(Native支付)接入流程_第12张图片

  • 3. 在弹出窗口内点击“下载证书工具”按钮下载证书工具。

    微信支付(Native支付)接入流程_第13张图片

  • 4. 安装证书工具并打开,选择证书需要存储的路径后点击“申请证书”。

    微信支付(Native支付)接入流程_第14张图片

  • 5. 在证书工具中,将复制的商户信息粘贴并点击“下一步”。

    微信支付(Native支付)接入流程_第15张图片

  • 6. 获取请求串

    微信支付(Native支付)接入流程_第16张图片

    微信支付(Native支付)接入流程_第17张图片

    微信支付(Native支付)接入流程_第18张图片

  • 7. 生成证书串

    步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;

    步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;

    步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;

    步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节

    微信支付(Native支付)接入流程_第19张图片

    微信支付(Native支付)接入流程_第20张图片

    微信支付(Native支付)接入流程_第21张图片

  • 8. 在【证书工具】-“生成证书”环节,已完成申请证书流程,点击“查看证书文件夹”,查看已生成的证书文件。

  • 9. 完成

5. 代码

import logging
import os

from wechatpayv3 import WeChatPayType, WeChatPay

"""
微信支付接入
SDK 文档:https://pypi.org/project/wechatpayv3/
pip install wechatpayv3
"""

logging.basicConfig(filename=os.path.join(os.getcwd(), 'pay.log'), level=logging.DEBUG, filemode='a',
                    format='%(asctime)s - %(process)s - %(levelname)s: %(message)s')
LOGGER = logging.getLogger("pay")


class WechatClient:
    def __init__(self, mchid, appid, private_key_path, cert_serial_no, api_v3_key, partner_mode=False, notify_url=None,
                 cert_dir=None, proxy=None):
        """
         初始化数据
         :param mchid: 微信支付商户号,服务商模式下为服务商户号,即官方文档中的sp_mchid。
         :param appid: APPID,应用ID,服务商模式下为服务商应用ID,即官方文档中的sp_appid,也可以在调用接口的时候覆盖。
         :param private_key_path: 商户证书私钥,此文件不要放置在下面设置的CERT_DIR目录里。
         :param cert_serial_no: 商户证书序列号
         :param api_v3_key: API v3密钥, https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml
         :param partner_mode:  接入模式:False=直连商户模式,True=服务商模式。
         :param notify_url:  回调地址,也可以在调用接口的时候覆盖。
         :param cert_dir:  微信支付平台证书缓存目录,初始调试的时候可以设为None,首次使用确保此目录为空目录。
         :param proxy:  代理设置,None或者{"https": "http://10.10.1.10:1080"},详细格式参见[https://requests.readthedocs.io/en/latest/user/advanced/#proxies](https://requests.readthedocs.io/en/latest/user/advanced/#proxies)
         """

        self.mchid = mchid
        self.appid = appid
        self.partner_mode = partner_mode
        self.private_key_path = private_key_path
        self.cert_serial_no = cert_serial_no
        self.api_v3_key = api_v3_key
        self.notify_url = notify_url
        self.cert_dir = cert_dir
        self.proxy = proxy
        self.wxpay = WeChatPay(
            wechatpay_type=WeChatPayType.NATIVE,
            mchid=self.mchid,
            private_key=self.read_secret(self.private_key_path),
            cert_serial_no=self.cert_serial_no,
            apiv3_key=self.api_v3_key,
            appid=self.appid,
            notify_url=self.notify_url,
            cert_dir=self.cert_dir,
            logger=LOGGER,
            partner_mode=self.partner_mode,
            proxy=self.proxy)

    @staticmethod
    def read_secret(secret_path):
        """
        从文件加载秘钥
        :param secret_path:
        :return:
        """
        # 商户证书私钥,此文件不要放置在下面设置的CERT_DIR目录里。
        with open(secret_path) as f:
            return f.read()

    def create_order(self, out_trade_no, amount, description):
        """
         native下单
        :param out_trade_no:  订单号
        :param amount: 金额
        :param description: 描述
        :return:
        下单成功后即可获取到'code_url',将'code_url'转换为二维码,并用微信扫码即可进行支付
        """
        code, message = self.wxpay.pay(
            description=description,
            out_trade_no=out_trade_no,
            amount={'total': amount},
            pay_type=WeChatPayType.NATIVE
        )
        return code, message

    def close_order(self, out_trade_no, mchid=None, sub_mchid=None):
        """
         关闭订单
        :param out_trade_no: 商户订单号,示例值:'1217752501201407033233368018'
        :param mchid: 微信支付商户号,可不传,默认传入初始化的mchid。示例值:'987654321'
        :param sub_mchid: (服务商模式)子商户的商户号,由微信支付生成并下发。示例值:'1900000109'
        """
        return self.wxpay.close(out_trade_no, mchid=mchid, sub_mchid=sub_mchid)

    def refund(self, out_refund_no, amount, transaction_id=None, out_trade_no=None, reason=None, funds_account=None,
               goods_detail=None, notify_url=None, sub_mchid=None):
        """
         申请退款
        :param out_refund_no: 商户退款单号,示例值:'1217752501201407033233368018'
        :param amount: 金额信息,示例值:{'refund':888, 'total':888, 'currency':'CNY', 'refund_fee':100}
        :param transaction_id: 微信支付订单号,示例值:'1217752501201407033233368018'
        :param out_trade_no: 商户订单号,示例值:'1217752501201407033233368018'
        :param reason: 退款原因,示例值:'商品已售完'
        :param funds_account: 退款资金来源,示例值:'AVAILABLE'
        :param goods_detail: 退款商品,示例值:{'merchant_goods_id':'1217752501201407033233368018', 'wechatpay_goods_id':'1001', 'goods_name':'iPhone6s 16G', 'unit_price':528800, 'refund_amount':528800, 'refund_quantity':1}
        :param notify_url: 通知地址,示例值:'https://www.weixin.qq.com/wxpay/pay.php'
        :param sub_mchid: (服务商模式)子商户的商户号,由微信支付生成并下发。示例值:'1900000109'
        """
        return self.wxpay.refund(out_refund_no, amount, transaction_id=transaction_id, out_trade_no=out_trade_no,
                                 reason=reason, funds_account=funds_account, goods_detail=goods_detail,
                                 notify_url=notify_url, sub_mchid=sub_mchid)

    def notify_verify(self, headers, body):
        """
        验证回调结果
        :param headers: 回调接口收到的headers
        :param body: 回调接口收到的body
        """
        result = self.wxpay.callback(headers, body)
        if result and result.get('event_type') == 'TRANSACTION.SUCCESS':
            resource = result.get('resource') or {}
            data = dict(
                appid=resource.get('appid'),
                mchid=resource.get('mchid'),
                out_trade_no=resource.get('out_trade_no'),
                transaction_id=resource.get('transaction_id'),
                trade_type=resource.get('trade_type'),
                trade_state=resource.get('trade_state'),
                trade_state_desc=resource.get('trade_state_desc'),
                bank_type=resource.get('bank_type'),
                attach=resource.get('attach'),
                success_time=resource.get('success_time'),
                payer=resource.get('payer'),
                amount=resource.get('amount').get('total'),
            )
            return data
        else:
            return None

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