当前位置: 首页 > 编程日记 > 正文

Springboot支付宝沙箱支付---完整详细步骤

Springboot支付宝沙箱支付—完整详细步骤

网页操作步骤

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

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

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

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

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

在这里插入图片描述 在沙箱进行调试前需要确保已经配置密钥/证书用于加签,支付宝提供了 系统默认密钥自定义密钥 两种方式进行配置。 这里我采取的是默认方式: 开发者如需使用系统默认密钥/证书,可在开发信息中选择系统默认密钥。注意:使用API在线调试工具调试OpenAPI必须使用系统默认密钥。

4.配置应用网关

在这里插入图片描述

应用网关用于接收支付宝沙箱环境的异步通知(对接 From 蚂蚁消息),如创建门店的被动通知。 注意:仅 HTTP 订阅模式的 From 蚂蚁消息才需要配置应用网关,WebSocket 订阅模式的 From 蚂蚁消息无需配置应用网关。

5.生成自己的密钥

在这里插入图片描述 至此,网页操作完成

idea操作步骤

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;
}
java复制代码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";
}

退款流程

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,所以复杂度和成本变高。

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

  2. 死信消费者在根据订单号码查询支付订单状态,如果是未支付情况下,则将该订单设置未超时。 对筛选出来的订单号码进行核对校验:

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

相关文章:

【Mongdb之数据同步篇】什么是Oplog、Mongodb 开启oplog,java监听oplog并写入关系型数据库、Mongodb动态切换数据源

oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。Oplog 可以说是Mongodb Replication的纽带了。

Rust XTask 模式介绍与应用

XTask(扩展任务)是一种在Rust项目中定义和执行自定义构建任务的方式。它通过创建一个独立的Rust库或二进制项目来封装这些任务,利用Rust语言的强类型、安全性和跨平台能力,使得构建流程更加健壮、可读和可维护。

Windows下安装和配置Redis

下载版本Redis-x64-5.0.14.1.zip。(可能需要开代理)

spring.factories文件的作用

即spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。spring.factories文件是Spring Boot自动配置的核心文件之一,它的作用是。

7min到40s:SpringBoot 启动优化实践

然后重点排查这些阶段的代码。先看下。

SpringBoot系列教程之Bean之指定初始化顺序的若干姿势

之前介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢?本文将介绍几种可行的方式来控制 bean 之间的加载顺序。

SpringBoot接口防抖(防重复提交)的一些实现方案

作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。啥是防抖所谓防抖,一是防用户手抖,二是防网络抖动。

SpringBoot请求转发与重定向

但是可能由于B网址相对于A网址过于复杂,这样搜索引擎就会觉得网址A对用户更加友好,因而在重定向之后任然显示旧的网址A,但是显示网址B的内容。在平常使用手机的过程当中,有时候会发现网页上会有浮动的窗口,或者访问的页面不是正常的页面,这就可能是运营商通过某种方式篡改了用户正常访问的页面。重定向,是指在Nginx中,重定向是指通过修改URL地址,将客户端的请求重定向到另一个URL地址的过程,Nginx中实现重定向的方式有多种,比如使用rewrite模块、return指令等。使用场景:在返回视图的前面加上。

SpringBoot 中实现订单30分钟自动取消的策略

在电商和其他涉及到在线支付的应用中,通常需要实现一个功能:如果用户在生成订单后的一定时间内未完成支付,系统将自动取消该订单。本文将详细介绍基于Spring Boot框架实现订单30分钟内未支付自动取消的几种方案,并提供实例代码。

ON DUPLICATE KEY UPDATE 导致mysql自增主键ID跳跃增长

具体解决方案可以根据项目来选择,如果项目不大,可以考虑1和2。如果不考虑高并发问题,可以考虑3。

SpringBoot 优雅实现超大文件上传,通用方案

通俗的说,你把要上传的东西上传,服务器会先做MD5校验,如果服务器上有一样的东西,它就直接给你个新地址,其实你下载的都是服务器上的同一个文件,想要不秒传,其实只要让MD5改变,就是对文件本身做一下修改(改名字不行),例如一个文本文件,你多加几个字,MD5就变了,就不会秒传了。分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。

mysql唯一索引与null

根据NULL的定义,NULL表示的是未知,因此两个NULL比较的结果既不相等,也不不等,结果仍然是未知。根据这个定义,多个NULL值的存在应该不违反唯一约束,所以是合理的,在oracel也是如此。在mysql 的innodb引擎中,是允许在唯一索引的字段中出现多个null值的。有上面的表和数据可以看出,查询多条数据。

Springboot + oauth2 单点登录 - 原理篇

OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。授权码模式(authorization code)密码模式(resource owner password credentials)客户端模式(client credentials) 不常用。

详解mybatis的insert,update,delete返回值

为什么要提数据的事呢,是因为据说这个save返回的就是插入的数据的条数。但是遗憾的是,我们的这个user怎么能没有id呢,没有id有怎么查,怎么删,怎么改。进来的是没有id的user,出去的是有id的user,真是太厉害了,没想到不仅把返回值改变了,连参数都发生了改变,真是太神奇了。keyProperty=“id” 这是id就是绑定的id,那我就疑惑了,这绑定的哪个id啊。这样一搞,如果插入成功的话返回的是1,如果不成功的话返回的是-1。我让你删id是222222的,我还没创建呢,看你怎么删。

MySQL主从复制(基于binlog日志方式)

主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。主从复制的作用1.做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。2.架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。3.读写分离,使数据库能支撑更大的并发。a.从服务器可以执行查询工作(就是我们常说的读功能),降低主服务器压力;(主库写,从库读,降压)

MySQL 中 is null 和 =null 的区别

如果 set ANSI_NULLS为 ON 时,表示SQL语句遵循SQL-92标准;如果 set ANSI_NULLS 为 OFF 时,表示不遵从 SQL-92 标准。但SQL-92 标准要求对null的 = 或不等于 (!= ,) 比较取值都为 false,也就是 =null 或者 null,返回的都是false。null 在MySQL中不代表任何值,通过运算符是得不到任何结果的,因此只能用 is null(默认情况)MySQL 中 null 不代表任务实际的值,类似于一个未知数。

CSS局限属性contain:优化渲染性能的利器

在网页开发中,优化渲染性能是一个重要的目标。CSS局限属性contain是一个强大的工具,可以帮助我们提高网页的渲染性能。本文将介绍contain属性的基本概念、用法和优势,以及如何使用它来优化网页的渲染过程。

配置nginx+keepalived高可用代理数据库ip端口

需求:配置nginx+keepalived高可用反向代理数据库ip端口(数据库服务器无法增加新SCAN IP或者需要隐藏数据库IP的情况下适用)本机ip为:192.168.20.10和192.168.20.11。2.任意节点关机或重启系统,浮动ip也会自动漂移到另外节点。1.任意节点停nginx:浮动ip会自动漂移到另外节点。安装依赖包和nginx和keepalived。浮动IP为:192.168.20.20。配置keepalived.conf。两台centos7.9。

Redis 击穿、穿透、雪崩产生原因解决思路

也就是在设定的时间里数据没有取出来,但是锁由过期了,常见的思路是,锁过期时间值递增,但是想想不靠谱,因为第一个请求可能超时,如果后面的也超时呢,接连多次超时之后,锁过期时间值势必特别大了,这样做弊端太多。雪崩,和击穿类似,不同的是击穿是一个热点Key某时刻失效,而雪崩是大量的热点Key在一瞬间失效,网络上很多博客都在强调解决雪崩的策略是随机过期时间,这个非常不准确,举个例子,银行做活动,之前这个利息系数为2%,过了零点系数改为3%,这种情况能将用户的对应的key改为随机过期吗?如果用的过去的数据叫脏数据。

MySQL数据库查询语句之组函数,子查询语句

当一个SQL的执行需要借助另一个SQL的执行结果时,则需要进行SQL嵌套,该语法结构称之为子查询。先筛选出符合要求的数据,再对符合要求的数据进行分组时,分组的工作量会被减少,效率更高。先确定从哪张表进行操作-->对表中数据进行分组-->基于分组结果进行查询操作。执行顺序:优先执行小括号内的子SQL,根据子SQL的执行结果再执行外层SQL。执行顺序:from-->where-->group by-->select。执行顺序:from-->group by-->select。

在 Spring MVC 中,用于接收前端传递的参数的注解常用的有以下几种

form-data参数使用multipart/form-data作为Content-Type,前端使用params格式传参,后端使用@RequestParam注解接收参数。- json请求体参数使用application/json作为Content-Type,前端使用data格式传参,后端使用@RequestBody注解接收参数。- 路径传参不需要设置Content-Type,前端将参数通过URL传递,后端使用@PathVariable注解接收参数。

Spring AOP 技术实现原理

Spring AOP的实现基于代理模式,通过代理对象来包装目标对象,实现切面逻辑的注入。通过本文,我们深入了解了Spring AOP是如何基于JDK动态代理和CGLIB代理技术实现的。通过详细的示例演示,希望读者能更清晰地理解Spring AOP的底层原理,并在实际项目中灵活应用这一强大的技术。

鸿蒙harmony--数据库sqlite详解

今天是1月20号星期六,早安,岁末大寒至,静后春归来。愿他乡故人,漂泊有归宿,前程有奔赴,愿人间不寒,温暖常伴,诸事顺利,喜乐长安。

Spring Boot应用程序如何配置 HTTPS 访问方式

在 Spring Boot 应用程序中配置 HTTPS 访问方式可以根据你是否拥有由受信任的证书颁发机构(CA)签发的证书来分为两种情况:使用自签名证书和使用 CA 签发的证书。

Redis的key过期策略是怎么实现的

这是一道经典的Redis面试题,一个Redis中可能存在很多很多的key,这些key中可能有很大一部分都有过期时间,此时Redis服务器咋知道哪些key已经过期,哪些还没过期呢?如果直接遍历所有的key,这显然是行不通的,效率非常低!!Redis整体的策略是定期删除和惰性删除相结合。举个栗子:假如我去小卖铺买东西,付款的时候,发现东西过期了。就告知老板,于是老板下架此产品。消费者发现过期了,才去下架,这就叫。小卖铺老板主动定期抽取一部分商品,进行筛查,这就叫定期删除。

雪花算法生成ID、UUID生成ID和MySql自增ID优缺点分析

综上所述,UUID适用于分布式系统和需要保密的场景,雪花ID适用于分布式系统和高并发环境,MySQL自增ID适用于单机系统和高效查询的场景。根据具体的业务需求和系统架构,选择合适的主键类型。通过本文的介绍和对比,希望读者能够更好地理解在MySQL中不推荐使用UUID或者雪花ID作为主键的原因,并能够根据实际情况做出明智的选择。在MySQL中,使用自增整数作为主键是一种常见的做法,因为它具有较小的存储空间、高效的索引和自动增长的特性。然而,具体选择何种主键类型还是要根据具体的业务需求和数据特点来决定。

【小白专用】C# 连接 MySQL 数据库

C# 连接 MySQL 数据库

Java基本数据类型/包装类/对象/数组默认值

不管程序有没有显示的初始化,Java 虚拟机都会先自动给它初始化为默认值。1、整数类型(byte、short、int、long)的基本类型变量的默认值为0。2、单精度浮点型(float)的基本类型变量的默认值为0.0f。3、双精度浮点型(double)的基本类型变量的默认值为0.0d。4、字符型(char)的基本类型变量的默认为 “/u0000”。5、布尔性的基本类型变量的默认值为 false。6、引用类型的变量是默认值为 null。7、数组引用类型的变量的默认值为 null。

如何用pthon连接mysql和mongodb数据库【极简版】

发现宝藏 前言 1. 连接mysql 1.1 安装 PyMySQL 1.2 导入 PyMySQL 1.3 建立连接 1.4 创建游标对象 1.5 执行查询 1.6 关闭连接 1.7 完整示例 2. 连接mongodb 2.1 安装 PyMongo 2.2 导入 PyMongo 2.3 建立连接 2.4

SpringBoot 使用过滤器、拦截器、切面(AOP),及其之间的区别和执行顺序

Servlet(Server Applet),全称是Java Servlet,是提供基于协议请求/响应服务的Java类。在JavaEE中是Servlet规范,即是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的Java类,一般人们理解是后者。是什么。