当前位置: 首页 > news >正文

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调用,如下:

var WxPay = require('wechatpay-node-v3'); 

const pay = new WxPay({
	  appid: config.appid,
	  mchid: config.mchid,
	  publicKey: fs.readFileSync(__dirname + "/wxkey/" + 'apiclient_cert.pem'), // 公钥
	  privateKey: fs.readFileSync(__dirname + "/wxkey/" + 'apiclient_key.pem'), // 秘钥
	  serial_no: config.serial_no
	});


var orderNo = "native121775ee0120140703355773bb";

function testPay() {
	try {
		const params = 
		{
				"mchid": config.mchid,  // 商户号
				"out_trade_no": orderNo,  // 系统订单号
				"appid": config.appid,  // appId
				"time_expire": "2022-05-23T14:57:00+08:00",  // 过期时间
				"description": "Image形象店-深圳腾大-QQ公仔",  // 
				"notify_url": "https://weixin.qq.com/",  // 回调地址,如果不参与回调,可以随便填
				"amount": {
					"total": 1, // 金额,单位分
					"currency": "CNY"
				},
		}
	    const nonce_str = Math.random().toString(36).substr(2, 15),  //随机字符串
	    timestamp = parseInt(+new Date() / 1000 + '').toString(),  //时间戳 秒
	    url = '/v3/pay/transactions/native';

	    // 获取签名
	    const signature = pay.getSignature('POST', nonce_str, timestamp, url, params);  //如果是get 请求 则不需要params 参数拼接在url上 例如 /v3/pay/transactions/id/12177525012014?mchid=1230000109
	    // 获取头部authorization 参数
	    const authorization = pay.getAuthorization(nonce_str, timestamp, signature);

	    const result = await request
	      .post('https:api.mch.weixin.qq.com/v3/pay/transactions/native')
	      .send(params)
	      .set({
	        Accept: 'application/json',
	        'Content-Type': 'application/json',
	        'User-Agent':
	          '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',
	        Authorization: authorization,
	        "Wechatpay-Serial" : config.serial_no
	      });

		const result = await pay.transactions_native(params);
		
	  } catch (error) {
	    console.log(error);
	  }
}

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

function testPay() {
	return new Promise ((resolve) => {
		pay.transactions_native(params).then((result) => {
			console.log('result==========>', result);
			
			if (result.status == 200) {
				resolve(result.code_url);
			} else {
				resolve(result.message);
			}
			
		})
	})
}

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

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的定时任务,去扫描订单表中超时的订单做状态更新。

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

相关文章:

  • 数据库MySQL----(二)语句
  • 2021年中国研究生数学建模竞赛D题——抗乳腺癌候选药物的优化建模
  • 【AI】PyTorch入门(三):数据集和数据加载器
  • Windows系统下MySQL8.0版详细安装及配置教程
  • Qt5开发从入门到精通——第五篇四节( 文本编辑器 Easy Word 开发 V1.3详解 )
  • c#-WPF使用类和子类绑定到DataContext
  • 图像隐写,如何在图像中隐藏二维码
  • SPL工业智能:发现时序数据的异常
  • 【Linux】进程概念(万字详解)—— 冯诺依曼体系结构 | 操作系统 | 进程
  • 网络编程套接字-----实现网络间通信
  • 机器学习:详细推导支持向量机SVM原理+Python实现
  • mysql socket文件丢失处理或者mysql.sock被删除
  • 欧拉计划详解第506题:钟摆序列
  • 《Python3 网络爬虫开发实战》:二、HTML消息结构
  • 调试接口小技巧-通过接口调试工具去下载上传文件
  • 自己简单写的 事件订阅机制
  • “大数据应用场景”之隔壁老王(连载四)
  • Java比较器对数组,集合排序
  • nodejs:开发并发布一个nodejs包
  • PHP面试之三:MySQL数据库
  • Twitter赢在开放,三年创造奇迹
  • Vue 动态创建 component
  • Web标准制定过程
  • 阿里研究院入选中国企业智库系统影响力榜
  • 入门到放弃node系列之Hello Word篇
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • (13)Hive调优——动态分区导致的小文件问题
  • (初研) Sentence-embedding fine-tune notebook
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (附源码)计算机毕业设计大学生兼职系统
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)甲方乙方——赵民谈找工作
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • .describe() python_Python-Win32com-Excel
  • .Net - 类的介绍
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .考试倒计时43天!来提分啦!
  • @EnableWebMvc介绍和使用详细demo
  • @SentinelResource详解
  • @在php中起什么作用?
  • [<MySQL优化总结>]
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)
  • [51nod1610]路径计数
  • [ai笔记4] 将AI工具场景化,应用于生活和工作
  • [AutoSar]BSW_Memory_Stack_003 NVM与APP的显式和隐式同步
  • [AX]AX2012 SSRS报表Drill through action
  • [ccc3.0][数字钥匙] UWB配置和使用(二)
  • [Codeforces] number theory (R1600) Part.11
  • [DevEpxress]GridControl 显示Gif动画
  • [GXYCTF2019]BabyUpload1 -- 题目分析与详解