聚合支付,实现微信和支付宝扫一码支付
1 目标
我们要实现的目标是:用微信客户端扫二维码的时候,用微信支付,用支付宝扫二维码的时候用支付宝支付。这就要求我们对微信和支付宝做一个聚合,在平台内做一个客户端判断。
2 支付选型
支付宝我们选用的是手机网页支付
微信用的是native支付
3 准备
新建一个工程,结构如下所示
主要用到的技术有springboot+springcloud+nacos+zxing二维码生成+alipay SDK
父工程依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
common工程依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
<!-- 二维码生成&识别组件 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
</dependencies>
支付宝代理工程依赖
<dependencies>
<dependency>
<groupId>cn.lx.pay</groupId>
<artifactId>juhe-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.7.73.ALL</version>
</dependency>
<!-- 支付宝SDK依赖的日志 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
接下来就是支付宝代理工程的配置文件了
server:
port: 56010
spring:
application:
name: juhe-zhifubao
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: DEFAULT
namespace: b36ded08-caa6-408a-9eee-cc50698693be
这里使用的是nacos作为注册中心,所以别忘了在启动类上加入注解@EnableDiscoveryClient
4 二维码生成技术
二维码说穿了也没有什么,实际上就是将一个网址转化为了一个唯一的图片,使用扫码工具扫描解析这个图片得到网址,然后访问网址
我们这里用的二维码生成技术是谷歌google 公司的 zxing,优快云有很多关于这个的教程,想要了解更多可以自己去找一下。
下面是生成二维码的工具类
public class QRCodeUtil {
/**
* 生成二维码
* @param content 二维码对应的URL
* @param width 二维码图片宽度
* @param height 二维码图片高度
* @return
*/
public static String createQRCode(String content, int width, int height) throws IOException {
String resultImage = "";
//除了尺寸,传入内容不能为空
if (!StringUtils.isEmpty(content)) {
ServletOutputStream stream = null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
//二维码参数
@SuppressWarnings("rawtypes")
HashMap<EncodeHintType, Comparable> hints = new HashMap<>();
//指定字符编码为“utf-8”
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//L M Q H四个纠错等级从低到高,指定二维码的纠错等级为M
//纠错级别越高,可以修正的错误就越多,需要的纠错码的数量也变多,相应的二维吗可储存的数据就会减少
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
//设置图片的边距
hints.put(EncodeHintType.MARGIN, 1);
try {
//zxing生成二维码核心类
QRCodeWriter writer = new QRCodeWriter();
//把输入文本按照指定规则转成二维吗
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
//生成二维码图片流
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
//输出流
ImageIO.write(bufferedImage, "png", os);
/**
* 原生转码前面没有 data:image/png;base64 这些字段,返回给前端是无法被解析,所以加上前缀
*/
resultImage = new String("data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray()));
return resultImage;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stream != null) {
stream.flush();
stream.close();
}
}
}
return null;
}
public static void main(String[] args) throws IOException {
/**
* http://33833g4w32.wicp.vip:49739/create/ali/pay
* 这个就是去支付宝下面的访问地址
* 手机访问了这个链接之后,他会调起手机端的支付宝,然后进行支付
* 我这里使用了内网穿透工具
* 如果你的手机和你的电脑不在同一个局域网下,那么使用你手机访问电脑的ip+项目端口是没有用的
*/
String qrCode = createQRCode("http://33833g4w32.wicp.vip:49739/create/ali/pay", 200, 200);
System.out.println(qrCode);
}
}
5 对接支付宝手机网页支付
一开始我们就已经说了,我们这里使用支付宝的手机网站支付。接下来就去支付宝开放平台,申请沙箱账号,找手机网站支付的开发文档
地址:https://opendocs.alipay.com/open/204/105695
支付宝已经详细解释了每个参数的意义,并且那些是必须参数
下面提供我自己的代码
@Controller
@Slf4j
public class PayController {
@GetMapping("/create/ali/pay")
public void aliPay(HttpServletRequest request, HttpServletResponse response){
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipaydev.com/gateway.do",
"2016101700706706",
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHXQPTEYQ4q1M7FWY0rCLSGsmKm51hbbxpFLd+RFrtUlazEc+IZEo6PQcUov7OWU2mQZMZ3HTROUhcRVxQ/TOgbk9a09ZQ2lSzWnJDGnct4yBPkbIh9K+sJr6CZ0gIbyThr2Ak/RIlr7XpG9tTK793/ejutFyzy/0xST8Q4/XrG2o9pbfBajG4FZog0t+0/FYho2t1q6AmSJnwaK4Yn3CZER1PpGDDu6xPU4Zh/5ilL6dUFzzs0xmQnCYMUsjjkd0rlOevzOo3Zy2DMniN+Hf3/FT4KLGs/XtJVVDt1ox73yVm98zozP329YngiXymaZekR4nT9fXlC7eM4Y8N4DOvAgMBAAECggEAHe0hgFN6EPFHqGNVwkVgOWU0s5Et3TFemzi6TI8eLyOqCVLht/y8MF33p8dVYBd8REpxFCGaLftlFQk8nKct98ULhEAbPKrYWQKhClbajGmPZigG4tzuzbePHNNqqHqyA7c7IVJV5cEQDaZb+epNHWEkU0nKyPFLW88ew0QyxTRo4mbfpY+eVrb3G/fBiYXbRem6TcCsbOQ77eTcMPpnOdP0QpwBCZ0lcbMOawpi9LE6RUv0mjRJYi07kUo5zeodenA3GC9RMf9YCCGClvuZVzR4xiOIO6vE0qT9pYa7uOce0dJhUT0bnKXBnZq4sciVVDRkaY5VTvUHdVB0utdScQKBgQDqwVvup7Lw9zVuwteWvlSUx95XbMKFVhX70GTW+qX+1/+duJ46kxqgvm/RwYLCPrNAqyGwQ9FVG9uNg8Vi1KBbQ1eOzbMo6+uiDtUogE8FVZwZWzSw9YKyIvahrWrLoRi5IiAbkN9UzYzuHti3Dy4/N+01lcIJ6RiHnDB2N8zT1wKBgQCTnQPDnZJc27V3slDqaf0iKS5llZ+eNFlnQwmv5Uz+1Wtb9cso+H4mElIUP13vauriHmXw6Ksy29rxtxPNRpVJyDG6wpKVb5ZvkK1634+uaRkC3lbRCxZZTO0NhaPCIhaXFLW2r6U0YHUYb/enI4z9aom0jihZ7juxF2e3+Msj6QKBgB5+aXOxwvO8GOu/UYPaS2BcKgyPKyFo0kg4hLDMND3LTv/s2FjhfOb+dcX4bgTPYjd3Q1QDKzD0Amv6fuxclEvmjnwVSj15j80oQhYVvK4DtdgxWcHW0lhTZFgSD7pNvclmnmcWRXxdiv3vcdUtmqNJn32Da4YgCjirWDwy+V9XAoGAfi11Dj0e4ykbURmnePjoW87/ze275yuwUEhJe4Vx71LW1mCgLIFcs4ZtiskvrnuiE28QjIEV9f9gg8WOs6Vl7w+lEpNHYV1lJjBxWdrHorpLmtwbMc1caTEMYMafWE5zKOmW+nXhrYfWD/GFq+UDm4r58tChRV4SwCnVirisTCECgYEAgpzraHQkr6wodgki6C/8p2WHkR5IRHwDel9dEGXWhlcrQXmNaSoQnZyQmVhnqrMSX9l76j+1RwKx0MN4IqlwxNx9u8LK+kQOlfPHjzTdvDQqPmbiul1uqMErMn1ifbx80WTTTxDd1gpuN2wmfAKHeJ8kR1o/wzypGyPPv1sHDKg=",
"json",
"utf-8",
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh10D0xGEOKtTOxVmNKwi0hrJipudYW28aRS3fkRa7VJWsxHPiGRKOj0HFKL+zllNpkGTGdx00TlIXEVcUP0zoG5PWtPWUNpUs1pyQxp3LeMgT5GyIfSvrCa+gmdICG8k4a9gJP0SJa+16RvbUyu/d/3o7rRcs8v9MUk/EOP16xtqPaW3wWoxuBWaINLftPxWIaNrdaugJkiZ8GiuGJ9wmREdT6Rgw7usT1OGYf+YpS+nVBc87NMZkJwmDFLI45HdK5Tnr8zqN2ctgzJ4jfh39/xU+CixrP17SVVQ7daMe98lZvfM6Mz99vWJ4Il8pmmXpEeJ0/X15Qu3jOGPDeAzrwIDAQAB",
"RSA2");
AlipayTradeWapPayRequest payRequest = new AlipayTradeWapPayRequest();
AlipayTradeWapPayModel alipayTradeWapPayModel = new AlipayTradeWapPayModel();
//封装订单相关参数
//下面4个是关键入参
//订单号不能重复
alipayTradeWapPayModel.setOutTradeNo(UUID.randomUUID().toString());
alipayTradeWapPayModel.setSubject("Iphone6 16G");
alipayTradeWapPayModel.setTotalAmount("23");
alipayTradeWapPayModel.setProductCode("FLASH-PAY");
alipayTradeWapPayModel.setTimeoutExpress("30m");
//支付宝订单相关参数
payRequest.setBizModel(alipayTradeWapPayModel);
//设置同步地址
payRequest.setReturnUrl("http://192.168.43.210:8081/ali/callback");
//设置异步地址
payRequest.setNotifyUrl("http://192.168.43.210:8081/ali/callback");
AlipayTradeWapPayResponse payResponse = null;
try {
payResponse = alipayClient.pageExecute(payRequest);
} catch (AlipayApiException e) {
throw new RuntimeException("支付异常");
}
if(payResponse.isSuccess()){
log.info("调用成功:{}",payResponse.getBody());
response.setContentType("text/html;charset=UTF-8");
try {
response.getWriter().write(payResponse.getBody());
response.getWriter().flush();
response.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("返回响应异常");
}
} else {
log.info("调用失败");
}
}
}
扫码之后的效果
6 优化支付宝支付
在上一步中,我们的支付宝的订单金额是固定的,不符合我们的日常使用,我们这一步需要实现扫码后输入金额,用户确认后,才调起客户端进行真正的支付
那我们就需要一个输入金额的页面了,我这里选择使用freemarker
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
页面pay.ftl,放在resource下的templates文件夹下
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="renderer" content="webkit">
<meta http-equiv="Expires" content="0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>向商家付款</title>
</head>
<body>
<div class="content">
<!-- <img src="./logo.png" alt="">-->
<p class="name">正兴鸡排</p>
</div>
<div class="form-container">
<!--提交的地址是上面我们写的api,http://33833g4w32.wicp.vip:49739/create/ali/pay-->
<form id="form" action="${rc.contextPath}/create/ali/pay" method="post">
<p><span>商品名称 :</span><input type="text" name="subject" value="正兴鸡排"
readonly="readonly" maxlength="5" size="25"/></p>
<p><span>订单描述 :</span><input type="text" name="body" value="向lx付款" readonly="readonly"
maxlength="5" size="25"/></p>
<p id="text" style="width: auto"><span>付款金额(元) :</span><input id="inp" type="text" name="totalAmount"
maxlength="5" size="20"/></p>
<input type="submit" value="立即支付" class="pay">
</form>
</div>
</body>
<script type="text/javascript" src="./keybord.js"></script>
<script type="text/javascript">
(function () {
var text = document.getElementById('text');
var input = document.getElementById('inp');
text.onclick = function () {
new KeyBoard(input);
};
})();
</script>
<style>
.form-container {
padding: 0 10px;;
}
input {
margin-top: 10px;
border: 0;
}
img {
width: 100px;
height: 50px;;
margin-left: 25%;
}
.content .name {
margin-top: 30px;
}
.pay {
width: 100%;
height: 50px;;
font-size: 20px;;
text-align: center;
background: rgb(26, 132, 231);;
border-radius: 5px;
border: 1px solid #ccc;
color: white;
margin: 0 auto;
}
.content {
width: 100%;
height: 150px;
background: rgb(26, 132, 231);
text-align: center;
overflow: hidden;
}
.content p {
margin: 0;
padding: 0;
}
.price input {
text-align: center;
background: rgb(26, 132, 231);
color: white;
outline: none;;
font-size: 20px;;
}
#form p {
width: 100%;
height: 40px;;
border-bottom: 1px solid #ccc;
font-size: 14px;;
display: flex;
justify-content: space-between;
}
#form p span {
float: left;
display: block;;
font-size: 13px;
line-height: 40px;
flex-shrink: 0;
}
#form p input {
flex-shrink: 0;
height: 20px;
float: right;;
line-height: 30px;
text-align: right;
outline: none;
}
#text {
border: 2px solid rgb(196, 184, 184);
padding: 0 10px;
border-radius: 5px;
height: 50px;;
line-height: 50px;
font-size: 20px;
}
#inp {
border: 0;
outline: none;;
width: 62%;
font-size: 20px;;
}
</style>
</html>
为该页面添加一个控制器
@Component
public class MvcConfig implements WebMvcConfigurer {
/**
* Configure simple automated controllers pre-configured with the response
* status code and/or a view to render the response body. This is useful in
* cases where there is no need for custom controller logic -- e.g. render a
* home page, perform simple site URL redirects, return a 404 status with
* HTML content, a 204 with no content, and more.
*
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ali/pay/page").setViewName("pay");
}
}
编写一个vo,用来接收表单提交的参数
@Data
public class PayParamVO {
private String subject;
private String body;
private String totalAmount;
}
接着我们将请求支付的代码修改一下
@Controller
@Slf4j
public class PayController {
@PostMapping("/create/ali/pay")
public void aliPay(PayParamVO payParamVO,HttpServletRequest request, HttpServletResponse response){
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipaydev.com/gateway.do",
"2016101700706706",
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHXQPTEYQ4q1M7FWY0rCLSGsmKm51hbbxpFLd+RFrtUlazEc+IZEo6PQcUov7OWU2mQZMZ3HTROUhcRVxQ/TOgbk9a09ZQ2lSzWnJDGnct4yBPkbIh9K+sJr6CZ0gIbyThr2Ak/RIlr7XpG9tTK793/ejutFyzy/0xST8Q4/XrG2o9pbfBajG4FZog0t+0/FYho2t1q6AmSJnwaK4Yn3CZER1PpGDDu6xPU4Zh/5ilL6dUFzzs0xmQnCYMUsjjkd0rlOevzOo3Zy2DMniN+Hf3/FT4KLGs/XtJVVDt1ox73yVm98zozP329YngiXymaZekR4nT9fXlC7eM4Y8N4DOvAgMBAAECggEAHe0hgFN6EPFHqGNVwkVgOWU0s5Et3TFemzi6TI8eLyOqCVLht/y8MF33p8dVYBd8REpxFCGaLftlFQk8nKct98ULhEAbPKrYWQKhClbajGmPZigG4tzuzbePHNNqqHqyA7c7IVJV5cEQDaZb+epNHWEkU0nKyPFLW88ew0QyxTRo4mbfpY+eVrb3G/fBiYXbRem6TcCsbOQ77eTcMPpnOdP0QpwBCZ0lcbMOawpi9LE6RUv0mjRJYi07kUo5zeodenA3GC9RMf9YCCGClvuZVzR4xiOIO6vE0qT9pYa7uOce0dJhUT0bnKXBnZq4sciVVDRkaY5VTvUHdVB0utdScQKBgQDqwVvup7Lw9zVuwteWvlSUx95XbMKFVhX70GTW+qX+1/+duJ46kxqgvm/RwYLCPrNAqyGwQ9FVG9uNg8Vi1KBbQ1eOzbMo6+uiDtUogE8FVZwZWzSw9YKyIvahrWrLoRi5IiAbkN9UzYzuHti3Dy4/N+01lcIJ6RiHnDB2N8zT1wKBgQCTnQPDnZJc27V3slDqaf0iKS5llZ+eNFlnQwmv5Uz+1Wtb9cso+H4mElIUP13vauriHmXw6Ksy29rxtxPNRpVJyDG6wpKVb5ZvkK1634+uaRkC3lbRCxZZTO0NhaPCIhaXFLW2r6U0YHUYb/enI4z9aom0jihZ7juxF2e3+Msj6QKBgB5+aXOxwvO8GOu/UYPaS2BcKgyPKyFo0kg4hLDMND3LTv/s2FjhfOb+dcX4bgTPYjd3Q1QDKzD0Amv6fuxclEvmjnwVSj15j80oQhYVvK4DtdgxWcHW0lhTZFgSD7pNvclmnmcWRXxdiv3vcdUtmqNJn32Da4YgCjirWDwy+V9XAoGAfi11Dj0e4ykbURmnePjoW87/ze275yuwUEhJe4Vx71LW1mCgLIFcs4ZtiskvrnuiE28QjIEV9f9gg8WOs6Vl7w+lEpNHYV1lJjBxWdrHorpLmtwbMc1caTEMYMafWE5zKOmW+nXhrYfWD/GFq+UDm4r58tChRV4SwCnVirisTCECgYEAgpzraHQkr6wodgki6C/8p2WHkR5IRHwDel9dEGXWhlcrQXmNaSoQnZyQmVhnqrMSX9l76j+1RwKx0MN4IqlwxNx9u8LK+kQOlfPHjzTdvDQqPmbiul1uqMErMn1ifbx80WTTTxDd1gpuN2wmfAKHeJ8kR1o/wzypGyPPv1sHDKg=",
"json",
"utf-8",
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh10D0xGEOKtTOxVmNKwi0hrJipudYW28aRS3fkRa7VJWsxHPiGRKOj0HFKL+zllNpkGTGdx00TlIXEVcUP0zoG5PWtPWUNpUs1pyQxp3LeMgT5GyIfSvrCa+gmdICG8k4a9gJP0SJa+16RvbUyu/d/3o7rRcs8v9MUk/EOP16xtqPaW3wWoxuBWaINLftPxWIaNrdaugJkiZ8GiuGJ9wmREdT6Rgw7usT1OGYf+YpS+nVBc87NMZkJwmDFLI45HdK5Tnr8zqN2ctgzJ4jfh39/xU+CixrP17SVVQ7daMe98lZvfM6Mz99vWJ4Il8pmmXpEeJ0/X15Qu3jOGPDeAzrwIDAQAB",
"RSA2");
AlipayTradeWapPayRequest payRequest = new AlipayTradeWapPayRequest();
AlipayTradeWapPayModel alipayTradeWapPayModel = new AlipayTradeWapPayModel();
//封装订单相关参数
//下面4个是关键入参
alipayTradeWapPayModel.setOutTradeNo(UUID.randomUUID().toString());
//现在就是动态的了
//alipayTradeWapPayModel.setSubject("Iphone6 16G");
alipayTradeWapPayModel.setSubject(payParamVO.getSubject());
//alipayTradeWapPayModel.setTotalAmount("23");
alipayTradeWapPayModel.setTotalAmount(payParamVO.getTotalAmount());
alipayTradeWapPayModel.setBody(payParamVO.getBody());
alipayTradeWapPayModel.setProductCode("FLASH-PAY");
alipayTradeWapPayModel.setTimeoutExpress("30m");
//支付宝订单相关参数
payRequest.setBizModel(alipayTradeWapPayModel);
//设置同步地址
payRequest.setReturnUrl("http://192.168.43.210:8081/ali/callback");
//设置异步地址
payRequest.setNotifyUrl("http://192.168.43.210:8081/ali/callback");
AlipayTradeWapPayResponse payResponse = null;
try {
payResponse = alipayClient.pageExecute(payRequest);
} catch (AlipayApiException e) {
throw new RuntimeException("生成支付异常");
}
if(payResponse.isSuccess()){
log.info("调用成功:{}",payResponse.getBody());
response.setContentType("text/html;charset=UTF-8");
try {
response.getWriter().write(payResponse.getBody());
response.getWriter().flush();
response.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("返回响应异常");
}
} else {
log.info("调用失败");
}
}
}
效果如下
7 支付客户端识别
微信支付和支付宝差不多,但是微信没有沙箱环境,申请开通支付需要很多证明文件,比如营业执照之类的,这些对于个人兴趣爱好者来说,实在是没有办法。所以这里不讲如何对接微信,我们只将客户端识别功能实现。
我们去浏览器开发者模式,随便找一个请求,看一下这个请求的请求头,我们会发现有下面一项
这是火狐浏览器的:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
这是Chrome浏览器的:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3970.5 Safari/537.36
再看postman的:·PostmanRuntime/7.26.5
与之对应的,支付宝的标识:Mozilla/5.0 (Linux; Android 10; JSN-AL00 Build/HONORJSN-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.99 Mobile Safari/537.36 NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:4G,ws:424|0|2.55) AliApp(AP/10.1.52.1226) AlipayClient/10.1.52.1226 Language/zh-Hans useStatusBar/true isConcaveScreen/true
微信的标识:Mozilla/5.0 (Linux; Android 10; JSN-AL00 Build/HONORJSN-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2691 MMWEBSDK/200901 Mobile Safari/537.36 MMWEBID/4279 MicroMessenger/7.0.19.1760(0x27001351) Process/toolsmp WeChat/arm64 NetType/4G Language/zh_CN ABI/arm64
我们提取这些标识特有的单词进行判断
public enum BrowserType {
ALIPAY,//支付宝
WECHAT,//微信
PC_BROWSER,//pc端浏览器
MOBILE_BROWSER; //手机端浏览器
/**
* 根据UA获取浏览器类型
* @param userAgent userAgent
* @return 浏览器类型
*/
public static BrowserType valueOfUserAgent(String userAgent) {
if (userAgent != null && userAgent.contains("AlipayClient")) {
return BrowserType.ALIPAY;
}else if (userAgent != null && userAgent.contains("MicroMessenger")) {
return BrowserType.WECHAT;
}else{
return BrowserType.MOBILE_BROWSER;
}
}
}
这个我以前用的工具类,获取了浏览器类型之后,调起对应的支付接口。
今天我们不这么用,我们使用gateway网关,由网关的请求头断言,决定将请求转发给那个微服务。
7.1 创建一个gateway网关
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
application.xml配置文件
server:
#我这里将端口修改为了56010,是因为我配置了内网穿透的端口就是56010,改内网穿透的端口麻烦了一点,所以我这里就直接改程序了
port: 56010
spring:
application:
name: juhe-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: DEFAULT
namespace: b36ded08-caa6-408a-9eee-cc50698693be
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
routes:
- id: zhifubao_route
uri: lb://juhe-zhifubao
#满足了下面两个条件,请求才会被转发到zhifubao
predicates:
#匹配请求头中user-agent的值中包含AlipayClient的请求
- Header=User-Agent, .+AlipayClient.+
#匹配请求路径为/pay/page,/create/ali/pay的请求
- Path=/pay/page,/create/ali/pay
- id: wx_route
uri: lb://juhe-wx
predicates:
- Header=User-Agent, .+MicroMessenger.+
- Path=/pay/page,/create/wx/pay
网关的重要配置就完成了
7.2 创建一个微信支付的工程
这个工程主要负责微信扫码之后的一系列处理
工程名juhe-wx
<dependencies>
<dependency>
<groupId>cn.lx.pay</groupId>
<artifactId>juhe-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
application.yml配置文件
server:
port: 57020
spring:
application:
name: juhe-wx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: DEFAULT
namespace: b36ded08-caa6-408a-9eee-cc50698693be
freemarker:
request-context-attribute: rc
页面就直接使用上面支付宝的,但是你要修改一个东西,表单提交的路径,现在你要提交的微信的
<form id="form" action="${rc.contextPath}/create/wx/pay" method="post">
还有mvc的配置类,我们将支付宝的配置类也修改成这样,扫码的时候回根据客户端类型转发到对应的服务
@Component
public class MvcConfig implements WebMvcConfigurer {
/**
* Configure simple automated controllers pre-configured with the response
* status code and/or a view to render the response body. This is useful in
* cases where there is no need for custom controller logic -- e.g. render a
* home page, perform simple site URL redirects, return a 404 status with
* HTML content, a 204 with no content, and more.
*
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/pay/page").setViewName("pay");
}
}
最后一步,我们给微信服务写一个测试的controller
@Controller
@Slf4j
public class PayController {
@PostMapping("/create/wx/pay")
public void aliPay( HttpServletRequest request, HttpServletResponse response){
log.info("微信支付创建成功");
}
}
控制台打印这句,说明调用的是微信服务。