微信支付开发(6)--付款码支付(被扫)开发详解

点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载

1. 场景

用户打开付款码,商户使用扫码枪等设备扫码用户的付款码完成支付,注意此时用户的二维码是被商户扫描的。

2. 开发说明

付款码支付可以在PC机、自助机的网页使用,只需要插入USB扫码器即可。

扫码器的原理需要简单说明下,扫码器扫码相当于键盘输入,扫码器会将二维码的识别结果(一般是数字或者英文字母或者网址)作为键盘输入内容通过USB输入我们的计算机。此处需要注意的是,大多数品牌的扫码器会在识别结果后面附带回车键,少部分品牌的扫码器会不附加任何内容(不附加的情况下,一般会通过指定长度的二维码内容确定输入结束)。

在网页点击开始支付后,需要展示倒计时信息。如果倒计时结束用户还没出示付款码,此时应结束本次支付行为。如果在倒计时期间,用户出示付款码且扫码成功,则发起付款码支付,并向用户提示付款结果。如果后台返回支付结果为USERPAYING,表示用户尚未完成支付(一般是扫码了,弹出输入密码框,但未完成输入密码),此时还需要通过定时器调用后端接口查询支付结果。

3. 普通商户付款码支付

3.1 项目构建

同样构建springboot项目,并配置pom.xml引入微信公众号、微信支付相关的依赖。

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.5.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.pandabrother</groupId>
	<artifactId>wx-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.0.0</maven-jar-plugin.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!-- 添加swagger2相关功能 -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<!-- 添加swagger-ui相关功能 -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>
		<!-- 微信公众号 -->
		<dependency>
			<groupId>com.github.binarywang</groupId>
			<artifactId>weixin-java-mp</artifactId>
			<version>4.1.0</version>
		</dependency>
		<!-- 微信支付 -->
		<dependency>
			<groupId>com.github.binarywang</groupId>
			<artifactId>weixin-java-pay</artifactId>
			<version>4.1.0</version>
		</dependency>

	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

3.2 编写配置类

编写微信支付配置类:

/**
 * 微信支付配置
 */
@Configuration
public class PayConfig {
	public static boolean isServiceMode = false;

	@Bean
	public WxPayService wxService() {
		WxPayConfig payConfig = new WxPayConfig();
		// ----------------------------------------------------------------------------------普通商户
		if (isServiceMode == false) {
			payConfig.setAppId("");// 微信公众号或者小程序等的appid
			payConfig.setMchId("");// 微信支付商户号
			payConfig.setMchKey("");// 微信支付商户密钥
			payConfig.setKeyPath("classpath:xxx.p12");// p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
		}
		// ----------------------------------------------------------------------------------服务商+特约商户
		else if (isServiceMode == true) {
			payConfig.setAppId("");// 微信公众号或者小程序等的appid
			payConfig.setMchId("");// 服务商商户号
			payConfig.setMchKey("");// 服务商商户密钥
			payConfig.setSubAppId("");// 服务商模式下的子商户公众账号ID,注意此处如果子商户使用了服务商的appid,则无需填写
			payConfig.setSubMchId("");// 服务商模式下的子商户号
			payConfig.setKeyPath("classpath:xxx.p12");// p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
		}
		// 可以指定是否使用沙箱环境
		payConfig.setUseSandboxEnv(false);
		WxPayService wxPayService = new WxPayServiceImpl();
		wxPayService.setConfig(payConfig);
		return wxPayService;
	}
}

3.3 编写网页

通过定时器完成倒计时,通过监听扫码器来实现扫码,扫码后发起付款码支付。支付结果如果不确定还需要继续通过定时器查询订单结果。

<html>

<head>
<meta charset="utf-8">
</head>

<body>
	<input id="btn-pay" type="button" value="开始付款码支付" onclick="btnPay()"> |
	<div id="info-box">信息提示:</div>
	<script src="https://cdn.staticfile.org/jquery/1.9.1/jquery.min.js"></script>
	<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
	<script>
		// 支付倒计时
		var curOrderId = ""; // 订单编号
		var timer = null; //定时器
		var seconds = 0; //倒计时剩余时间
		function btnPay() {
			if (timer == null) {
				$("#btn-pay").blur();
				seconds = 90;//倒计时90秒
				timer = setInterval("clock()", 1000); // 开始计时
				window.addEventListener('keypress', listenKeyboard); // 开始监听键盘
			} else {
				alert("请等待上次支付完成!");
			}
		}

		function clock() {
			var str = "请将付款码对准扫码器完成支付,剩余时间:" + seconds + "秒";
			$("#info-box").text(str);
			if (seconds <= 0) {
				stopPay(); // 停止支付
				alert("支付超时!");
			} else if (curOrderId != "") { //如果存在当前订单,且未结束支付,需要主动向后台查询订单状态
				var param = {
					outTradeNo : curOrderId,
				};
				$.ajax({
					type : "POST",
					url : "/wx-server/microPayQuery",
					data : JSON.stringify(param),
					contentType : "application/json",
					dataType : "json",
					success : function(re) {
						console.log("query re:", re);
						if (re.returnCode == "SUCCESS" && re.resultCode == "SUCCESS" && re.tradeState == "SUCCESS") {
							stopPay();
							alert("查询支付成功!");
						}
					}
				});
			}
			seconds--;
		}

		function stopPay() {
			clearInterval(timer);
			timer = null;
			window.removeEventListener('keypress', listenKeyboard); // 停止监听键盘
			barCode = "";
			lastTime = 0;
			curOrderId = "";
		}
		// 扫码器监听(实际上就是键盘监听)
		var barCode = "";
		var lastTime = 0;

		function listenKeyboard(e) {
			console.log("按键按下");
			e = e || window.event;
			var curCode = e.keyCode || e.which || e.charCode;
			var curTime = new Date().getTime();
			if (lastTime == 0) { // 第一次按键
				barCode = String.fromCharCode(curCode);
			} else {
				if (curTime - lastTime <= 100) {
					if (curCode != 13) { // 回车不算输入内容
						barCode += String.fromCharCode(curCode);
					}
				} else if (curTime - lastTime > 500) { // 输入间隔500毫秒清空
					barCode = "";
					lastTime = 0;
				}
			}
			lastTime = curTime;
			if (curCode == 13) { // 回车
				if (barCode != "" && barCode.length >= 8) {
					console.log("扫码结果:" + barCode);
					realPay();
				}
			}
		}

		function realPay() {
			curOrderId = '20000006'; // 设置订单号
			var param = {
				body : '电费',
				outTradeNo : curOrderId,
				spbillCreateIp : '123.135.154.64',
				feeType : 'CNY',
				totalFee : 1, //单位分
				authCode : barCode,
			};
			$.ajax({
				type : "POST",
				url : "/wx-server/microPay",
				data : JSON.stringify(param),
				contentType : "application/json",
				dataType : "json",
				success : function(re) {
					console.log("microyPay re:", re);
					if (re.returnCode == "SUCCESS" && re.resultCode == "SUCCESS" && re.tradeType == "MICROPAY") {
						stopPay();
						alert("直接支付成功!");
					}
				}
			});
		}
	</script>
</body>

</html>

3.4 编写后端方法

后端有两个方法,一个是生成订单,一个是查询订单。

/**
 * 付款码支付控制器
 */
@Controller
@Api(tags = "付款码支付API")
public class MicroPayController {
	@Autowired
	private WxPayService wxPayService;

	/**
	 * 付款码支付
	 */
	@PostMapping("/microPay")
	@ResponseBody
	public WxPayMicropayResult microPay(@RequestBody WxPayMicropayRequest request) throws WxPayException {
		WxPayMicropayResult result = wxPayService.micropay(request);
		return result;
	}

	/**
	 * 查询订单
	 */
	@PostMapping("/microPayQuery")
	@ResponseBody
	public WxPayOrderQueryResult microPayQuery(@RequestBody WxPayOrderQueryRequest wxPayOrderQueryRequest) throws WxPayException {
		return wxPayService.queryOrder(wxPayOrderQueryRequest);
	}
}

3.5 测试

3.5.1 直接支付成功

将支付金额设为1,即1分,此时金额较小,无需用户输入密码,所以可以直接扫码完成支付。即realPay方法后显示alert("直接支付成功!");
在这里插入图片描述
此时查看后台返回结果,可见付款码支付后立即返回成功了。
在这里插入图片描述

3.5.2 查询支付成功

将支付金额改为10000,即100元,然后修改订单号发起新的支付,我们故意晚一会输入密码,这样就会通过microPayQuery查询来获取到订单结果,从而显示alert("查询支付成功!");
在这里插入图片描述
此时查看后台结果,tradeState由USERPAYING变为SUCCESS,也就是用户支付中,变为支付成功。这说明有时候直接支付未成功,还是需要借助查询功能完成支付的。

3.6 订单状态处理

最后别忘记,后端的microPay和microPayQuery方法都有可能发现支付成功,所以在这两个方法中都得处理订单状态更新。

4.服务商模式付款码支付

4.1 修改配置类

修改PayConfig的isServiceMode值为true,同时配置服务商模式下的各个参数:

else if (isServiceMode == true) {
			payConfig.setAppId("");// 微信公众号或者小程序等的appid
			payConfig.setMchId("");// 服务商商户号
			payConfig.setMchKey("");// 服务商商户密钥
			payConfig.setSubAppId("");// 服务商模式下的子商户公众账号ID,注意此处如果子商户使用了服务商的appid,则无需填写
			payConfig.setSubMchId("");// 服务商模式下的子商户号
			payConfig.setKeyPath("classpath:xxx.p12");// p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
		}

4.2 产品授权

此时发起支付,会提示【特约子商户商户号未授权服务商的产品权限】,此时需要先登录服务商平台发起授权邀请,然后登录商户平台授权。

授权邀请依次点击【服务商平台】-【产品中心】-【特约商户授权产品】-【服务商付款码支付】-【发起邀请】。

特约商户授权依次点击【商户平台】-【产品中心】-【我授权的产品】-【授权】。

4.3 支付测试

此时再次发起支付,会发现支付商户变为了特约商户,测试成功。

5. 小结

付款码支付的方式比较绕,还是需要先理顺思路,再实现代码。

另外如果支付超时,为了保证订单状态一直,还应该主动撤销订单。调用撤销订单后,如果此订单用户支付失败,微信支付系统会将此订单关闭;如果用户支付成功,微信支付系统会将此订单资金退还给用户。这样才能保证订单状态一致。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员大阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值