SpringBoot 集成支付宝支付

news2024/11/20 7:00:18

网页操作步骤

1.进入支付宝开发平台—沙箱环境

使用开发者账号登录开放平台控制平台

2.点击沙箱进入沙箱环境

图片

 

说明:沙箱环境支持的产品,可以在沙箱控制台 沙箱应用 > 产品列表 中查看。

3.进入沙箱,配置接口加签方式

图片

 

在沙箱进行调试前需要确保已经配置密钥/证书用于加签,支付宝提供了 系统默认密钥 及 自定义密钥 两种方式进行配置。这里我采取的是默认方式:

开发者如需使用系统默认密钥/证书,可在开发信息中选择系统默认密钥。注意:使用API在线调试工具调试OpenAPI必须使用系统默认密钥。

4.配置应用网关

图片

 

应用网关用于接收支付宝沙箱环境的异步通知(对接 From 蚂蚁消息),如创建门店的被动通知。

注意:仅 HTTP 订阅模式的 From 蚂蚁消息才需要配置应用网关,WebSocket 订阅模式的 From 蚂蚁消息无需配置应用网关。

5.生成自己的密钥

图片

 

至此,网页操作完成

2idea操作步骤

1.导入依赖
<dependency>
   <groupId>com.alipay.sdk</groupId>
   <artifactId>alipay-sdk-java</artifactId>
   <version>4.22.110.ALL</version>
</dependency>

2.在 application.yml 里面进行配置
alipay:
  appId: 
  appPrivateKey: 
  alipayPublicKey: 
  notifyUrl: (回调接口)

3.alipay的JAVA配置:AlipayConfig.java

读取yml中的配置信息,自动填充到对应的属性

@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
    private String appId;
    private String appPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;

}

4.支付接口 新建一个 AliPayController.java

1、 在Controller中配置gateway_url(调用支付宝url的一个网关地址)、format(JSON形式)、charset(UTF-8)、sign_type(签名方式-rsa2;
2、 编写一个Get请求,(方法参数是一个AliPay的配置类里面包括自己生成的订单号、总金额、支付的名称、支付宝交易凭证号和HttpServletResponse);
3、 创建Client(他是由通用SDK提供的Client,负责调用支付宝的API,设置参数包含网关地址、appid、密钥、公钥、format、charset、签名方式)----------------------->创建Client,他是由通用SDK提供的Client,负责调用支付宝的API;
4、 创建AlipayTradePagePayRequest,配置notifyUrl并设置Request参数(参数包含订单号、总金额、支付的名称)(格式:JSON格式)------------------------->创建Request并设置Request参数;
5、 通过AlipayClient执行request调用SDK生成表单,用HttpServletResponse(浏览器响应的一个流)写表单的内容,创建一个html的网页)--------------------------->执行请求,拿到响应的结果,返回给浏览器;

@Data
public class AliPay {
    private String traceNo;
    private double totalAmount;
    private String subject;
    private String alipayTraceNo;
}

private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
private static final String FORMAT = "JSON";
private static final String CHARSET = "UTF-8";
    //签名方式
    private static final String SIGN_TYPE = "RSA2";
@Resource
private AliPayConfig aliPayConfig;

@Resource
private OrdersMapper ordersMapper;

@GetMapping("/pay") // &subject=xxx&traceNo=xxx&totalAmount=xxx
public void pay(AliPay aliPay, HttpServletResponse httpResponse) throws Exception {
    // 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
    AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
            aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);

    // 2. 创建 Request并设置Request参数
    AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();  // 发送请求的 Request类
    request.setNotifyUrl(aliPayConfig.getNotifyUrl());
    JSONObject bizContent = new JSONObject();
    bizContent.set("out_trade_no", aliPay.getTraceNo());  // 我们自己生成的订单编号
    bizContent.set("total_amount", aliPay.getTotalAmount()); // 订单的总金额
    bizContent.set("subject", aliPay.getSubject());   // 支付的名称
    bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");  // 固定配置
    request.setBizContent(bizContent.toString());

    // 执行请求,拿到响应的结果,返回给浏览器
    String form = "";
    try {
        form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
    } catch (AlipayApiException e) {
        e.printStackTrace();
    }
    httpResponse.setContentType("text/html;charset=" + CHARSET);
    httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
    httpResponse.getWriter().flush();
    httpResponse.getWriter().close();
}

5.在拦截器里面加上 忽略alipay接口的配置

遇到的坑:

url中有中文字符报错,更换依赖

官网提供有easy版和正式版

easy-sdk 好像不太支持中文的subject,否则 biz_content就会乱码,那我索性就用了 alipay-sdk 正式版的

6.回调接口

1、 使用的Post接口,首先验证交易状态是否成功,获取request里面的信息;
2、 支付宝验签(使用的是AlipaySignature(通用SDK提供的类)获取一个String字符串将其与sign签名验证),通过后,使用OrderMapper更新到数据库);

使用的Post接口,因为官方建议处理付款成功后的操作在异步调用方法中,异步调用为post请求,异步回调方法必须为公网IP,因为支付宝是基于公网访问,访问不了localhost,需要代理,设置公网IP有两种方案,1、内网穿透,2、将项目部署到服务器,我们项目使用的是内网穿透,使用的是natapp,配置一条免费的隧道,在idea中配置notifyurl接口

@PostMapping("/notify")  // 注意这里必须是POST接口
public String payNotify(HttpServletRequest request) throws Exception {
    if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
        System.out.println("=========支付宝异步回调========");        
  Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            params.put(name, request.getParameter(name));
            // System.out.println(name + " = " + request.getParameter(name));
        }

        String tradeNo = params.get("out_trade_no");
        String gmtPayment = params.get("gmt_payment");
        String alipayTradeNo = params.get("trade_no");

        String sign = params.get("sign");
        String content = AlipaySignature.getSignCheckContentV1(params);
        boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名
        // 支付宝验签
        if (checkSignature) {
            // 验签通过
            System.out.println("交易名称: " + params.get("subject"));
            System.out.println("交易状态: " + params.get("trade_status"));
            System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
            System.out.println("商户订单号: " + params.get("out_trade_no"));
            System.out.println("交易金额: " + params.get("total_amount"));
            System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
            System.out.println("买家付款时间: " + params.get("gmt_payment"));
            System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));

            // 更新订单未已支付
            ordersMapper.updateState(tradeNo, "已支付", gmtPayment, alipayTradeNo);
        }
    }
    return "success";
}

3退款流程

1、 创建Client(他是由通用SDK提供的Client,负责调用支付宝的API)(参数包含网关地址、appid、密钥、公钥、format、charset、签名方式)---------------------->创建Client,通用SDK提供的Client,负责调用支付宝的API;

2、 创建AlipayTradePagePayRequest,设置Request参数(参数包含支付宝回调的订单流水号、总金额、我的订单编号)(格式:JSON格式)---------------------------->创建Request,设置参数;
3、 通过AlipayClient执行request获取response,通过isSuccess判断是否成功,成功后更新数据库状态------------->执行请求,更新数据库;

@GetMapping("/return")
public Result returnPay(AliPay aliPay) throws AlipayApiException {
        // 7天无理由退款
        String now = DateUtil.now();
        Orders orders = ordersMapper.getByNo(aliPay.getTraceNo());
        if (orders != null) {
            // hutool工具类,判断时间间隔
            long between = DateUtil.between(DateUtil.parseDateTime(orders.getPaymentTime()), DateUtil.parseDateTime(now), DateUnit.DAY);
            if (between > 7) {
                return Result.error("-1", "该订单已超过7天,不支持退款");
            }
        }
    // 1. 创建Client,通用SDK提供的Client,负责调用支付宝的API
    AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL,
            aliPayConfig.getAppId(), aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET,
            aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);
    // 2. 创建 Request,设置参数
    AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
    JSONObject bizContent = new JSONObject();
    bizContent.set("trade_no", aliPay.getAlipayTraceNo());  // 支付宝回调的订单流水号
    bizContent.set("refund_amount", aliPay.getTotalAmount());  // 订单的总金额
    bizContent.set("out_request_no", aliPay.getTraceNo());   //  我的订单编号

    // 返回参数选项,按需传入
    //JSONArray queryOptions = new JSONArray();
    //queryOptions.add("refund_detail_item_list");
    //bizContent.put("query_options", queryOptions);

    request.setBizContent(bizContent.toString());

    // 3. 执行请求
    AlipayTradeRefundResponse response = alipayClient.execute(request);
    if (response.isSuccess()) {  // 退款成功,isSuccess 为true
        System.out.println("调用成功");

        // 4. 更新数据库状态
        ordersMapper.updatePayState(aliPay.getTraceNo(), "已退款", now);
        return Result.success();
    } else {   // 退款失败,isSuccess 为false
        System.out.println(response.getBody());
        return Result.error(response.getCode(), response.getBody());
    }

}

订单三十分钟未支付自动取消

使用消息队列

我们可以采用rabbitMQ的延时队列。RabbitMQ具有以下两个特性,可以实现延迟队列

RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter

RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能

优缺点

  • 优点: 高效,可以利用rabbitmq的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性。

  • 缺点: 本身的易用度要依赖于rabbitMq的运维.因为要引用rabbitMq,所以复杂度和成本变高。

用户下单之后,投递一个msg消息存放在msg服务器daunt,该消息msg消息过期时间为30分钟,一直未被订单消费者消费,消息会转移到死信交换机路由到死信队列中,被我们的死信消费者30分钟后消息。

死信消费者在根据订单号码查询支付订单状态,如果是未支付情况下,则将该订单设置未超时。

对筛选出来的订单号码进行核对校验:

1、 订单中是否存在;
2、 携带订单号码调用支付宝查询订单支付状态是否为待支付;
3、 更新该订单号码状态;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1351435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

扫地机器人地图与用户终端的同步

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/APaJheSbgTW3jNssWsp5Ng 地图数据来源于机器人算法模块&#xff0c;一般通过SLAM算法完成建图的过程。 建图过程中&#xff0c;基础数据涉及到各…

Android 串口协议

前言 本协议是 Android 应用端与主控板之间的通信协议&#xff0c;是串行通信协议。 协议要求同一时间只能有两个通讯端点在相互通讯&#xff0c;采用小端传输数据。 硬件层基于RS485协议&#xff0c;采取半双工&#xff0c;一主多从的通讯模式。Android定义为主机&#xff0c…

一、初识Redis与分布式系统

目录 一、Redis应用 二、实现方式 三、Redis应用 四、分布式系统 五、分布式系统实现 1、应用服务和数据库服务分离 2、引入负载均衡&#xff0c;应用服务器集群&#xff08;解决高并发&#xff09; 3、引入读写分离&#xff0c;数据库主从结构&#xff08;解决高并发&a…

如何在iPhone设备中查看崩溃日志

​ 目录 如何在iPhone设备中查看崩溃日志 摘要 引言 导致iPhone设备崩溃的主要原因是什么&#xff1f; 使用克魔助手查看iPhone设备中的崩溃日志 奔溃日志分析 总结 摘要 本文介绍了如何在iPhone设备中查看崩溃日志&#xff0c;以便调查崩溃的原因。我们将展示三种不同的…

流媒体学习之路(WebRTC)——GCC分析(4)

流媒体学习之路(WebRTC)——GCC分析&#xff08;4&#xff09; —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全配置…

【Java并发】深入浅出 synchronized关键词原理-上

一个问题的思考 建设我们有两个线程&#xff0c;一个进行5000次的相加操作&#xff0c;另一个进行5000次的减操作。那么最终结果是多少 package com.jia.syn;import java.util.concurrent.TimeUnit;/*** author qxlx* date 2024/1/2 10:08 PM*/ public class SynTest {privat…

使用Vue3开发学生管理系统模板1

环境搭建 通过解压之前《Vue3开发后台管理系统模板》的代码&#xff0c;我们能够得到用户增删改查的页面&#xff0c;我们基于用户增删改查的页面做进一步的优化。 创建学生增删改查页面 第一步&#xff1a;复制用户增删改查页面&#xff0c;重命名为StudentCRUD.vue <…

新闻稿发布:媒体重要还是价格重要

在当今信息爆炸的数字时代&#xff0c;企业推广与品牌塑造不可或缺的一环就是新闻稿发布。新闻稿是一种通过媒体渠道传递企业信息、宣传品牌、事件或产品新闻的文本形式。发布新闻稿的过程旨在将企业的声音传递给更广泛的受众&#xff0c;借助媒体平台实现品牌故事的广泛传播。…

exec、execFile、fork、spawn的区别与使用场景

在Node.js中&#xff0c;通过子进程可以实现并行执行任务&#xff0c;处理复杂的操作&#xff0c;以及与外部命令或文件进行交互。Node.js提供了多种子进程创建方法&#xff0c;包括exec、execFile、fork和spawn。本文将对这些方法进行比较&#xff0c;并介绍它们的适用场景和示…

【深度学习-基础学习】Transformer 笔记

本篇文章学习总结 李宏毅 2021 Spring 课程中关于 Transformer 相关的内容。课程链接以及PPT&#xff1a;李宏毅Spring2021ML这篇Blog需要Self-Attention为前置知识。 Transfomer 简介 Transfomer 架构主要是用来解决 Seq2Seq 问题的&#xff0c;也就是 Sequence to Sequence…

大数据StarRocks(一) StarRocks概述

1 StarRocks介绍 StarRocks是新一代极速全场景MPP(Massively Parallel Processing)数据库&#xff0c;它充分吸收关系型OLAP数据库和分布式存储系统在大数据时代的优秀研究成果&#xff0c;在业界实践的基础上&#xff0c;进一步改进优化、升级架构&#xff0c;并增添了众多全…

NSSCTF 1zjs

开启环境: 搞就完事了,别玩魔法! 源码打开 点击访问:./dist/index.umd.js" 搜索php,找到23条相关的,注意到有一个特别的信息: PERFORMANCE OF THIS SOFTWARE.Your gift just take it : /fk3f1ag.php 访问: node4.anna.nssctf.cn:28325/fk3f1ag.php 得到这样: ([![]…

【hyperledger-fabric】部署和安装

简介 对hyperledger-fabric进行安装&#xff0c;话不多说&#xff0c;直接开干。但是需要申明一点&#xff0c;也就是本文章全程是开着加速器进行的资源操作&#xff0c;所以对于没有开加速器的情况可能会由于网络原因导致下载资源失败。 资料提供 1.官方部署文档在此&#…

IPC之十二:使用libdbus在D-Bus上异步发送/接收信号的实例

IPC 是 Linux 编程中一个重要的概念&#xff0c;IPC 有多种方式&#xff0c;本 IPC 系列文章的前十篇介绍了几乎所有的常用的 IPC 方法&#xff0c;每种方法都给出了具体实例&#xff0c;前面的文章里介绍了 D-Bus 的基本概念以及调用远程方法的实例&#xff0c;本文介绍 D-Bus…

mysql 条件位运算实现多值存储

一、多值存储 mysql 条件位运算位运算实现多值存储&#xff0c;方法适合数据范围有限&#xff0c;且不会变更在业务上往往会出现多选的情况&#xff0c;例&#xff1a;选择 周一 至 周日 随意组合&#xff1b; 数据在设计时就会如何去储存&#xff1f; 一种是一般是在储存是以…

ASP.NET Core AOT

Native AOT 最初在 .NET 7 中引入&#xff0c;在即将发布的 .NET 8 版本中可以与 ASP.NET Core 一起使用。在这篇文章中&#xff0c;我们从总体角度审视其优点和缺点&#xff0c;并进行测量以量化不同平台上的改进。 源代码&#xff1a;https://download.csdn.net/download/he…

MATLAB中xcorr函数用法

目录 语法 说明 示例 两个向量的互相关 向量的自相关 归一化的互相关 xcorr函数的功能是返回互相关关系。 语法 r xcorr(x,y) r xcorr(x) r xcorr(___,maxlag) r xcorr(___,scaleopt) [r,lags] xcorr(___) 说明 r xcorr(x,y) 返回两个离散时间序列的互相关。互相…

SpringBoot整合ElasticSearch实现CRUD操作

本文来说下SpringBoot整合ES实现CRUD操作 文章目录 概述项目搭建 概述 SpringBoot支持两种技术和es交互。一种的jest&#xff0c;还有一种就是SpringData-ElasticSearch。根据引入的依赖不同而选择不同的技术。反正作为spring全家桶&#xff0c;目前是以springdata为主流使用技…

uniapp中uview组件丰富的Code 验证码输入框的使用方法

目录 基本使用 #自定义提示语 #保持倒计时 API #Props #Methods #Event 基本使用 通过ref获取组件对象&#xff0c;再执行后面的操作&#xff0c;见下方示例。 通过seconds设置需要倒计的秒数(默认60)通过ref调用组件内部的start方法&#xff0c;开始倒计时通过监听cha…

在 Oracle 数据库表中加载多个数据文件

在本文中&#xff0c;我将展示 SQL 加载器 Unix 脚本实用程序的强大功能&#xff0c;其中 SQL 加载器可以使用自动 shell 脚本加载多个数据文件。这在处理大量数据以及需要将数据从一个系统移动到另一个系统时非常有用。 它适合涉及大量历史数据的迁移项目。那么就不可能为每…