之前一篇文章介绍python3封装成类调用微信JSAPI下单、支付、生成付款码,本文介绍python实现微信分账功能。
微信支付里面分账接口调用时需要证书,本文介绍python使用证书。
1 JSAPI文档地址
JSAPI文档,普通商户微信分账是通过https请求实现的。
2 代码实现
封装一个类WxPay,实现微信分账,有如下接口:
- 普通商户添加分账接收方
- 普通商户删除分账接收方
- 普通商户单次分账请求
- 普通商户查询分账请求
- 普通商户完结分账
2.1 类主体框架
import logging
import requests
import time
import json
import hmac
import hashlib
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Pragma': 'no-cache',
'User-Agent': 'self-defind-user-agent',
'Cookie': 'name=self-define-cookies-in header',
'X-Requested-With': 'XMLHttpRequest'
}
def get_md5(data, salt=True):
if salt:
return hashlib.md5('get{0}{1}md5'.format(data, time.time()).encode(encoding='UTF-8')).hexdigest()
else:
return hashlib.md5(data.encode(encoding='UTF-8')).hexdigest()
class WxPay:
__key = 'xxxxxxxx' # 商户API密钥,自己的商户密钥
__appid = 'xxxxxxxxx' # 小程序ID(是),自己的小程序ID
__mch_id = 'xxxxxx' # 商户号,自己的商户号
__device_info = '' # 设备号(否),这里用门店号做参数
__nonce_str = '' # 随机字符串(是)
__sign = '' # 签名(是)
__body = '' # 商品描述(是)
__out_trade_no = '' # 商户订单号(是),传入系统商品订单
__total_fee = '' # 标价金额(是),订单总金额,单位为分
__spbill_create_ip = '' # 终端IP(是)
__notify_url = 'https://www.xxxxxx.com/' # 通知地址(是),填项自己的url地址
__trade_type = 'JSAPI' # 交易类型(是),小程序支付
__cert_name = 'xxxx.p12' # 证书名称
__cert_pwd = 'xxxxx' # 同__mch_id
2.2 生成随机字符串函数
在类里面定义随机字符串生成函数,代码如下:
@staticmethod
def get_nonce_str():
return get_md5(WxPay.__appid)
2.3 生成MD5签名函数
在类里面定义签名字符串生成函数,代码如下:
@staticmethod
def get_sign_str(sign_dict: dict):
data = ''
sort_keys = sorted(sign_dict) # 从小到大排序
for i, key in enumerate(sort_keys):
data += '{0}={1}&'.format(key, sign_dict.get(key, ''))
data += 'key={0}'.format(WxPay.__key)
print(data)
return get_md5(data, False).upper()
注意:签名里面的key一定要先按升序排序,再组合生成MD5
2.4 生成HMAC-SHA256签名函数
@staticmethod
def create_hmac_sha256_signature(key: str, message: str):
byte_key = key.encode('utf-8')
message = message.encode('utf-8')
return hmac.new(byte_key, message, hashlib.sha256).hexdigest().upper()
@staticmethod
def get_sign_strc_hmac_sha256(sign_dict: dict):
data = ''
sort_keys = sorted(sign_dict) # 从小到大排序
for i, key in enumerate(sort_keys):
data += '{0}={1}&'.format(key, sign_dict.get(key, ''))
data += 'key={0}'.format(WxPay.__key)
print(data)
return WxPay.create_hmac_sha256_signature(WxPay.__key, data)
2.5 数据转换函数
@staticmethod
def get_xml_str(sign_dict: dict):
xml_data = '<xml>'
for k, v in sign_dict.items():
xml_data += '<{0}>{1}</{0}>'.format(k, v)
xml_data += '</xml>'
# print(xml_data)
return xml_data
@staticmethod
def xml_to_dict(xml_data: str):
dict_data = {}
result = xml_data.split('\n')
for res in result:
if 'total_fee' in res:
dict_data['total_fee'] = res.split('<total_fee>')[-1].split('<')[0]
continue
tmp1 = res.split('><![CDATA[')
if len(tmp1) == 2:
tmp2, tmp3 = tmp1[0].split('<'), tmp1[1].split(']')
dict_data[tmp2[-1]] = tmp3[0] if tmp3 else ''
return dict_data
2.6 创建带证书接口请求函数
- 先安装requests_pkcs12,用于发送带证书接口请求
- 微信申请证书,参照微信证书申请文档
- 代码如下:
@staticmethod
def send_post_cert(url, jsonData):
try:
from requests_pkcs12 import post
# 注意:用res.content,不用res.text,否则会有中文乱码
res = post(url=url, data=jsonData.encode('utf-8'), headers=headers,
pkcs12_filename=WxPay.__cert_name, pkcs12_password=WxPay.__cert_pwd)
print(res.content.decode("utf-8"))
return res.content.decode("utf-8")
except Exception as e:
logging.error('[send_get]Failed to json.load, {0}'.format(e))
return ''
说明:
- pkcs12_filename:对应的是证书名称
- pkcs12_password:对应证书密码,密码是商户号
- 证书格式:.p12格式证书
2.7 普通商户添加分账接收方
@staticmethod
def profit_add_receiver(type: str, account: str, relation_type: str): # 普通商户添加分账接收方
# https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id,
'receiver': json.dumps({'account': account, 'relation_type': relation_type, 'type': type},
separators=(',', ':'), ensure_ascii=False),
'nonce_str': nonce_str, 'sign_type': 'HMAC-SHA256'}
sign_str = WxPay.get_sign_strc_hmac_sha256(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post(url, data)
2.8 普通商户删除分账接收方
@staticmethod
def profit_del_receiver(type: str, account: str): # 普通商户删除分账接收方
# https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id,
'receiver': json.dumps({'account': account, 'type': type},
separators=(',', ':'), ensure_ascii=False),
'nonce_str': nonce_str, 'sign_type': 'HMAC-SHA256'}
sign_str = WxPay.get_sign_strc_hmac_sha256(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post(url, data)
2.9 普通商户单次分账请求
@staticmethod
def create_profit_sharing(transaction_id: str, out_order_no: str, type: str, account: str, amount: int,
description: str): # 普通商户单次分账请求
# https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id, 'transaction_id': transaction_id,
'out_order_no': out_order_no,
'receivers': json.dumps({'account': account, 'amount': amount, 'description': description,
'type': type}, separators=(',', ':'), ensure_ascii=False),
'nonce_str': nonce_str, 'sign_type': 'HMAC-SHA256'}
sign_str = WxPay.get_sign_strc_hmac_sha256(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post_cert(url, data)
2.10 普通商户查询分账请求
@staticmethod
def get_profit_sharing(transaction_id: str, out_order_no: str): # 普通商户查询分账请求
# https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
nonce_str = WxPay.get_nonce_str()
argc_dict = {'mch_id': WxPay.__mch_id, 'transaction_id': transaction_id,
'out_order_no': out_order_no, 'nonce_str': nonce_str, 'sign_type': 'HMAC-SHA256'}
sign_str = WxPay.get_sign_strc_hmac_sha256(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/pay/profitsharingquery'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post(url, data)
2.11 普通商户完结分账
@staticmethod
def finish_profit_sharing(transaction_id: str, out_order_no: str, description: str): # 普通商户完结分账
# https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id, 'transaction_id': transaction_id,
'out_order_no': out_order_no, 'description': description,
'nonce_str': nonce_str, 'sign_type': 'HMAC-SHA256'}
sign_str = WxPay.get_sign_strc_hmac_sha256(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post_cert(url, data)