支付

3613 字
18 分钟
支付

支付业务流程#

业务场景如下,用户确认订单之后,跳转到支付界面,点击支付宝扫码支付的图标,就可以进行扫码支付了

1. 用户点击扫码支付

请求url:http://localhost/pay/alipay/submit/{orderId}

image-20230324102648455
image-20230324102648455

2. 跳转到支付宝付款页面

image-20230324152933063
image-20230324152933063

3. 用户扫码付款

image-20230324165118538
image-20230324165118538

4. 浏览器跳转到支付成功页面

image-20230324163337306
image-20230324163337306

支付宝支付#

支付宝简介#

支付宝是国内的第三方支付平台,致力于提供简单、安全、快速的支付解决方案。自2014年第二季度开始成为当前全球最大的移动支付)厂商。

支付详细流程#

1. 用户点击扫码支付图标,然后由浏览器发起支付请求,发给Gateway服务
2. Gateway收到请求之后把请求路由分发给 service-pay 服务
3. 支付服务接受请求,收到参数OrderId,然后调用订单服务的接口,查询这个订单是否是未支付的状态
4. 支付服务保存一条支付记录到数据库,并且记录状态为《未支付》
5. 支付服务向支付宝发起请求,获取支付页
6. 把支付页交给浏览器渲染,用户在浏览器中看到如下效果
7. 用户扫码或者是 用户输入账号密码确认支付
8. 同步回调:用户支付完成之后,浏览器发起请求,跳转到支付成功页面
9. 异步回调:用户支付完成之后,支付宝服务器发起请求,把支付结果通知给我们的服务,我们的服务收到请求之后,需要做修改订单状态,修改支付状态,扣减库存等操作

image-20230324165332289
image-20230324165332289

接入支付流程#

image-20230324164020243
image-20230324164020243

  1. 找到对应的开发者平台(支付宝开放平台、微信开发平台、百度开放平台、讯飞开放平台、顺丰开放平台)

  2. 首先申请相关的账号

    • 在那里申请呢? 支付宝开放平台/微信开放平台+微信商户平台

    • 申请什么账号呢?申请商户账号→ 商户id,在对应的平台上创建应用 → 应用id

    • 申请账号需要什么资料呢?

      需要提交公司的营业执照,法人代表等身份信息,保证公司的合法性

  3. 绑定商户账号和应用ID,并配置接口访问的秘钥以及下载配置商户

  4. 确定需要接入支付的类别

    • 刷脸支付

    • 扫码支付

    • APP支付

    • 手机网站支付

    • 电脑网站支付

  5. 查看你要接入的支付类别相对应的接口文档

  6. 编写代码

沙箱使用说明#

由于我们目前在开发的时候,没有商户账号,也没有营业执照等,所以暂时没有条件在支付宝上申请到对应的账号与AppId,不过支付宝为了协助我们开发者进行开发联调测试,推出了一个沙箱环境。

沙箱环境是协助开发者进行接口开发及主要功能联调的模拟环境,目前仅支持网页/移动应用和小程序两种应用类型。在沙箱完成接口调试后,直接修改必要的配置就可以接入正式环境。

沙箱环境地址

通过沙箱环境,我们需要获取的参数:

  1. APPID
  2. 支付宝网关地址
  3. 应用公钥
  4. 应用私钥
  5. 支付宝公钥

当然,我们也可以使用沙箱平台在线调试我们的代码。

支付宝接口说明#

我们是网站应用,所以接入电脑网站支付

在本项目中,使用到的接口有:

  1. 支付下单,获取支付页面
  2. 异步通知说明
  3. 统一收单交易查询
  4. 统一收单交易关闭接口

支付功能测试#

内网穿透#

  1. 为什么需要内网穿透?

    我们当前的支付服务,网关服务等也是运行在网络上,为什么支付宝访问不到我们的支付服务或者是网关呢?

    image-20230327115055579
    image-20230327115055579

  2. 如何解决`问题?

    image-20230327120739464
    image-20230327120739464

  3. 内网穿透工具

    • Natapp 下载地址

    • 在配置页面我们可以看到authtoken,根据authToken启动natapp客户端,进入natapp所在的目录,打开命令行输入如下命令:

      natapp -authtoken=你的authtoken

测试#

创建springboot工程,版本为2.7.17

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--导入支付宝支付sdk-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.35.71.ALL</version>
</dependency>
</dependencies>

定义启动类

@SpringBootApplication
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class, args);
}
}

定义配置类

@Configuration
public class MyAlipayConfig {
@Bean
public AlipayConfig getPublicConfig() {
String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwH5CIl0zwjtnxXSVAom43F1QQFOz30V3y/ZswtHKQzC12e1dYkxLqeq7HOVYhBK225MU1WHZWM0he8V4ISGY7nCz0zE179OS8EcNR152MMkYlp6ZjspC6TtkdY/3WDbo/lgC6GN9T1bqiQnpAYL+61DAF4v70iOXVo6vWy/2IBlmG0Nfyn67RswFgmurNa/m/3MhbH3Ncd6hUUB8GnGgjtvXXTHJPniYNZzDUCBdA1YF5wQhG5D9zEpPitM10kPVBd0eJD+uThKQ1ZyKE7Wnb1l8VXIyFEOFkGqylAtCtjhz37F/YMOSn+QLGEZsc77TYwNABneHt1nuFKle8AMhfAgMBAAECggEAEoIOpzv3GuR4JLQcIRGwsVtjOxln2ZcH32wlLdYYn/zE3kmR4T37Y+amjUsKMQgT1T9vNe7o6KAU/90ve4FYNPVxh/wcPGV80AKx2tzksoHp+zUF+D4glWOJz1vdpevlYZ86zlOkzGOObFS+EhvYqiJ4NXYoQrxMIspDWZwwNWYAsqbUl2DITYo+JkBZP6U1oeQCs09KFsoIs73tsLc7UE5JLZL/fjmigo8wJvfGrAXAQwlg0ie8oAzpjlS2hK5YhaRezw9OqgcRb/KgnPOWApAepJ86RYOQsvhiuuP9SLctYSnCOWWxP5f+zLU1Hf1l4RAlctdy255QWaC6lbDHAQKBgQDi3Xl3Ccnv8zQVkIpiNSChjiYtjDELcdqbrdCSbLdUxRvjxg6t1NPVRTI9LzjCSIRWny01pYv6Ujt2+nAW28bGiPcMPg2J+Q5G70H33g85uJ6yy4aPTyfDi0XuUwn0Xaetm/qWFR4Mrvgy2a1eKMWXMN8OpD+Ebk/CKbLsr0kv3wKBgQDGveCk0GY+NOYx791nea9mWlFdY54Zx3HWf/cTnwcDGwr51jTYBgeoiWX0WCC2s4i6awCEedkTz0QdRRze/NLC9gYRwagHFA9rUwENYFih4HPiFcz13DjN6I0pjLam1VQf37ZWJyRwXXf4J2trMBDYFz8l84IPnxQWn2m7LQF3gQKBgQCWYVzMrW5wYfQaf09bvf+9V26zLoSsI3JXU6Y4CVyVEntkRrsgOz2X12Bv8kdbcZpXmPfs4amh6rSEL4nxfQmMPOoV8WQkGzV9i8dcuJO7HUgFGKg/gqbHFiDq05x7oUEu8X/v0Fu06J6ZhnVHPxuLFtgk6nc4H686800pWx/WXQKBgQCY3Bx3x86MFBXl3M8XMnHlMJyaTu+gdlWpnN0GG2/CRL+Jb+dPLDwhtiRT7qCixa3pbDmGq016vhVuyeSt4hmdWKtMZv39C8HcU4hgqHUjdMbM4uW1SL/sJ+zDQ3aNFVHR/jh5RTvyrQGEPZWSaPLbse2hHA0yRLGnwM8K50/UgQKBgHgd0L619bDKRsBKRag4fquNn+oU1kgWX8jjpGcLcDP/Eqpc7eE7AFSkMMuRS9aL8f59gyC4uv1DyuYpx3pgiPKER82uEkeLRrLGxJ31EtmOtc9XshOEw6v1x0MqkELVwCuJmf/DiW8MYEZDN07rkrx7doxKFBV+FrZQ5oJPnoCW";
String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqnyvBrR4QWg0vY30uYToeDyZ7mq71nYVgkUKsBqaCzKoGbrGt75kWw7r+07ZHI8CqiYeQGd9krQ7TkFZ/DYF4p0k86wn21kqAILAVBW5mDkZF8xznDXX754NIcRXyDeUBuwGQ+FtH3GWtQVHAoTkv9XTW3RWonC/9z7DumDG3CajKZGUq64PnX0xttfEAIkGnGd7jgT8TQCVC6CH31XQMvOSDmkpCMNKYQn/XNpwaoV+SlXpJ8vV7a/9iThGBhXXRceilzOhK1bAB6MBFaMu67KWOV075cP3AwCHJmJyPVxzK54h/zn9wx0gEFGMAsuWaMTS6QI6VJCODKLwOdkpKQIDAQAB";
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipaydev.com/gateway.do");
alipayConfig.setAppId("2021000117621153");
alipayConfig.setPrivateKey(privateKey);
alipayConfig.setFormat("json");
alipayConfig.setAlipayPublicKey(alipayPublicKey);
alipayConfig.setCharset("UTF8");
alipayConfig.setSignType("RSA2");
return alipayConfig;
}
}

定义Controller

@RestController
public class payController {
@Autowired
MyAlipayConfig myalipayConfig;
@GetMapping("/test/page/pay")
public String testPagePay() throws AlipayApiException {
// 创建AlipayAlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
// 创建Request对象
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 创建Model对象封装请求参数
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo("cskaoyan005");
model.setTotalAmount("88.88");
model.setSubject("Iphone6 16G");
model.setProductCode("FAST_INSTANT_TRADE_PAY");
request.setNotifyUrl("https://732sw00878.zicp.fun/callback/notify");
// 给Model对象
request.setBizModel(model);
// 发起请求,获取结果
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
// 输出响应体(支付表单页面)
return response.getBody();
}
@GetMapping("/test/pay/query")
public String testPayQuery() throws AlipayApiException {
// 创建AlipayAlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
// 创建Request对象
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
// 创建Model对象封装具体的请求参数
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo("cskaoyan005");
// 给Request设置具体的请求
request.setBizModel(model);
// 发起请求并获取响应
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
// 返回响应中查询到的订单支付状态
return response.getTradeStatus();
}
@GetMapping("return/notify")
public String returnCallback(@RequestParam Map<String, String> paramsMap) {
System.out.println(paramsMap);
return "return notify success";
}
@GetMapping("/test/pay/close")
public String testClosed() throws AlipayApiException {
// 创建AlipayAlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
// 创建Request对象
AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
// 创建Model对象封装具体的请求参数
AlipayTradeCloseModel model = new AlipayTradeCloseModel();
model.setOutTradeNo("cskaoyan005");
// 给Request设置具体的请求
request.setBizModel(model);
// 发起请求并获取响应
AlipayTradeCloseResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("调用成功");
} else {
System.out.println("调用失败");
}
// 输出响应体
return response.getBody();
}
@Autowired
RedissonClient redissonClient;
@PostMapping("/callback/notify")
public String notifyCallback(@RequestParam Map<String, String> paramsMap) throws AlipayApiException {
@Autowired
RedissonClient redissonClient;
@PostMapping("/callback/notify")
public String notifyCallback(@RequestParam Map<String, String> paramsMap) throws AlipayApiException {
// 1. 调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());
// 2. 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验
if (signVerified) {
String outTradeNo = paramsMap.get("out_trade_no"); // 外部订单号
String totalAmount = paramsMap.get("total_amount"); // 交易总金额
String appId = paramsMap.get("app_id"); // appId
String tradeStatus = paramsMap.get("trade_status"); // 交易状态
// 3. 二次参数校验,校验金额,校验appid
// 根据订单id去数据库获取订单信息,以及其中的订单金额
BigDecimal dbTotalAmount = new BigDecimal("88.88");
int result = new BigDecimal(totalAmount).compareTo(dbTotalAmount);
if (!appId.equals(alipayConfig.getAppId()) || result != 0) {
return "failure";
}
// 4. 交易状态校验
if (tradeStatus != null && (tradeStatus.equals("TRADE_SUCCESS") || tradeStatus.equals("TRADE_FINISHED"))) {
// 5. 幂等性校验
String notifyId = paramsMap.get("notify_id");
String key = "pay:callback:notifyid" + notifyId;
RBucket<String> bucket = redissonClient.getBucket(key);
boolean ret = bucket.trySet(notifyId, 25, TimeUnit.HOURS);
if (!ret) {
System.out.println("failure");
return "failure";
}
// 执行业务逻辑
System.out.println("success");
return "success";
} else {
System.out.println("failure");
return "failure";
}
} else {
System.out.println("failure");
return "failure";
}
}
}

表结构设计#

CREATE TABLE `payment_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`out_trade_no` varchar(50) DEFAULT NULL COMMENT '对外业务编号,创建订单的时候生成,从订单中获取',
`order_id` varchar(50) DEFAULT NULL COMMENT '订单编号',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`payment_type` varchar(20) DEFAULT NULL COMMENT '支付类型(微信 支付宝)',
`trade_no` varchar(50) DEFAULT NULL COMMENT '交易编号,回调时生成',
`total_amount` decimal(10,2) DEFAULT NULL COMMENT '支付金额',
`subject` varchar(200) DEFAULT NULL COMMENT '交易内容,利用商品名称拼接。',
`payment_status` varchar(20) DEFAULT NULL COMMENT '支付状态',
`callback_time` datetime DEFAULT NULL COMMENT '回调时间',
`callback_content` text COMMENT '回调信息',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`is_deleted` tinyint(3) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8 COMMENT='支付信息表';

接口#

去支付#

@RequestMapping("/pay/alipay/submit/{orderId}")
@ResponseBody
public String submitOrder(@PathVariable Long orderId){
String form = alipayService.createAliPay(orderId);
// 在上面这个支付的方法中,需要做的事情有如下几个
// 1. 校验支付对应的订单状态是否为未支付,如果是已支付或已关闭,则直接返回
// 2. 保存支付记录到支付表
// 3. 调用支付宝SDK,生成支付表单(支付表单实际上就是一个支付页面,是一个html的字符串)
// 4. 返回支付表单
return form;
}

支付工厂#

在第三步调用支付宝SDK的时候,我们可以用一个接口来定义对于支付服务提供商的访问行为如下:

public interface PayHelper {
/*
预下单方法,获取交易二维码字符串,或者交易表单字符串
*/
String prePay(OrderInfoDTO orderInfo);
/*
根据订单号查询订单交易状态
*/
String queryTradeStatus(String outTradeNo);
/*
根据订单编号关闭订单交易
*/
void closeTrade(String outTradeNo);
}

这样做的好处在于:

  • 我们只需要访问PayHelper接口中定义的方法即可访问支付服务提供商的功能,代码书写简洁
  • 因为调用的是接口方法,所以我们可以有不同的接口实现类,从而可以实现,在几乎不修改代码的情况下,访问不同支付服务提供商的支付功能

比如,如果我们使用支付宝支付时,我们就可以实现针对支付宝的实现类如下,同理如果我们如果还要访问微信支付,我们还可以定义微信支付的实现类。

@Component
public class AlipayHelper implements PayHelper {
@Autowired
CsmallAlipayConfig csmallAlipayConfig;
@Autowired
AlipayClient alipayClient;
/**
* 向支付宝发起请求,生成支付页面(表单)
*/
@Override
public String prePay(OrderInfoDTO orderInfo) {
// 构建请求对象
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 设置同步回调(不需要公网可访问)
request.setReturnUrl(csmallAlipayConfig.getReturnPaymentUrl());
// 设置异步回调(公网可访问)
request.setNotifyUrl(csmallAlipayConfig.getNotifyPaymentUrl());
// 构建参数
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(orderInfo.getOutTradeNo());
model.setTotalAmount(orderInfo.getTotalAmount().toString());
model.setSubject(orderInfo.getTradeBody());
model.setProductCode("FAST_INSTANT_TRADE_PAY");
// ...此处省略了一些代码(这些代码在4.1.2中解释)
request.setBizModel(model);
// 向支付宝发起请求
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
return response.getBody();
}
/**
* 根据外部订单号 查询支付宝支付状态
*/
@Override
public String queryTradeStatus(String outTradeNo) {
String tradeStatus = "ACQ.SYSTEM_ERROR";
try {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if ("ACQ.TRADE_NOT_EXIST".equals(response.getSubCode())) {
return "ACQ.TRADE_NOT_EXIST";
}
if (response.isSuccess()) {
tradeStatus = response.getTradeStatus();
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
return tradeStatus;
}
/**
* 关闭支付宝支付记录
*/
public void closeTrade(String outTradeNo) {
AlipayTradeCloseModel model = new AlipayTradeCloseModel();
model.setOutTradeNo(outTradeNo);
AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
request.setBizModel(model);
try {
alipayClient.execute(request);
} catch (AlipayApiException e) {
e.printStackTrace();
}
}
}

如果在我们的项目中要接入不同的支付方式,很显然就需要用不同的PayHelper接口实现类对象,如何方便的获取这些PayHelper对象呢?我们可以使用前面学习过的简单工厂来实现:

public interface PayHelperFactory {
// 获取PayHelper对象
PayHelper getPayHelper(PaymentType paymentType);
}
@Getter
public enum PaymentType {
ALIPAY("支付宝"),
WEIXIN("微信" );
private String comment ;
PaymentType(String comment ){
this.comment=comment;
}
}
@Component
public class SimplePayHelperFactory implements PayHelperFactory{
@Autowired
AlipayHelper alipayHelper;
@Override
public PayHelper getPayHelper(PaymentType paymentType) {
if (PaymentType.ALIPAY.equals(paymentType)) {
return alipayHelper;
}
return null;
}
}

超时时间#

这里有一点需要注意,在上面的第3步生成表单的时候,考虑到订单超市取消功能的实现,我们需要设置支付表单的过期时间,用当前时间 - 订单创建时间:

  • 如果当前时间 - 订单创建时间 > 订单超时时间,则直接返回,不生成表单
  • 否则,指定绝对时间 = 当前时间 + 剩余的订单超时时间(订单超时时间 - (当前时间 - 订单创建时间))
// 获取当前时间
long now = new Date().getTime();
// 距离下单已经过去的时间
long orderTimeSpan = now - createTime.getTime();
long timeOutSpan = 30;
if (orderTimeSpan >= timeOutSpan * 60 * 1000) {
// 超过了订单的超时时间
return "对不起,已经超过支付时间,请重新下单";
}
String timeoutStr;
// 四舍五入求超时时间
// 求以毫秒为单位的超时时间
long timeoutRemain = timeOutSpan * 60 * 1000 - orderTimeSpan;
// 生成过期时间对应的日期格式字符串
timeoutStr = DateFormatUtils.format(new Date(now + timeoutRemain), "yyyy-MM-dd HH:mm:ss");
// 设置相对超时时间
model.setTimeExpire(timeoutStr);

支付完成#

需要说明的是,同步回调和异步回调都会携带参数,在当前项目中,我们使用同步回调来跳转到支付成功页面;使用异步回调来修改订单状态等。这也是支付宝推荐的使用方式,新版本的支付接口已经取消了同步回调的支付结果的传递。

image-20230326184921170
image-20230326184921170

同步回调#

同步回调的作用是跳转到支付成功页面。

pay-service#PayApiController

/**
* 支付成功:同步回调
*/
@RequestMapping("/pay/alipay/alipay/callback/return")
public String callBack() {
log.info("支付成功,同步回调! ");
// 同步回调给用户展示信息
return "redirect:" + "http://localhost:8000/pay/success";
}

异步回调#

异步回调是指在请求参数中传入 notify_url 参数,在用户支付成功后,支付宝服务器会按照这个异步地址使用 post 方式给 notify_url 来发送交易信息,参数示例。需要注意的是:notify_url 地址由商户自己定义保证可以正常使用外网 post 方式访问,否则是无法正常接收到异步通知的。

pay-service#PayApiController

/**
* 支付成功:异步回调
*/
@PostMapping("/pay/alipay/callback/notify")
@ResponseBody
@SneakyThrows
public String callbackNotify(@RequestParam Map<String, String> paramsMap){
log.info("支付成功,异步回调,paramMap:{}", JSON.toJSONString(paramsMap));
// 验证参数
// 验签
// 校验金额
// 校验appid
// 校验交易状态
// 幂等性校验
// 1. 调用SDK验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, alipayConfig.getAlipayPublicKey(), CsmallAlipayConfig.charset, CsmallAlipayConfig.sign_type);
// 2. 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验
if (signVerified){
String outTradeNo = paramsMap.get("out_trade_no"); // 外部订单号
String totalAmount = paramsMap.get("total_amount"); // 交易总金额
String appId = paramsMap.get("app_id"); // appId
String tradeStatus = paramsMap.get("trade_status"); // 交易状态
PaymentInfoDTO paymentInfo = payService.queryPaymentInfoByOutTradeNoAndPaymentType(outTradeNo, PaymentType.ALIPAY.name());
// 3. 二次参数校验,校验金额,校验appid
if (paymentInfo == null) return "failure";
BigDecimal dbTotalAmount = paymentInfo.getTotalAmount();
double m = Double.valueOf(totalAmount) - dbTotalAmount.doubleValue();
if (!appId.equals(alipayConfig.getAppId()) || m != 0) {
return "failure";
}
// 4. 交易状态校验
if (tradeStatus != null && (tradeStatus.equals("TRADE_SUCCESS") || tradeStatus.equals("TRADE_FINISHED"))) {
// 5. 幂等性校验
String notifyId = paramsMap.get("notify_id");
String key = RedisConst.PAY_CALL_BACK_VERFY_PREFIX + notifyId;
RBucket<String> bucket = redissonClient.getBucket(key);
// setnx
boolean ret = bucket.trySet(notifyId,RedisConst.PAY_CALL_BACK_EXPIRE_TIME, TimeUnit.SECONDS);
if (!ret) {
// 幂等标记存在,说明处理成功,重复的通知,返回success
return "success";
}
// 6. 修改支付表状态为支付成功 //修改失败的话,需要删除幂等标记
} else {
// 通知失败,返回failure
return "failure"
}
}
// 回调成功,返回success
return "success";
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

支付
https://firefly-mu-weld.vercel.app/posts/microservice-25-payment/
作者
Daisy
发布于
2026-06-14
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Daisy
Hello, I'm Daisy.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签

文章目录