Springboot实现支付宝沙箱支付功能(下)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Springboot实现支付宝沙箱支付功能(下)

2.8 Controller


package com.devin.alipay_demo.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.devin.alipay_demo.entity.UserOrder;
import com.devin.alipay_demo.service.OrderService;
import com.devin.alipay_demo.util.AlipayProperties;
import com.devin.alipay_demo.util.OrderEnum;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * @author Devin Zhang
 * @className OrderController
 * @description TODO
 * @date 2020/5/12 10:53
 */
@Controller
@RequestMapping("/order")
public class OrderController {
    @Resource
    private OrderService orderService;
    @Resource
    private AlipayProperties alipayProperties;
    /**
     * 跳转到下单页面
     *
     * @return
     */
    @RequestMapping("/goPay")
    public String goPay() {
        return "pay";
    }
    /**
     * 下单,并调用支付宝
     *
     * @param orderAmount
     * @return
     * @throws AlipayApiException
     */
    @PostMapping("/pay")
    public void pay(BigDecimal orderAmount, HttpServletResponse httpResponse) throws Exception {
        String payResult = orderService.orderPay(orderAmount);
        httpResponse.setContentType("text/html;charset=" + alipayProperties.getCharset());
        httpResponse.getWriter().write(payResult);
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }
    /**
     * 支付成功的跳转页面
     *
     * @return
     */
    @RequestMapping("/goPaySuccPage")
    public String goPaySuccPage() {
        return "pay_succ";
    }
    /**
     * 支付成功的回调接口
     *
     * @return
     */
    @ResponseBody
    @RequestMapping("/notifyPayResult")
    public String notifyPayResult(HttpServletRequest request) {
        System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<进入支付宝回调->>>>>>>>>>>>>>>>>>>>>>>>>");
        // 1.从支付宝回调的request域中取值放到map中
        Map<String, String[]> requestParams = request.getParameterMap();
        Map<String, String> params = new HashMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        //2.封装必须参数
        // 商户订单号
        String outTradeNo = params.get("out_trade_no");
        //交易状态
        String tradeStatus = params.get("trade_status");
        System.out.println("outTradeNo:" + outTradeNo + " tradeStatus:" + tradeStatus);
        //3.签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
        boolean signVerified = false;
        try {
            //3.1调用SDK验证签名
            signVerified = AlipaySignature.rsaCheckV1(params, alipayProperties.getPublicKey(), alipayProperties.getCharset(), alipayProperties.getSignType());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("--------------->验签结果:" + signVerified);
        //4.对验签进行处理
        if (signVerified) {
            //验签通过
            //只处理支付成功的订单: 修改交易表状态,支付成功
            if ("TRADE_FINISHED".equals(tradeStatus) || "TRADE_SUCCESS".equals(tradeStatus)) {
                //根据订单号查找订单,防止多次回调的问题
                UserOrder orderByOrder = orderService.getOrderByOrderNo(outTradeNo);
                if (orderByOrder != null && orderByOrder.getOrderStatus() == OrderEnum.ORDER_STATUS_NOT_PAY.getStatus()) {
                    //修改订单状态
                    orderByOrder.setOrderStatus(OrderEnum.ORDER_STATUS_PAID.getStatus());
                    orderByOrder.setLastUpdateTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                    orderService.updateOrder(orderByOrder);
                }
                return "success";
            } else {
                return "failure";
            }
        } else {
            //验签不通过
            System.err.println("-------------------->验签失败");
            return "failure";
        }
    }
}

2.9 启动类


package com.devin.alipay_demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@MapperScan("com.devin.alipay_demo.dao")
@SpringBootApplication
public class AlipayDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AlipayDemoApplication.class, args);
    }
}


2.10 支付宝的配置 alipay.properties


其中私钥是我们生成的私钥,支付宝公钥可以从开发者沙盒环境拿到
 其中notifyUrl 和 returnUrl 我使用natapp ,将内网的映射到外网,以方便做测试
# 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
appId:2016102400751401
# 商户私钥,您的PKCS8格式RSA2私钥
privateKey:MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDncMvgR0vP05DtLHSIoboaSd7mCzVMzS3VSxl0RdHf/HPFWcqzZqAgtDOPGd9fWcr3M65HcGEY4F69Rn5SKuBHTuq+QJFkq7i2Px/EtbCjBWK1bMyk+fYwYwgFQ7YB4bYp2ssDAf6fDB0sDdylnAZY4Pr3OyRZFLRsbZvLAMpc5pymtTMa3knbIruxsKkj4Fc0QVAkOZCgnV2tD+KucZAoO88K1MBSnZyiO06cFX6vclpv1W7rb+pLs+2x6bxNNRMy4wf/LWrqkZlFnZrxRyF30FoJGaQY4rSJZ5FOHoputO/H0TAKT3hUjLOlauo9awo7oh/VJq55xMy8TQJU2tE/AgMBAAECggEBALRrEPPAgI/9nH/XZOzSLnKp3XI1kJJTbIRWq/whJY/XjCRvb/3UZcW41Gycva3mILv+dMDKEVdEKXahin3hYL8V/Rbc3Lq+xxqDMO+2t4EOBLnrm8iL92gW+zynYS4sV0ZfglzQ5D32QpOCJtyPOb91ew7Z8ubiphfUhluFPTuXK0MCt5n12tBI8kelGQsf3GP0xCEM1um6Ic3tDKOWwQjH0/CvssIZo5dzdc4WBv1Jgh1YconqT6oLOjfitLs6Py9K8jIrFLE1RWzt0/8L5fEyPqquqrmhML5Dce5qztdxeGDWVqVm/KFZJe8k5l8HtUlQYX7SaJ80G2UZEROQJ2kCgYEA9rszfwhF5rFQJNBKlrOl023IuSVhKmt0o8B/hR1d34y88I7cHGJ6a4+MMookpvKIEAOvdf0r/d0Z6MrQ1ptwkimm2Qa0jbFRSiG7GDJk3gVfpaNOvO3KdGWnbeJ4np51i1I80HGt9HeavyxGSeLI144A8CgK66vPkKF1FBX/vTUCgYEA8CKLzBdg26yG9SRRINA+ATaQM1myitggjk8p1dv7erFeYpKzzIWjvKZvH+kiFtAo0HLhTyF7a8V14m6znVU2SJ22GbBauXmFTjjhCbQvqUwgUjM57oH9EJMCxE9brIPV3VHdEtibQFhS36lg47NM6A5tGCEPb+Bt6dXhtLpJhyMCgYAeCQfpzO4FeUxSTvDli5UCOfkXYM+FRHN8g7CCWeLVleJiPmHZKrvQYDcm594yXI/nsysm59z1GHdQ+W+W0HFRubRP8xsDrLRCm/yUo33X8TuFhG3PXfspVD6fh9Q7KvsQLMCud0g/3FeAMjmUQQFGDElc8uLxcYbhCmagPVVWiQKBgHFNO3y+gxrjGoJL8mNzHe5gmkVASzerpiC/RVP8iXloesozwdX8MDdwp/n8e/MboEZKDfjSKXO+JVMDPIg9jnFQyHzycrwUlEtGFxgHBn3wx0dBmFHqz0aktqd9chnB0oSsfYzI2ufPRLr3JhoJnX3YYK0D3E7DK9kq62Xkh5DVAoGBAPW3Ltan5MT9Ry5D6W6ROPoPfl4Sy7C13FjFlujC8LcwsGoIt3+cKntDv7CJuiLoBDf+JtF9v8OSMlhYzy1ozLpVrO7Xn2k1fO76sotlXxsI3cqwnUlkcOuF7TpnHWGVnFGj7/9bPdIOEfnikdDTmxvqUSD9QdvhVtWfFECr2hlw
# 支付宝公钥,不是应用公钥 查看地址:https://openhome.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
publicKey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqOl0nOy/amFKACzBMpO2uvibXOJbehvdMFUMxJMomtYp1RfqvTddsPtGPtX6EX8LFzKB5HC4Ew8rMlh+qvCQctcJfLOsYgK1W8dReLqRxsvMDBtrvDPGVKJwJFmRCbvqWX5b4BOj2fhCP1q0GfS9wsYTP08Xvwq2bDx4OpqdMMi04MPNomIgjgIZmUKeUepsOb0K7rUsgB0OZ6h2RxwKX8BTh2I8AiCU1bvYu6dKpikXz0x72JUg5l69gQCWe6CYHlUSu+gakTz648/GZ6pJXiMzQeemdcytczHrbzA7b3GqtnC5Fj+mxe5E3Upi5MOm82Tl6H3GOyXJ1OuKXdJj1wIDAQAB
# 服务器异步通知页面路径需http://格式的完整路径,不能加?id=123这类自定义参数
notifyUrl:http://mf5s4k.natappfree.cc/order/notifyPayResult
# 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数
returnUrl:http://mf5s4k.natappfree.cc/order/goPaySuccPage
# 签名方式
signType:RSA2
# 字符编码格式
charset:utf-8
# 支付宝网关
gatewayUrl:https://openapi.alipaydev.com/gateway.do
# 日志路径
logPath:"d:\\data\\"


2.11 应用设置application.yml


# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html spring\u914D\u7F6E\u7684\u6587\u6863
server:
  port: 10086
spring:
  servlet:
    multipart:
      max-request-size: 100MB #最大请求文件的大小
      max-file-size: 20MB #设置单个文件最大长度
  mvc:
    view:
      prefix: /
      suffix: .html
  datasource:
      platform: mysql
      type: com.alibaba.druid.pool.DruidDataSource
      initialSize: 3
      minIdle: 1
      maxActive: 500
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 30000
      validationQuery: select 1
      testOnBorrow: true
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/order_db?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8&useAffectedRows=true&rewriteBatchedStatements=true
      username: root
      password: root
mybatis:
#  config-location: classpath:mybatis-config.xml
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.devin.alpay_demo.entity
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置分页插件pagehelper
pagehelper:
    helperDialect: mysql
    reasonable: true
    supportMethodsArguments: true
    params: count=countSql


2.12 下单页面


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>order pay</title>
    <script type="text/javascript" src="/js/jquery-1.8.2.js"></script>
</head>
<body>
<form action="/order/pay" method="post">
    金额:<input id="orderAmount" name="orderAmount"><br>
    <input type="submit">
</form>
</body>
</html>


2.13 支付成功跳转页面

该页面即为上面支付宝配置的returnUrl


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 充值成功!
</body>
</html>
  1. 测试


启动程序,访问 http://localhost:10086/order/goPay,输入金额后点击去支付,可以看到订单已经产生,并跳转到支付宝扫码支付页面


下载支付宝沙盒app,登录测试账号,扫码付款后可以看到,可以付款成功,并且回调也调用成功,订单状态变为已支付。


沙盒支付宝下载: https://sandbox.alipaydev.com/user/downloadApp.htm
沙盒测试账号可以在开发者平台上看到

至此,支付宝的对接完成。

完整的代码git地址: https://github.com/devinzhang0209/alipay_demo.git


结语


🔥一个人可以掌握知识,但只有与他人交流才能形成智慧。

🔥One person can acquire knowledge, but wisdom is formed only in the exchange with others.

🏆 我坚信人与人之间的差距是表面上是财富的差距,本质上是大脑中认知的差距,


我们下期再见。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Java 关系型数据库 MySQL
创建一个SpringBoot项目,实现简单的CRUD功能和分页查询
【9月更文挑战第6天】该内容介绍如何使用 Spring Boot 实现具备 CRUD 功能及分页查询的项目。首先通过 Spring Initializr 创建项目并选择所需依赖;其次配置数据库连接,并创建实体类与数据访问层;接着构建服务层处理业务逻辑;最后创建控制器处理 HTTP 请求。分页查询可通过添加 URL 参数实现。
|
8天前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
|
5天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
18 2
|
9天前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
15 6
|
1月前
|
NoSQL 前端开发 Java
使用 Spring Boot + Neo4j 实现知识图谱功能开发
在数据驱动的时代,知识图谱作为一种强大的信息组织方式,正逐渐在各个领域展现出其独特的价值。本文将围绕使用Spring Boot结合Neo4j图数据库来实现知识图谱功能开发的技术细节进行分享,帮助读者理解并掌握这一技术栈在实际项目中的应用。
108 4
|
2月前
|
机器学习/深度学习 文字识别 前端开发
基于 Spring Boot 3.3 + OCR 实现图片转文字功能
【8月更文挑战第30天】在当今数字化信息时代,图像中的文字信息越来越重要。无论是文档扫描、名片识别,还是车辆牌照识别,OCR(Optical Character Recognition,光学字符识别)技术都发挥着关键作用。本文将围绕如何使用Spring Boot 3.3结合OCR技术,实现图片转文字的功能,分享工作学习中的技术干货。
80 2
|
2月前
|
安全 Java 应用服务中间件
如何在 Spring Boot 3.3 中实现请求 IP 白名单拦截功能
【8月更文挑战第30天】在构建Web应用时,确保应用的安全性是至关重要的。其中,对访问者的IP地址进行限制是一种常见的安全措施,特别是通过实施IP白名单策略,可以只允许特定的IP地址或IP段访问应用,从而有效防止未授权的访问。在Spring Boot 3.3中,我们可以通过多种方式实现这一功能,下面将详细介绍几种实用的方法。
95 1
|
14天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
45 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
15天前
|
前端开发 JavaScript Java
SpringBoot项目部署打包好的React、Vue项目刷新报错404
本文讨论了在SpringBoot项目中部署React或Vue打包好的前端项目时,刷新页面导致404错误的问题,并提供了两种解决方案:一是在SpringBoot启动类中配置错误页面重定向到index.html,二是将前端路由改为hash模式以避免刷新问题。
66 1
|
12天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
29 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统