问题背景

最近接入微信支付,微信官方并没有提供Python版的服务端SDK,因而只能根据文档手动实现一版,这里记录一下微信支付的整体流程、踩坑过程与最终具体实现。

微信支付APP下单流程

根据微信官方文档: https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_5_2.shtml

下单流程如下:



和支付宝不同,微信多了一个预付单的概念,这里把APP下单实际分为四大部分,其中包含请求微信后端需要的首次签名和需要返回给APP的二次支付信息签名--这里踩一个小坑,流程图中并没把第二次签名支付信息需要返回给APP的步骤画出来(即下面的步骤6.5),因而一开始误以为只需要返回prepay_id给客户端,导致校验失败。

一. 对应步骤1~4,APP 请求业务后端,业务后台进行V3签名后,请求微信后端生成预付单prepay_id

二. 对应步骤5~6.5,业务后端收到微信后端返回prepay_id,将支付相关参数打包进行二次签名后返回给APP,这里相比流程图多了一个6.5--即业务后端返回签名支付信息到APP

三. 对应步骤7~18,APP收到业务后端返回签名支付信息后调起SDK发起支付请求,收到同步消息结果通知

四. 对应步骤19~22,APP查询业务后端,业务后端通过回调通知或直接查询微信后端返回最终支付结果

代码实现

首次签名逻辑

第一次请求生成预付单号的签名文档为:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml, 共5个部分参与签名,其组成格式为:

HTTP请求方法\nURL\n请求时间戳\n请求随机串\n请求报文主体\n

对应签名代码:

class WechatPayDALBase(object):
def __init__(self, mch_appid, mchid, v3key, serial_no, client_key):
self.mch_appid = mch_appid
self.mchid = mchid
self.v3key = v3key
# serial_no可通过openssl直接获取, 例: openssl x509 -in 1900009191_20180326_cert.pem -noout -serial
self.serial_no = serial_no with open(client_key, 'r') as ifile:
pkey = RSA.importKey(ifile.read())
self.signer = pkcs1_15.new(pkey) def compute_sign_v3(self, method, url, body):
'''
V3签名逻辑
'''
ts = int(time.time())
nonce = self.generate_nonce()
uparts= parse_url(url)
ustr = uparts.path + ('?{}'.format(uparts.query) if uparts.query else '')
content = '{}\n{}\n{}\n{}\n{}\n'.format(method, ustr, ts, nonce, body) digest = SHA256.new(content.encode('utf-8'))
sign_v = base64.b64encode(self.signer.sign(digest)).decode('utf-8')
sign_str = 'serial_no="{}",mchid="{}",timestamp="{}",nonce_str="{}",signature="{}"'.format(
self.serial_no, self.mchid, ts, nonce, sign_v)
return sign_str def make_headers_v3(self, url, headers=None, body='', method='GET'):
'''
微信支付V3版本签名header生成函数
'''
if not headers:
headers = {}
headers['Accept'] = 'application/json'
sign = self.compute_sign_v3(method, url, body)
auth_info = 'WECHATPAY2-SHA256-RSA2048 {}'.format(sign)
headers['Authorization'] = auth_info
return headers def generate_nonce(self):
rnd = int(time.time()) + random.randint(100000, 1000000)
nonce = hashlib.md5(str(rnd).encode()).hexdigest()[:16]
return nonce

二次签名逻辑

由业务后端返回给APP的二次签名信息文档为:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml

共4个部分参与签名,其组成格式为:

应用id\n时间戳\n随机字符串\n预支付交易会话ID\n

返回签名支付信息的对应代码:

    def get_pay_sign_info(self, prepay_id):
ts = int(time.time())
nonce = self.generate_nonce()
content = '{}\n{}\n{}\n{}\n'.format(self.mch_appid, ts, nonce, prepay_id) digest = SHA256.new(content.encode('utf-8'))
sign_v = base64.b64encode(self.signer.sign(digest)).decode('utf-8')
return {
'appid': self.mch_appid,
'partnerid': self.mchid,
'timestamp': str(ts),
'noncestr': nonce,
'prepay_id': prepay_id,
'package': 'Sign=WXPay',
'sign': sign_v,
}

业务后端查询订单详情

文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_2.shtml

代码如下:

    def query_order(self, out_trade_no):
'''
查询指定订单信息
'''
url = f'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid={self.mchid}'
headers = self.make_headers_v3(url)
rsp = requests.get(url, headers=headers)
pay_logger.info('out_trade_no:{}, rsp:{}|{}'.format(out_trade_no, rsp.status_code, rsp.text))
rdct = rsp.json()
return rdct

业务后端调用APP下单API

文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml

代码如下:

    def create_order_info(self, data, callback_url):
'''
创建微信预支付订单, 注意包含两次签名过程:
首次签名用于请求微信后端获取prepay_id
二次签名信息返回客户端用于调起SDK支付
'''
url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app'
ndt = datetime.now()
out_trade_no = self.generate_partner_trade_no(ndt)
data = {
'mchid': self.mchid,
'out_trade_no': out_trade_no,
'appid': self.mch_appid,
'description': data['subject'],
'notify_url': callback_url,
'amount': {
'currency': 'CNY',
'total': int(data['price']),
},
'time_expire': (ndt + timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%S+08:00')
}
jdata = json.dumps(data, separators=[',', ':'])
headers = {'Content-Type': 'application/json'}
# 第一次签名, 直接请求微信后端
headers = self.make_headers_v3(url, headers=headers, body=jdata, method='POST')
rsp = requests.post(url, headers=headers, data=jdata)
pay_logger.info('rsp:{}|{}'.format(rsp.status_code, rsp.text))
rdct = rsp.json()
# 第二次签名, 返回给客户端调用
sign_info = self.get_pay_sign_info(rdct['prepay_id'])
return sign_info

源码地址

试水代码开源,把相关代码分享在了github:https://github.com/liuzhi67/wechat-pay-python

转载请注明出处,原文地址:https://www.cnblogs.com/AcAc-t/p/wechat_pay_by_python.html

微信APP支付V3版本签名 && APP下单/订单查询接口Python版实现的更多相关文章

  1. php微信支付v3版本签名生成

    前几天需要对接微信支付卡包营销活动需要对接微信新版SDKv3版 签名生成规则,微信的官方文档里面说明的还算可以吧,不过个人觉得不太理想- -.  自己调试的时候调试了半天才找了错误原因. https: ...

  2. Java中的微信支付(1):API V3版本签名详解

    1. 前言 最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑.目前微信支付的API已经发展到V3版本,采用了流行的Restful风格. 今天来分享微信支付的 ...

  3. 利用微信支付的订单查询接口可以在APP 中提高支付的可靠性

    最近公司有一个应用,用户可以在微信公众号上面下单,也可以在APP 中下单. 当用户在公共号上面下单时,微信支付成功可以返回微信支付单号,但是在APP 中用户微信支付时,个别时候会出现用户已经付款成功, ...

  4. 微信H5支付.NET版本备忘

    微信H5支付.NET版本备忘

  5. 微信支付(公众号支付APIJS、app支付)服务端统一下单接口java版

    一.微信公众号支付APIJS: 要完整的实现微信支付功能,需要前后端一起实现,还需要微信商户平台的配置.这里只是涉及服务端的代码. jar包:pom.xml <!-- ↓↓↓↓↓↓↓↓ 支付相关 ...

  6. 微信支付(java版本)_统一下单

    最近工作接触到微信支付,刚开始解决微信支付很神秘,接触之后发现并没有那么神秘,就是有很多坑,在开发的时候需要注意,整理出来: 1.准备工作 首先需要登录微信支付公众平台阅读接口文档,地址:https: ...

  7. 微信移动支付V3开发详细教程服务端采用.net mvc webapi(C#)

    转自:http://www.kwstu.com/ArticleView/netmvc_201511132050268716 最近开发手机app需要实现移动支付功能,由于考虑支付安全将微信支付生成签名写 ...

  8. 微信支付V3版本的那些事

    最近在接入微信支付这块功能,博客园也有很多博友发表了支付的各种吐槽和解决之道,基于那些经验分享之上,我也来说说我的填坑之路. 1:准备工作,首先去申请注册一个公众号——服务号,然后将微信支付功能开通, ...

  9. 微信支付v3版本NET 图片上传API

    最近在写特约服务商进件的由于微信官方没有DEMO,导致踩了很多坑,特把自己经验分享给大家. 注意几点: 1.上传图片签名不是把所有body内容都进行签名,只需签名计算的请求主体为meta的json串: ...

随机推荐

  1. 【C#基础概念】字节顺序(大端、小端)

    字节顺序,又称端序或尾序(英語:Endianness),在计算机科学领域中,指電腦記憶體中或在数字通信链路中,组成多字节的字的字节的排列顺序. 例如假设上述变量x类型为int,位于地址0x100处,它 ...

  2. 关于C#理解装箱与拆箱

    目录 1.理解装箱 2.理解拆箱 3.生成的 IL 代码 4.实际应用 5.小结 1.理解装箱 简单地说,装箱就是将一个值类型的数据存储在一个引用类型的变量中. 假设你一个方法中创建了一个 int 类 ...

  3. 2020CCPC长春F. Strange Memory

    题目大意 一棵以 \(1\) 为根的 \(n(2\leq n\leq 10^5)\) 的树,每个节点 \(i\) 有权值 \(a_{i}(1\leq a_{i}\leq 10^6)\) ,求 \(\s ...

  4. 计算机网络——HTTP

    目录 计算机网络-HTTP篇 HTTP 基本概念 常见状态码 常见字段 Get 与 Post HTTP 特性 HTTP(1.1) HTTP/1.1 HTTPS 与 HTTP HTTP/1.1.HTTP ...

  5. net core or Linux

    某用户执行net core sdk 版本不生效 sudo chmod +x /home/username/netcore3.1sdk/dotnet //某个用户执行新版本net core sdk

  6. 服务端&客户端注册进Eureka

    目录 服务端(接口提供方) 创建项目 导入Eureka客户端POM 启动类添加注解 配置YML 暴漏接口 启动服务 集群 配置成功后页面如下 客户端(接口调用方) 修改Yml文件 配置类 启动类添加注 ...

  7. 分享自研实现的多数据源(支持同DB不同表、跨DB表、内存数据、外部系统数据等)分页查询工具类实现原理及使用

    思考: 提起分页查询,想必任何一个开发人员(不论是新手还是老手)都能快速编码实现,实现原理再简单不过,无非就是写一条SELECT查询的SQL语句,ORDER BY分页排序的字段, 再结合limit ( ...

  8. linux定时任务 - at定时任务

    at命令是一次性定时计划任务,at的守护进程atd会以后台模式运行,检查作业队列来运行作业.atd守护进程会检查系统上的一个特殊目录来获取at命令的提交的作业,默认情况下,atd守护进程每60秒检查一 ...

  9. git同步代码到另一分支

    将dev分支的代码同步到master 方法一:用git命令 1.git checkout master 2.git merge dev 3.git push --set-upstream origin ...

  10. JVM学习总结(一)

    JVM--Java虚拟机 1.类加载器 JVM虚拟机的类加载器有三个 bootstrapClassLoader 引导类加载器 是有C语言编写,在JVM虚拟机启动时 加载到内存中负责加载rt.jar夹包 ...