• pc网站和手机端h5网站开发接入微信支付


    有关支付类开发,现在比以前要简单很多了,微信和支付宝两大支付巨头早已经给出了非常详细的接入文档,并且迭代了好多版本,但在实际开发中其实文档的可读性还是有些磕磕绊绊的,而且也有一些坑需要注意。以微信支付来说,其实大多数商户的接入方式,文档里还不是很直白,引导也有些混乱,我也是搞了一段时间才摸清楚里面的大致逻辑。

    1. 准备工作

    首先需要公司以商户身份在微信支付中开通账号,涉及到上传公司的一些营业执照,法人身份等信息,审核通过后即可开通。后台界面如下:

    2. 功能介绍

    交易中心是查看你的微信支付的订单信息的,你的系统接入微信支付后,你系统里的订单和微信支付的订单有个1对1的对应关系,你的应用用户以后凡是支付过的订单都可以在这里面查看,核对。

    账户中心,在这里公司管理员可以给公司各种角色,比如开发人员,财务,客服等员工开通账号并配置权限,而且还有支付安全信息的配置,比如密钥,证书这些。

    营销中心,可以给你的应用配置红包,满减,消费券等活动参数,小型应用可以不看。

    产品中心,这个界面是员工的主要操作台,管理员给公司各个角色配置的功能,都在这里操作,比如我作为开发人员,财务,运营,在这里都能看到我可以操作的功能模块。

    数据中心,主要用来排查错误异常的。

    本文以开发者角度来科普微信支付怎么接入,技术栈为angularjs+nodejs+mongo

    3. 开发接入

    我开发的系统是pc网站和h5手机端接入微信支付,需求很简单:pc端用户选购完产品点击支付,弹出二维码并设置倒计时时间,用户扫码完成支付,类似12306pc网站那种。h5手机站需要在用户点击支付时候,唤起微信app进行支付操作。(网站只接入微信支付,不接入微信登录)

    用户支付的钱先到我们公司的总账户,然后公司对公分润给供应商。

    3.1 管理员开通权限

    管理员需要在账户中心给开发人员开通:native支付,h5支付这三个功能。pc端网站生成二维码可以用native支付手机端h5站就选择h5支付

    3.2 关键参数配置

    要接入微信支付并调用api接口,以下几个关键参数要在开发前进行配置。

    mchid和appid

    mchid就是商户号,只要注册通过成为商户,登陆后右上角会自动显示你的商户号。

    appid是你的应用id,怎么理解呢,就是你要接入微信支付这个功能前,必须注册一个微信生态里的应用,才能使用微信支付。这些应用可以是服务号,订阅号,小程序等等,具体解释可以看这里:https://kf.qq.com/faq/1801116VJfua1801113QVNVz.html

    感觉appid这个逻辑还是有些强制性,微信强制开发者遵循他自身的生态闭环,比如我们要开发的这个平台,用的是我们公司之前的一个服务号,但是这个服务号跟系统其实没啥联系。

    API证书和私钥, Apiv3密钥,serial_no

    这三个概念容易搞混,我大白话解释一下,你想把你的系统接入微信支付,微信官方得给你一个授权的身份,你只有这个身份合法了,调用人家接口人家才认得你,不然都按非法请求。API证书和私钥就类似于你主动调用微信接口使用到的公私钥对,获取方式和概念解释如下:

    https://kf.qq.com/faq/161222NneAJf161222U7fARv.html

    私钥和证书-接口规则 | 微信支付商户平台文档中心

    利用证书工具最终会生成apiclient_cert.pem,apiclient_key.pem这两个文件,可以简单理解这两个文件就是证书和私钥。调用时候需要在http请求头设置一个Authorization参数,微信端就是来通过解析Authorization的值判断你这个请求是否合法。

    Authorization参数怎么生成,具体方法可以看签名生成-接口规则 | 微信支付商户平台文档中心

    上面解释了api证书和私钥,那另外的Apiv3密钥是什么,当微信端回调你的接口时候,发过来加密的请求,你要用的就是Apiv3密钥进行解密。再直白点说两者区别,当你调用微信接口时候用到的是api证书和私钥加签,当微信端回调你的接口时候,你用Apiv3密钥解签。微信生成订单接口支持回调的,就是说支付成功后微信可以回调我的一个接口修改这个订单状态,但是文档里说可能有延迟以及支付成功但始终无回调的情况发生,因此在我的开发中对于订单状态的修改,我是主动去查询微信端接口的,而不是依赖于微信回调,所以在我的系统中Apiv3密钥就没有用到

    serial_no是商户api证书序列号,发送请求时候放在header里的Wechatpay-Serial就行了。

    4. 代码相关

    微信支付api相关的接口调用文档在微信支付-开发者文档,注意微信目前文档里涉及到v2和v3两个版本,目前接入一般直接选择最新的v3版本,所以看文档要看v3版本的文档。我在实际开发中查看微信官方文档时,里面有些跳转混乱,会在v2和v3来回跳,记住看到的文档里url包含/v3就是最新文档,比如下面这个:

    我后台用的是node,但微信api接口只有java,.net, python示例(为啥不支持node),我后来在社区里找到了两个支持node版本的npm包,wechatpay-node-v3和wxpay-v3,前者比较好用。项目git地址在GitHub - klover2/wechatpay-node-v3-ts: 微信支付v3

    可以通过组装http调用,如下:

    1. var WxPay = require('wechatpay-node-v3');
    2. const pay = new WxPay({
    3. appid: config.appid,
    4. mchid: config.mchid,
    5. publicKey: fs.readFileSync(__dirname + "/wxkey/" + 'apiclient_cert.pem'), // 公钥
    6. privateKey: fs.readFileSync(__dirname + "/wxkey/" + 'apiclient_key.pem'), // 秘钥
    7. serial_no: config.serial_no
    8. });
    9. var orderNo = "native121775ee0120140703355773bb";
    10. function testPay() {
    11. try {
    12. const params =
    13. {
    14. "mchid": config.mchid, // 商户号
    15. "out_trade_no": orderNo, // 系统订单号
    16. "appid": config.appid, // appId
    17. "time_expire": "2022-05-23T14:57:00+08:00", // 过期时间
    18. "description": "Image形象店-深圳腾大-QQ公仔", //
    19. "notify_url": "https://weixin.qq.com/", // 回调地址,如果不参与回调,可以随便填
    20. "amount": {
    21. "total": 1, // 金额,单位分
    22. "currency": "CNY"
    23. },
    24. }
    25. const nonce_str = Math.random().toString(36).substr(2, 15), //随机字符串
    26. timestamp = parseInt(+new Date() / 1000 + '').toString(), //时间戳 秒
    27. url = '/v3/pay/transactions/native';
    28. // 获取签名
    29. const signature = pay.getSignature('POST', nonce_str, timestamp, url, params); //如果是get 请求 则不需要params 参数拼接在url上 例如 /v3/pay/transactions/id/12177525012014?mchid=1230000109
    30. // 获取头部authorization 参数
    31. const authorization = pay.getAuthorization(nonce_str, timestamp, signature);
    32. const result = await request
    33. .post('https:api.mch.weixin.qq.com/v3/pay/transactions/native')
    34. .send(params)
    35. .set({
    36. Accept: 'application/json',
    37. 'Content-Type': 'application/json',
    38. 'User-Agent':
    39. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
    40. Authorization: authorization,
    41. "Wechatpay-Serial" : config.serial_no
    42. });
    43. const result = await pay.transactions_native(params);
    44. } catch (error) {
    45. console.log(error);
    46. }
    47. }

    这个npm包也支持隐式直接调用,也可以一个方法搞定,源码大家可以自行研究

    1. function testPay() {
    2. return new Promise ((resolve) => {
    3. pay.transactions_native(params).then((result) => {
    4. console.log('result==========>', result);
    5. if (result.status == 200) {
    6. resolve(result.code_url);
    7. } else {
    8. resolve(result.message);
    9. }
    10. })
    11. })
    12. }

    主流程主要涉及的三个接口:

    1. 生成订单

    对应wechatpay-node-v3里面的方法就是pay.transactions_native(pc端),pay.transactions_h5(手机端)

    https://github.com/klover2/wechatpay-node-v3-ts/blob/master/docs/transactions_native.md

    https://github.com/klover2/wechatpay-node-v3-ts/blob/master/docs/transactions_h5.md

    微信api对应:

    native生成订单

    h5生成订单

    2.关闭订单(取消)

    对应wechatpay-node-v3里面的方法就是pay.closewechatpay-node-v3-ts/close.md at master · klover2/wechatpay-node-v3-ts · GitHub

    微信api对应:关闭订单

    3. 查询订单状态

    对应wechatpay-node-v3里面的方法就是pay.queryhttps://github.com/klover2/wechatpay-node-v3-ts/blob/master/docs/query.md

    微信api对应:查询订单状态

    5. 业务逻辑与实现

    pc端用户点击支付后弹出二维码并显示倒计时90s,用户支付成功后,在这里有两种更新订单状态的方式,第一种常见的方式是,在前台或后台有个定时器在90s期间轮询到微信端查询接口查看订单状态,一旦发现成功就自动刷新整个页面给出支付成功提示。这样的方式用户体验度很好,不过当出现类似秒杀这种瞬间大批量用户同时支付时候,会给后台很大请求压力。

    另一种方式是二维码弹出框下有支付成功和支付取消两个按钮,用户支付后自己点击支付成功,我后台去更新订单状态。这样即使在秒杀活动中也能分担掉大批量请求同时涌入的情况,不过缺点就是牺牲了一些用户体验度,另外我们更新订单状态不能只依赖用户自己点击这一种情况,如果用户支付后直接关掉页面或者刷新页面订单状态就迟迟未更新。所以这时我们还需要在后台有一个1-2m的定时任务,去扫描订单表中超时的订单做状态更新。

    我用的是第二种方式,这里面除了需要后台有额外一个定时任务,还需要在状态更新方法里考虑多种情况:用户未支付但点击了支付成功,用户支付成功后但点击了支付取消,支付时间过期后点击了支付成功等等情况。

  • 相关阅读:
    业务安全相关安全产品的反思
    Asp.Net Core&Jaeger实现链路追踪
    第五章《类的继承》第4节:属性的屏蔽
    【漏洞复现】易思智能物流无人值守系统文件上传
    can中继 智能CAN总线隔离中继器集线器CANBridge-300/400
    Spring+spring mvc+mybatis整合的框架
    ssh常用操作
    【人脸生成】HiSD-通过层级风格解耦实现图到图的迁移
    (Java)数据类型与变量
    人和AI不可能把所有的情况都考虑到
  • 原文地址:https://blog.csdn.net/u012491783/article/details/126700661