微信支付 V3 版本的 Python 使用过程

本贴最后更新于 1393 天前,其中的信息可能已经沧海桑田

微信支付自推出 V3 版本接口以来,体验下来确实比 V2 有了很大的提升,但介于其官方没有提供 Python 版本的 SDK,故在此记录通过 Python3 来使用微信支付 V3 的主要过程。

1.搞清楚自己的身份

这一步看似可以忽略,但也需要仔细,笔者之前就犯了一个错,看了不对应的文档。
微信支付的商家有两种身份模式:普通商户、服务商

一般的大部分商户都是属于普通商户,只有品牌商等才为服务商,并且两者身份不能互换。
普通商户的 API 文档https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml
服务商 API 文档https://pay.weixin.qq.com/wiki/doc/apiv3_partner/index.shtml

2.配置阶段

需要在商户端界面申请 API 证书配置 APIv3 密钥,次过程可以参考每一个步骤右侧的查看指引按钮。
image.png

3.构造签名

V3 版本的接口调用需要对请求进行签名,参考 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml 根据官方的文档可以按此步骤生成请求签名。

class WeixinPayUtil(object): @staticmethod def signature(private_key_path, sign_str): """ 生成签名值 private_key_path 私钥路径 sign_str 签名字符串 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml """ with open(private_key_path) as file: private_key = file.read() try: rsa_key = RSA.import_key(private_key) signer = pkcs1_15.new(rsa_key) digest = SHA256.new(sign_str.encode('utf-8')) return b64encode(signer.sign(digest)).decode('utf-8') except Exception: raise WeixinPaySignIError
def _generate_request_sign(self, url_path, data, method='POST'): """ 生成请求签名 """ sign_list = [method, url_path, self.timestamp, self.nonce_str] if data is not None: sign_list.append(json.dumps(data)) else: sign_list.append('') sign_str = '\n'.join(sign_list) + '\n' return WeixinPayUtil.signature(private_key_path=self.cert_dir + 'api_key.pem', sign_str=sign_str)

统一支付

准备好了上面这一切就可以开始调用 V3 版本的接口了,以统一支付为例,笔者写了一个示例,其中部分传参的命名和官方的命名稍有些不一致,至此整个 V3 版本的微信支付对接就告一段落了。

class WeixinPay(object): """ 微信支付 """ base_url = 'https://api.mch.weixin.qq.com' def __init__(self, mch_id, app_id, api_v3_key, mch_cert_no, cert_dir, notify_url): self.mch_id = mch_id self.app_id = app_id self.api_v3_key = api_v3_key self.mch_cert_no = mch_cert_no self.cert_dir = cert_dir self.notify_url = notify_url self.timestamp = str(int(time.time())) self.nonce_str = ''.join(random.sample(string.ascii_letters + string.digits, 16)) def _generate_request_sign(self, url_path, data, method='POST'): """ 生成请求签名 """ sign_list = [method, url_path, self.timestamp, self.nonce_str] if data is not None: sign_list.append(json.dumps(data)) else: sign_list.append('') sign_str = '\n'.join(sign_list) + '\n' return WeixinPayUtil.signature(private_key_path=self.cert_dir + 'api_key.pem', sign_str=sign_str) def _generate_pay_sign(self, app_id, package): """ 生成支付签名 """ sign_list = [app_id, self.timestamp, self.nonce_str, package] sign_str = '\n'.join(sign_list) + '\n' return WeixinPayUtil.signature(private_key_path=self.cert_dir + 'api_key.pem', sign_str=sign_str) def _generate_auth_header(self, signature): """ 生成授权请求头 """ return f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mch_id}",nonce_str="{self.nonce_str}",' \ f'signature="{signature}",timestamp="{self.timestamp}",serial_no="{self.mch_cert_no}"' def unified_order(self, order_id, openid, amount, desc, mch_id=None, notify_url=None, profit_sharing=False, expire_time=None, attach=None, goods_tag=None, detail=None, scene_info=None, currency='CNY'): """ 统一下单 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml """ url_path = '/v3/pay/transactions/jsapi' url = self.base_url + url_path data = { 'appid': self.app_id, 'mchid': mch_id if mch_id is not None else self.mch_id, 'description': desc, 'out_trade_no': order_id, 'notify_url': notify_url if notify_url is not None else self.notify_url, 'settle_info': { 'profit_sharing': profit_sharing }, 'amount': { 'total': amount, 'currency': currency }, 'payer': { 'openid': openid } } if attach: data.update({'attach': attach}) if expire_time: data.update({'time_expire': expire_time}) if goods_tag: data.update({'goods_tag': goods_tag}) if detail: data.update({'detail': detail}) if scene_info: data.update({'scene_info': scene_info}) signature = self._generate_request_sign(url_path=url_path, data=data) headers = {'Authorization': self._generate_auth_header(signature)} res = requests.post(url=url, json=data, headers=headers, timeout=TIMEOUT).json() # 支付签名 pay_sign = self._generate_pay_sign(app_id=self.app_id, package='prepay_id=' + res['prepay_id']) return { 'timestamp': self.timestamp, 'nonce_str': self.nonce_str, 'prepay_id': res['prepay_id'], 'sign_type': 'RSA', 'pay_sign': pay_sign }
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    557 引用 • 675 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...