惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
Lohrmann on Cybersecurity
V
Visual Studio Blog
Cyberwarzone
Cyberwarzone
D
Docker
The Hacker News
The Hacker News
C
CERT Recently Published Vulnerability Notes
Vercel News
Vercel News
Project Zero
Project Zero
S
Schneier on Security
aimingoo的专栏
aimingoo的专栏
I
Intezer
腾讯CDC
M
MIT News - Artificial intelligence
Hugging Face - Blog
Hugging Face - Blog
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
AWS News Blog
AWS News Blog
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Vulnerabilities – Threatpost
G
Google Developers Blog
N
Netflix TechBlog - Medium
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
A
Arctic Wolf
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
Cisco Talos Blog
Cisco Talos Blog
Recent Announcements
Recent Announcements
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
T
Threatpost
Latest news
Latest news
Blog — PlanetScale
Blog — PlanetScale
Security Latest
Security Latest
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
The GitHub Blog
The GitHub Blog
T
Tor Project blog
P
Proofpoint News Feed

牛牛技术客栈

设计模式-备忘录模式 - OXOXTECH 牛牛技术客栈 设计模式-中介者模式 - OXOXTECH 牛牛技术客栈 Linux【Ubuntu】修改ssh默认端口 - OXOXTECH 牛牛技术客栈 设计模式-迭代器模式 - OXOXTECH 牛牛技术客栈 scheduled定时任务的三种基本实现方式 - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(三) - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(二) - OXOXTECH 牛牛技术客栈 Apriori - 基于关联规则的推荐算法(一) - OXOXTECH 牛牛技术客栈 基于JavaFX的桌面端网络调试工具 - OXOXTECH 牛牛技术客栈 Golang Channel的原理介绍 - OXOXTECH 牛牛技术客栈 Go语言Map的原理分析 - OXOXTECH 牛牛技术客栈 Go语言错误处理(panic)的最佳实践 - OXOXTECH 牛牛技术客栈 设计模式-解释器模式 - OXOXTECH 牛牛技术客栈 Redis报错Redis is configured to save RDB snapshots, but it's currently unable to persist to disk. go-webpbin库在Linux报错failed to encode image to WebP: exit status 1.......的问题 exe4j 打包加密的jar - OXOXTECH 牛牛技术客栈 Go生成图形验证码示例 - OXOXTECH 牛牛技术客栈 澳门一天游:一日尽享东方与西方的交融之美 - OXOXTECH 牛牛技术客栈 设计模式-命令模式 - OXOXTECH 牛牛技术客栈 别再自己瞎写工具类了,SpringBoot内置工具类应有尽有 - OXOXTECH 牛牛技术客栈 中山一日游 - OXOXTECH 牛牛技术客栈 设计模式-责任链模式 - OXOXTECH 牛牛技术客栈 起舞吧,齐舞吧 - OXOXTECH 牛牛技术客栈 设计模式-组合模式 - OXOXTECH 牛牛技术客栈 Go语言Web开发|GoFrame框架入门笔记 - OXOXTECH 牛牛技术客栈 Java打包exe教程 - OXOXTECH 牛牛技术客栈 设计模式-代理模式 - OXOXTECH 牛牛技术客栈 MySQL存储过程的优缺点有哪些? - OXOXTECH 牛牛技术客栈 前端渲染优化有哪些? - OXOXTECH 牛牛技术客栈 HTTP状态码及其含义 - OXOXTECH 牛牛技术客栈 从浏览器地址栏输入url到显示页面的步骤 - OXOXTECH 牛牛技术客栈 TypeScript事件派发管理器 - OXOXTECH 牛牛技术客栈 MQTT保留消息的使用方法 - OXOXTECH 牛牛技术客栈 世界工程-港珠澳大桥游 - OXOXTECH 牛牛技术客栈 Golang逃逸分析 - OXOXTECH 牛牛技术客栈 设计模式-享元模式 - OXOXTECH 牛牛技术客栈 牛牛成长记录 - OXOXTECH 牛牛技术客栈 ffmpeg常用命令 - OXOXTECH 牛牛技术客栈 设计模式-外观模式 - OXOXTECH 牛牛技术客栈 设计模式-装饰器模式 - OXOXTECH 牛牛技术客栈 设计模式-桥接模式 - OXOXTECH 牛牛技术客栈 5周年恋爱纪念日 - OXOXTECH 牛牛技术客栈 2024新年快乐,龙腾四海 - OXOXTECH 牛牛技术客栈 迎接新年:除夕的美好时刻 - OXOXTECH 牛牛技术客栈 设计模式-适配器模式 - OXOXTECH 牛牛技术客栈 设计模式-原型模式 - OXOXTECH 牛牛技术客栈 设计模式-建造者模式 - OXOXTECH 牛牛技术客栈 设计模式-工厂模式 - OXOXTECH 牛牛技术客栈 设计模式-单例模式 - OXOXTECH 牛牛技术客栈 SpringBoot在Linux环境下发送163邮件失败(No appropriate protocol (protocol is disabled or cipher suites are inappropriate)) 海与日落 - OXOXTECH 牛牛技术客栈 Swagger比较常用的注解 - OXOXTECH 牛牛技术客栈 猫🐱牛 - OXOXTECH 牛牛技术客栈 2023年最后一个晚霞 - OXOXTECH 牛牛技术客栈 Linux(Centos)部署Nginx教程 - OXOXTECH 牛牛技术客栈 Linux MySQL下载安装详细教程(CentOS版) - OXOXTECH 牛牛技术客栈 JavaFx打包成exe - OXOXTECH 牛牛技术客栈 Flux脚本语言入门教程 - OXOXTECH 牛牛技术客栈 演唱会出图 - OXOXTECH 牛牛技术客栈 Netty TCP解决粘包拆包 - OXOXTECH 牛牛技术客栈 SpringBoot实现订单超时取消的几种方案 - OXOXTECH 牛牛技术客栈 详解Java并发中的各种锁 - OXOXTECH 牛牛技术客栈 雪花算法:分布式系统唯一ID生成算法 - OXOXTECH 牛牛技术客栈 Java解决空指针的神器Optional - OXOXTECH 牛牛技术客栈 与兴一起 - OXOXTECH 牛牛技术客栈 Java17新特性详解与安装 - OXOXTECH 牛牛技术客栈 Jdk17安装+环境配置详细教程 - OXOXTECH 牛牛技术客栈 孤注一掷 - OXOXTECH 牛牛技术客栈 解决WinSCP经常断线重连 - OXOXTECH 牛牛技术客栈 内存不足导致Tomcat崩溃问题排查与解决办法 - OXOXTECH 牛牛技术客栈 influxDB初识,一个高效的时序数据库 - OXOXTECH 牛牛技术客栈 SpringBoot 服务接口限流方案 - OXOXTECH 牛牛技术客栈 Docker 安装 Portainer - OXOXTECH 牛牛技术客栈 Linux 安装Docker - OXOXTECH 牛牛技术客栈 物料宣传 - OXOXTECH 牛牛技术客栈 Java使用EMQX实现MQTT通信 - OXOXTECH 牛牛技术客栈 Java实现常见的排序算法 - OXOXTECH 牛牛技术客栈 FreeSwitch Windows安装教程 - OXOXTECH 牛牛技术客栈 MQTT单向SSL数据加密 - OXOXTECH 牛牛技术客栈 随性 - OXOXTECH 牛牛技术客栈 mysql报错Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre的解决方案 Git Push项目报 push to origin/master was rejected 错误解决方案 游行记——珠海金沙滩与金湖公园之行 - OXOXTECH 牛牛技术客栈 Tomcat:解决Tomcat启动警告:"无法将资源添加到Web应用程序缓存中....请考虑增加缓存空间" 的问题 - OXOXTECH 牛牛技术客栈 励骏庞都广场,迷一般的皇宫 - OXOXTECH 牛牛技术客栈 Docker 常用命令集合 - OXOXTECH 牛牛技术客栈 ElasticSearch Windows版-安装教程 - OXOXTECH 牛牛技术客栈 Java去除对象中为null的字段 - OXOXTECH 牛牛技术客栈 我和我的青春 - OXOXTECH 牛牛技术客栈 Java实现螺旋矩阵算法: - OXOXTECH 牛牛技术客栈 Java直接内存分配和释放的理解 - OXOXTECH 牛牛技术客栈 FreeSwitch将默认数据库迁移至MySQL - OXOXTECH 牛牛技术客栈 别错过路上的风景,别错过刹那间的深情! - OXOXTECH 牛牛技术客栈 Viewer.js:一款强大的图片预览组件 - OXOXTECH 牛牛技术客栈 Java JDK Proxy和CGLib动态代理示例 - OXOXTECH 牛牛技术客栈 redis常用命令 - OXOXTECH 牛牛技术客栈 SpringBoot查询IP归属地 - OXOXTECH 牛牛技术客栈 Spring 事务失效的六种情况 - OXOXTECH 牛牛技术客栈 #张艺兴每时每刻# - OXOXTECH 牛牛技术客栈 Nginx常用命令及具体应用 - OXOXTECH 牛牛技术客栈
SpringBoot集成支付宝支付 - OXOXTECH 牛牛技术客栈
Mr.Potato · 2023-09-27 · via 牛牛技术客栈

前言

最近在做一个支付小demo,后端采用了SpringBoot,需要集成支付宝进行线上支付,在这个过程中研究了大量支付宝的集成资料,也走了一些弯路,现在总结出来,相信你读完也能轻松集成支付宝支付。

在开始集成支付宝支付之前,我们需要准备一个支付宝商家账户,如果是个人开发者,可以通过注册公司或者让有公司资质的单位进行授权,后续在集成相关API的时候需要提供这些信息。

下面我以电脑网页端在线支付为例,介绍整个从集成、测试到上线的具体流程。

预期效果展示

在开始之前我们先看下我们要达到的最后效果,具体如下:

  1. 前端点击支付跳转到支付宝界面
  2. 支付宝界面展示付款二维码
  3. 用户手机端支付
  4. 完成支付,支付宝回调开发者指定的url。

202309241543362827.webp

开发流程

沙箱调试

支付宝为我们准备了完善的沙箱开发环境,我们可以先在沙箱环境调试好程序,后续新建好应用并成功上线后,把程序中对应的参数替换为线上参数即可。

1、创建沙箱应用

直接进入 open.alipay.com/develop/san… 创建沙箱应用即可。

202309251325326244.webp

这里因为是测试环境,我们就选择系统默认密钥就行了,下面选择公钥模式,另外应用网关地址就是用户完成支付之后,支付宝会回调的url。在开发环境中,我们可以采用内网穿透的方式,将我们本机的端口暴露在某个公网地址上,这里推荐 natapp.cn/ ,可以免费注册使用。

2、SpringBoot代码实现

在创建好沙盒应用,获取到密钥,APPID,商家账户PID等信息之后,就可以在测试环境开发集成对应的API了。这里我以电脑端支付API为例,介绍如何进行集成。

关于电脑网站支付的详细产品介绍和API接入文档可以参考:opendocs.alipay.com/open/repo-0…opendocs.alipay.com/open/270/01…
  • 步骤1 添加alipay sdk对应的Maven依赖。
<!-- alipay -->  
<dependency>  
   <groupId>com.alipay.sdk</groupId>  
   <artifactId>alipay-sdk-java</artifactId>  
   <version>4.35.132.ALL</version>  
</dependency>
  • 步骤2 添加支付宝下单、支付成功后同步调用和异步调用的接口。
@RestController
@Api(tags = "支付模块控制器")
@RequestMapping(value = RequestConstants.Handler.PC_V1)
public class AliPayController {
    @Resource
    private AliPayService aliPayService;

    @PostMapping(RequestConstants.Payment.ALI_PAY_ORDER)
    @ApiOperation("PCWeb下订单")
    public AjaxResult placeOrderForPCWeb(@RequestBody AliPayRequest aliPayRequest) {
        return aliPayService.placeOrderForPCWeb(aliPayRequest);
    }

    @PostMapping(RequestConstants.Payment.ALI_PAY_CALLBACK_ASYNC)
    public AjaxResult asyncCallback(HttpServletRequest request) {
        return aliPayService.orderCallbackInAsync(request);
    }

    @GetMapping(RequestConstants.Payment.ALI_PAY_CALLBACK_SYNC)
    public void syncCallback(HttpServletRequest request, HttpServletResponse response) {
        aliPayService.orderCallbackInSync(request, response);
    }
}
这里需要注意,同步接口是用户完成支付后会自动跳转的地址,因此需要是Get请求。异步接口,是用户完成支付之后,支付宝会回调来通知支付结果的地址,所以是POST请求。
  • 步骤3 实现Service层代码
public interface AliPayService {
    //PCWeb 下订单
    AjaxResult placeOrderForPCWeb(AliPayRequest aliPayRequest);

    AjaxResult orderCallbackInAsync(HttpServletRequest request);

    void orderCallbackInSync(HttpServletRequest request, HttpServletResponse response);
}

实现类:

@Service("AliPayService")
@Slf4j
public class AliPayServiceImpl extends BaseController implements AliPayService {
    @Resource
    private AliPayHelper aliPayHelper;
    @Resource
    private AlipayConfig alipayConfig;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult placeOrderForPCWeb(AliPayRequest aliPayRequest) {
        log.info("【请求开始-在线购买-交易创建】*********统一下单开始*********");

        String tradeNo = aliPayHelper.generateTradeNumber();
        log.info("订单号:"+tradeNo);
        String subject = "购买套餐1";
        Map<String, Object> map = aliPayHelper.placeOrderAndPayForPCWeb(tradeNo, 12000.50f, subject);

        if (Boolean.parseBoolean(String.valueOf(map.get("isSuccess")))) {
            log.info("【请求开始-在线购买-交易创建】统一下单成功,开始保存订单数据");
            //保存订单信息
            // 添加你自己的业务逻辑,主要是保存订单数据
            log.info("【请求成功-在线购买-交易创建】*********统一下单结束*********");
            return success(String.valueOf(map.get("body")).replace("\\\"","\""));
        }else{
            log.info("【失败:请求失败-在线购买-交易创建】*********统一下单结束*********");
            return error(String.valueOf(map.get("subMsg")));
        }
    }

    @Override
    public AjaxResult orderCallbackInAsync(HttpServletRequest request) {
        try {
            log.info("进入异步回调方法");
            Map<String, String> map = aliPayHelper.paramsToMap(request);
            String tradeNo = map.get("out_trade_no");
            String sign = map.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(map);
            boolean signVerified = aliPayHelper.CheckSignIn(sign, content);

            // check order status
            // 这里在DB中检查order的状态,如果已经支付成功,无需再次验证。
//            if(从DB中拿到order,并且判断order是否支付成功过){
//                log.info("订单:" + tradeNo + " 已经支付成功,无需再次验证。");
//                return "success";
//            }
            OrderInfo orderInfo = new OrderInfo(new BigDecimal("100.50"), 0);
            if(orderInfo.status() == 1){
                log.info("订单:" + tradeNo + " 已经支付成功,无需再次验证。");
                return success("已经支付成功!");
            }
            //验证业务数据是否一致
            if(!checkData(map, orderInfo)){
                log.error("返回业务数据验证失败,订单:" + tradeNo );
                return error("返回业务数据验证失败");
            }
            //签名验证成功
            if(signVerified){
                log.info("支付宝签名验证成功,订单:" + tradeNo);
                // 验证支付状态
                String tradeStatus = request.getParameter("trade_status");
                if(tradeStatus.equals("TRADE_SUCCESS")){
                    log.info("支付成功,订单:"+tradeNo);
                    // 更新订单状态,执行一些业务逻辑

                    return success("支付成功,订单:"+tradeNo);
                }else{
                    System.out.println("支付失败,订单:" + tradeNo);
                    return error("支付失败,订单:" + tradeNo);
                }
            }else{
                log.error("签名验证失败,订单:" + tradeNo );
                return error("签名验证失败");
            }
        } catch (IOException e) {
            log.error("IO exception happened ", e);
            throw new RuntimeException();
        }
    }

    @Override
    public void orderCallbackInSync(HttpServletRequest request, HttpServletResponse response) {
        try {
            log.info("进入同步回调方法");
            OutputStream outputStream = response.getOutputStream();
            //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
            response.setHeader("content-type", "text/html;charset=UTF-8");
            String outputData = "支付成功,请返回网站并刷新页面。";

            /**
             * data.getBytes()是一个将字符转换成字节数组的过程,这个过程中一定会去查码表,
             * 如果是中文的操作系统环境,默认就是查找查GB2312的码表,
             */
            //将字符转换成字节数组,指定以UTF-8编码进行转换
            byte[] dataByteArr = outputData.getBytes(StandardCharsets.UTF_8);
            outputStream.write(dataByteArr);//使用OutputStream流向客户端输出字节数组
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private boolean checkData(Map<String, String> map, OrderInfo order) {
        log.info("【请求开始-交易回调-订单确认】*********校验订单确认开始*********");
        //todo 此处应该验证订单号是否准确,并且订单状态为待支付
        if(true){
            float amount1 = Float.parseFloat(map.get("total_amount"));
            float amount2 = order.orderAmount().floatValue();
            //判断金额是否相等
            if(amount1 == amount2){
                //验证收款商户id是否一致
                if(map.get("seller_id").equals(alipayConfig.getPid())){
                    //判断appid是否一致
                    if(map.get("app_id").equals(alipayConfig.getAppid())){
                        log.info("【成功:请求开始-交易回调-订单确认】*********校验订单确认成功*********");
                        return true;                    }
                }
            }
        }
        log.info("【失败:请求开始-交易回调-订单确认】*********校验订单确认失败*********");
        return false;
    }
}
  • 步骤4 实现AliPayHelper核心类。这个类里面对支付宝的接口进行封装。
@Slf4j
@Component
public class AliPayHelper {

    @Resource
    private AlipayConfig alipayConfig;

    //返回数据格式
    private static final String FORMAT = "json";
    //编码类型
    private static final String CHART_TYPE = "utf-8";
    //签名类型
    private static final String SIGN_TYPE = "RSA2";

    /*支付销售产品码,目前支付宝只支持FAST_INSTANT_TRADE_PAY*/
    public static final String PRODUCT_CODE = "FAST_INSTANT_TRADE_PAY";

    private static AlipayClient alipayClient = null;

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
    private static final Random random = new Random();

    @PostConstruct
    public void init(){
        alipayClient = new DefaultAlipayClient(
                alipayConfig.getGateway(),
                alipayConfig.getAppid(),
                alipayConfig.getPrivateKey(),
                FORMAT,
                CHART_TYPE,
                alipayConfig.getPublicKey(),
                SIGN_TYPE);
    };

    /**
     * 生成交易号
     */
    public String generateTradeNumber() {
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();
        // 将时间戳格式化为字符串
        String timestampStr = dateFormat.format(new Date(timestamp));
        int randomNumber = random.nextInt(1000000); // 0 到 999999 之间的随机数
        // 使用字符串拼接将时间戳和随机数组合成交易号
        return timestampStr + String.format("%06d", randomNumber);
    }
    /*================PC网页支付====================*/
    /**
     * 统一下单并调用支付页面接口
     * @param outTradeNo
     * @param totalAmount
     * @param subject
     * @return
     */
    public Map<String, Object> placeOrderAndPayForPCWeb(String outTradeNo, float totalAmount, String subject){
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(alipayConfig.getNotifyUrl());
        request.setReturnUrl(alipayConfig.getReturnUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", outTradeNo);
        bizContent.put("total_amount", totalAmount);
        bizContent.put("subject", subject);
        bizContent.put("product_code", PRODUCT_CODE);

        request.setBizContent(bizContent.toString());
        AlipayTradePagePayResponse response = null;
        try {
            response = alipayClient.pageExecute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        Map<String, Object> resultMap = new HashMap<>();
        assert response != null;
        resultMap.put("isSuccess", response.isSuccess());
        if(response.isSuccess()){
            log.info("调用成功");
            resultMap.put("body", response.getBody());
        } else {
            log.error("调用失败");
            log.error(response.getSubMsg());
            resultMap.put("subMsg", response.getSubMsg());
        }
        return resultMap;
    }

    /**
     * 交易订单查询
     * @param out_trade_no
     * @return
     */
    public Map<String, Object> tradeQueryForPCWeb(String out_trade_no){
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", out_trade_no);
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        Map<String, Object> resultMap = new HashMap<>();
        assert response != null;
        resultMap.put("isSuccess", response.isSuccess());
        if(response.isSuccess()){
            System.out.println("调用成功");
            System.out.println(JSON.toJSONString(response));
            resultMap.put("status", response.getTradeStatus());
        } else {
            System.out.println("调用失败");
            System.out.println(response.getSubMsg());
            resultMap.put("subMsg", response.getSubMsg());
        }
        return resultMap;
    }

    /**
     * 验证签名是否正确
     * @param sign
     * @param content
     * @return
     */
    public boolean CheckSignIn(String sign, String content){
        try {
            return AlipaySignature.rsaCheck(content, sign, alipayConfig.getPublicKey(), CHART_TYPE, SIGN_TYPE);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将异步通知的参数转化为Map
     * @return
     */
    public Map<String, String> paramsToMap(HttpServletRequest request) throws UnsupportedEncodingException {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        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] + ",";
            }
            // 乱码解决,这段代码在出现乱码时使用。
//            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        return params;
    }

}
  • 步骤5,封装config类,用于存放所有的配置属性。
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
    private String gateway;

    private String appid;

    private String pid;

    private String privateKey;

    private String publicKey;

    private String returnUrl;

    private String notifyUrl;
}

另外需要在application.yml中,准备好上述对应的属性。

202309261631328185.webp

3、前端代码实现

由于是测试的小demo,这里就简单做一下页面,就只有一个支付的按钮。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="paymentContainer" style="width: 100%;height: 500px"></div>
    <button onclick="pay()">点击支付</button>
</body>
<script>
    function pay(){
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'http://3qr9js.natappfree.cc/pc/v1/alipay/order', true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.onload = function () {

            if (xhr.status===200){
                let msg = JSON.parse(xhr.response).msg;
                console.log(msg)
                document.getElementById("paymentContainer").innerHTML = msg;
                document.forms[0].submit();
            }
        };
        var requestBody = JSON.stringify({  });
        xhr.send(requestBody);
    }
</script>
</html>

打开页面,当我们点击支付按钮的时候就会跳到支付宝支付页面

20230926163658859.webp

跳出此页面,然后就可以用支付宝沙箱版扫码测试了

202309261704372639.webp

创建并上线应用

完成沙盒调试没问题之后,我们需要创建对应的支付宝网页应用并上线。

登录 open.alipay.com/develop/man… 并选择创建网页应用

202309271409199948.webp

然后输入对应的信息即可

202309271410046561.webp

创建好应用之后,首先在开发设置中,设置好接口加签方式以及应用网关

202309271411557066.webp

注意:密钥选择RSA2,其他按照上面的操作指南一步步走即可,注意保管好自己的私钥公钥

之后在产品绑定页,绑定对应的API,比如我们这里是PC网页端支付,找到对应的API绑定就可以了。如果第一次绑定,可能需要填写相关的信息进行审核,按需填写即可,一般审核一天就通过了。

202309271416228298.webp

最后如果一切就绪,我们就可以把APP提交上线了,上线成功之后,我们需要把下面SpringBoot中的application.yml配置替换为线上应用的信息,然后就可以在生产环境调用支付宝的接口进行支付了。

#支付宝支付配置属性
alipay:
  gateway: https://openapi-sandbox.dl.alipaydev.com/gateway.do
  appid: ****
  pid: ****
  privateKey: ****
  publicKey: ****
  #完成支付后的同步跳转地址
  returnUrl: 3qr9js.natappfree.cc/pc/v1/alipay/callback/sync
  #完成支付后,支付宝会异步回调的地址
  notifyUrl: 3qr9js.natappfree.cc/pc/v1/alipay/callback/async
参考: https://juejin.cn/post/7244498421283881021#heading-6