聚合支付,实现微信和支付宝扫一码支付

聚合支付,实现微信和支付宝扫一码支付

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,CSDN有很多关于这个的教程,想要了解更多可以自己去找一下。

下面是生成二维码的工具类

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("微信支付创建成功");
    }
}

控制台打印这句,说明调用的是微信服务。

  • 1
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
支付宝微信是中国最知名的移动支付平台,它们在用户量和影响力上都具有很大的优势。支付宝主要由蚂蚁金服运营,而微信支付则是腾讯公司旗下的支付平台。这两个平台通过技术和市场的整合,逐渐形成了移动支付市场的垄断局面。 支付宝微信支付都具备了快捷、安全、便利的特点,用户可以通过手机APP进行线上和线下的支付。无论是购买商品、转账还是缴纳各类费用,支付宝微信支付都能提供便捷的支付方式。用户只需扫描二维码或输入相关信息即可完成支付,节省了线下排队等待的时间。 而CSDN是中国领先的专业技术社区,提供了软件开发、IT技术、互联网等领域的最新资讯和技术交流平台。CSDN提供了丰富的学习资源、技术分享和在线交流社区,为广大的软件开发人员、技术爱好者和IT从业人员提供了良好的学习和交流环境。 将支付宝微信支付与CSDN聚合在一起,可以为用户提供更便捷的支付和技术交流方式。比如,在CSDN平台上购买技术书籍或参加技术培训,用户可以直接通过支付宝微信支付完成交易;同时,在技术论坛上,用户也可以使用支付宝微信支付打赏优秀的技术分享者,激励他们分享更多有价值的技术内容。 总之,支付宝微信支付作为中国领先的移动支付平台,与CSDN的聚合,将为用户提供更加便捷和全面的支付和技术交流服务,促进移动支付和技术发展的融合。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值