欢迎光临
我们一直在努力

基于Nacos的SpringBoot微服务项目接入支付宝沙箱支付全流程

一、重要参数

  • 支付宝的公钥和私钥(最好使用txt保存 or 下载)
  • 支付的网关
  • 支付的APPID
  • 建议先看完博客在开始跟着步骤操作

二、支付宝开发者平台配置沙箱环境

地址:支付宝开放平台

1.登录支付宝开放平台

2.点击进入控制台

3.进入沙箱页面

4.这里能看到 APPID和支付宝网关和密钥

注意:这里我们使用自定义密钥,需要使用支付宝开放平台密钥工具

三、下载支付宝开放平台密钥工具

地址:小程序文档 – 支付宝文档中心

1.根据自己的系统选择不同的版本(我的是Windows)

2.下载并安装完成后我们生成自己的公钥和私钥

3.生成应用公钥和应用私钥

注意:最好点击打开文件位置进行保存

4.返回沙箱应用页面-切换自定义密钥-点击查看

5.查看 -> 复制刚刚生成的应用公钥到下图 -> 保存

四、设置内网穿透环境

地址:NATAPP-内网穿透 基于ngrok的免费企业级国内高速内网映射工具

1.注册和登录

2.登录后,选择免费隧道

3.我的隧道可以看到刚刚免费购买隧道

注意:authtoken,下面步骤需要使用authtoken

4.修改服务器端口号

点击右侧:配置

修改为自己后端,端口号。(由于本项目是springcloud项目,端口号是9000,配置在gateway-service)

五、下载NATAPP客户端

地址:客户端下载 – NATAPP-内网穿透 基于ngrok的免费企业级国内高速内网映射工具

1.根据自己的系统选择下载

2.下载后并解压后

3.运行natapp.exe

注意:不要双击运行,需要使用命令行窗口进行运行

4.使用命令行窗口进行运行

natapp.exe -authtoken=你的authtoken

3)启动成功如下图

注意:这里的地址会因为网络环境的不同,每次启动该地址都会变化 其实这里就是将项目运行的地址代理到了内网穿透的地址,但是因为每次启动都会变化,所以还是建议使用 localhost:9000

六、后端实现

1. 依赖(pom.xml)

<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.157.ALL</version>
</dependency>

2. 配置文件(application.yml)

alipay:
server-url: https://openapi-sandbox.dl.alipaydev.com/gateway.do
app-id: 你的APPID
private-key: 你的应用私钥
alipay-public-key: 你的应用私钥
return-url: http://localhost:5173/pay-result
notify-url: https://xxx.ngrok-free.app/api/alipay/notify
sign-type: RSA2
charset: UTF-8
format: json

3. 配置类

@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
private String serverUrl;
private String appId;
private String privateKey;
private String alipayPublicKey;
private String returnUrl;
private String notifyUrl;
private String signType;
private String charset;
private String format;
}

4. 订单实体

@Data
public class PayOrder {
private Long id;
private String orderNo; // 订单号,全局唯一
private BigDecimal amount; // 金额,必须用BigDecimal
private String subject; // 商品名称
private String status; // WAIT_PAY / SUCCESS / CLOSED
private String tradeNo; // 支付宝交易号
private String buyerId; // 买家支付宝ID
private LocalDateTime createTime;
private LocalDateTime payTime;
}
CREATE TABLE `pay_order` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`order_no` VARCHAR(32) NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`subject` VARCHAR(255) DEFAULT '',
`status` VARCHAR(20) NOT NULL DEFAULT 'WAIT_PAY',
`trade_no` VARCHAR(64) DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5. 核心支付服务

@Slf4j
@Service
@RequiredArgsConstructor
public class AlipayService {

private final AlipayConfig alipayConfig;

/**
* 发起支付,返回HTML表单(前端直接渲染即可跳转)
*/
public String createPayment(Long orderId) throws AlipayApiException {
String orderNo = "PAY" + System.currentTimeMillis()
+ String.format("%04d", new Random().nextInt(10000));

AlipayClient client = new DefaultAlipayClient(
alipayConfig.getServerUrl(),
alipayConfig.getAppId(),
alipayConfig.getPrivateKey(),
alipayConfig.getFormat(),
alipayConfig.getCharset(),
alipayConfig.getAlipayPublicKey(),
alipayConfig.getSignType()
);

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(alipayConfig.getNotifyUrl());
request.setReturnUrl(alipayConfig.getReturnUrl());

AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(orderNo);
model.setTotalAmount("0.01"); // 金额,字符串格式
model.setSubject("校园超市购物");
model.setProductCode("FAST_INSTANT_TRADE_PAY");

request.setBizModel(model);
return client.pageExecute(request).getBody();
}

/**
* 异步通知处理
*/
public String handleNotify(Map<String, String> params) {
String tradeStatus = params.get("trade_status");
String orderNo = params.get("out_trade_no");
String tradeNo = params.get("trade_no");

if ("TRADE_SUCCESS".equals(tradeStatus)) {
// TODO: 更新订单状态为 SUCCESS,记录 tradeNo
log.info("订单 {} 支付成功, 交易号: {}", orderNo, tradeNo);
}
return "success"; // 必须返回success,否则支付宝会重复通知
}
}

6. Controller

@RestController
@RequestMapping("/api/alipay")
@RequiredArgsConstructor
public class AlipayController {

private final AlipayService alipayService;

/** 发起支付: GET /api/alipay/pay?id=100 */
@GetMapping(value = "/pay", produces = "text/html")
public String pay(@RequestParam Long id) throws AlipayApiException {
return alipayService.createPayment(id);
}

/** 异步通知: POST /api/alipay/notify */
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterNames().asIterator()
.forEachRemaining(k -> params.put(k, request.getParameter(k)));
return alipayService.handleNotify(params);
}
}

7. 前端调用

<script setup>
import axios from 'axios';
const payForm = ref('');

async function goPay() {
const res = await axios.get('/api/alipay/pay', {
params: { id: 100 },
responseType: 'text'
});
payForm.value = res.data; // 渲染后自动提交跳转收银台
}
</script>

<template>
<el-button @click="goPay">去支付</el-button>
<div v-html="payForm"></div>
</template>

七、踩坑清单

问题 原因 解决
签名校验失败 私钥不是 PKCS8 格式 openssl pkcs8 -topk8 转换
异步通知收不到 notify-url 支付宝访问不了 用 ngrok/frp 内网穿透
金额校验失败 用了 double 导致精度丢失         必须用 BigDecimal。toString()
页面提示系统繁忙 沙箱环境波动 等一下重试,或检查 APPID

重点:传道授业解惑老师:被代码敲的艾伦

赞(0)
未经允许不得转载:171主机测评 » 基于Nacos的SpringBoot微服务项目接入支付宝沙箱支付全流程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址