之前一篇文章介绍python3封装成类调用微信JSAPI下单、支付、生成付款码,本文介绍python实现微信退款功能。
微信支付里面像退款等涉及到资金回滚接口调用时需要证书,本文介绍python使用证书。
1 JSAPI文档地址
JSAPI文档,微信下单和支付是通过https请求实现的。
2 代码实现
封装一个类WxPay,实现退款申请、退款查询
2.1 类主体框架
import logging
import requests
import time
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 生成签名函数
在类里面定义签名字符串生成函数,代码如下:
@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 数据转换函数
@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.5 创建带证书接口请求函数
- 先安装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.6 创建申请退款函数
@staticmethod
def create_refund_order(out_trade_no: str, out_refund_no: str, total_fee: int,
refund_fee: int, refund_desc: str=''): # 申请退款
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id, 'nonce_str': nonce_str,
'out_trade_no': out_trade_no, 'out_refund_no': out_refund_no, 'total_fee': total_fee,
'refund_fee': refund_fee, 'refund_desc': refund_desc}
sign_str = WxPay.get_sign_str(argc_dict)
argc_dict['sign'] = sign_str
xml_data = WxPay.get_xml_str(argc_dict)
print(xml_data)
url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post_cert(url, data)
2.7 创建退款查询函数
@staticmethod
def get_refund_query(out_trade_no: str): # 退款查询
nonce_str = WxPay.get_nonce_str()
argc_dict = {'appid': WxPay.__appid, 'mch_id': WxPay.__mch_id, 'out_trade_no': out_trade_no,
'nonce_str': nonce_str}
sign_str = WxPay.get_sign_str(argc_dict)
argc_dict['sign'] = sign_str
url = 'https://api.mch.weixin.qq.com/pay/refundquery'
data = WxPay.get_xml_str(argc_dict)
return WxPay.send_post(url, data)