微信当前支持的支付产品有如下这些:
可以根据自己的需要选择接入的支付方式。 接入指引
我们常用的支付方式:JSAPI支付
Native支付
APP支付
接入文档里面有各个接入方式的详细说明。这里只介绍Native支付
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。
● 直连模式:
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
● 服务商模式:
**—— 信息流 —— 资金流**
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见:服务商模式介绍
商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户
申请APPID
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。
如申请社交载体为公众号,请前往 公众平台申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
流程:
申请mchid
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
绑定APPID及mchid
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。
请根据以下步骤配置API key:
1. 登录微信商户平台,进入【账户中心 > API安全 】目录,设置APIV3密钥。
2. 在弹出窗口中点击“已沟通”。
3. 输入API密钥,内容为32位字符,包括数字及大小写字母。点击获取短信验证码。
生成密钥的方法有很多种。下面是一些常见的方法:
secrets
模块。这个模块专门用于生成高质量的随机数,适用于管理密码、帐户验证、安全令牌以及相关秘密的生成和管理。例如:import secrets
print(secrets.token_hex(16)) # 生成一个32位的十六进制密钥
urandom
函数和binascii
模块的hexlify
函数。例如:import os
import binascii
print(binascii.hexlify(os.urandom(24))) # 生成一个48位的十六进制密钥
import uuid
print(uuid.uuid4().hex) # 生成一个32位的十六进制密钥
记得保存好生成的密钥
4. 输入短信验证码,点击“确认”即设置成功。
5. 完成
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
1. 从2018年底开始,微信支付新入驻机构及商户都将使用CA签发证书,在证书申请页面上点击“申请证书”。
2. 在弹出窗口中点击“确定”。
3. 在弹出窗口内点击“下载证书工具”按钮下载证书工具。
4. 安装证书工具并打开,选择证书需要存储的路径后点击“申请证书”。
5. 在证书工具中,将复制的商户信息粘贴并点击“下一步”。
6. 获取请求串
7. 生成证书串
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
8. 在【证书工具】-“生成证书”环节,已完成申请证书流程,点击“查看证书文件夹”,查看已生成的证书文件。
9. 完成
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