注意:沙箱网关地址和生产环境网关地址不一致,如果是沙箱环境需要配置沙箱网关地址
前言
沙箱环境问题
使用的sdk版本比较新的话(这版本多新才算新啊?我也不知道),很大可能不是你的问题,而是沙箱环境自身的问题,有条件的话建议在生产环境做测试,没条件就听天由命吧…
官方文档对于该问题的描述
我这里使用的版本:
<!-- 支付宝 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.40.82.ALL</version>
</dependency>
解决方式
把沙箱配置改成生产环境的配置
支付宝对接
注入AlipayConfig
package com.explain.framework.config;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author QiuXin
* @since 2025/3/19
*/
@Configuration
@ConfigurationProperties(prefix = "alipay")
public class TestAliPayConfig extends AlipayConfig {
@Bean
public AlipayClient alipayClient() throws AlipayApiException {
return new DefaultAlipayClient(getAlipayConfig(1));
}
/**
* 通过不同方式获取支付宝支付配置
*
* @param configurationMode 配置模式:1-密钥加签;2-服务器证书地址加签;<s>3-证书内容加签(证书内容过多已废弃)</s>
* @return {@link AlipayConfig}
*/
public AlipayConfig getAlipayConfig(int configurationMode) {
AlipayConfig alipayConfig = new AlipayConfig();
if (configurationMode == 1) {
alipayConfig.setAppId(super.getAppId());
alipayConfig.setPrivateKey(super.getPrivateKey());
alipayConfig.setAlipayPublicKey(super.getAlipayPublicKey());
} else {
alipayConfig.setAppId(super.getAppId());
alipayConfig.setPrivateKey(super.getPrivateKey());
alipayConfig.setAppCertPath(super.getAppCertPath());
alipayConfig.setAlipayPublicCertPath(super.getAlipayPublicCertPath());
alipayConfig.setRootCertPath(super.getRootCertPath());
}
return alipayConfig;
}
}
配置文件
#支付宝
alipay:
#应用id
appId:
#应用私钥(需要使用支付宝开放平台密钥工具的格式转换功能转换为PKCS8格式,不然可能报错私钥格式不对)
private-key:
#支付宝公钥
alipay-publicKey:
#证书文件地址加签
# app-cert-path: C:\Users\xxx(你的服务器或本地路径)\appCertPublicKey_2021005130641045.crt
# alipay-public-cert-path: C:\Users\xxx(你的服务器或本地路径)\alipayCertPublicKey_RSA2.crt
# root-cert-path: C:\Users\xxx(你的服务器或本地路径)\alipayRootCert.crt
后端使用
package com.explain.framework.alipay.service.impl;
import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.domain.RefundGoodsDetail;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.explain.framework.alipay.service.AliPayService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.alipay.api.AlipayConstants.CHARSET;
import static com.alipay.api.AlipayConstants.SIGN_TYPE_RSA2;
/**
* @author QiuXin
* @since 2025/3/19
*/
@Slf4j
@Service
public class AliPayServiceImpl implements AliPayService {
@Resource
private AlipayClient alipayClient;
@Resource
private AlipayConfig alipayConfig;
@Override
public void tradePagePay(HttpServletResponse response) {
log.info("收到支付宝下单请求");
// 发送请求的 Request类
AlipayTradePagePayRequest request = getAlipayTradePagePayRequest();
// 执行请求,拿到响应的结果,返回给浏览器
String form = "";
try {
// 调用SDK生成表单
form = alipayClient.pageExecute(request).getBody();
log.info("下单form信息:{}", form);
} catch (AlipayApiException e) {
log.error("支付宝下单失败:{}", e.getMessage());
}
response.setContentType("text/html;charset=utf-8");
try {
// 直接将完整的表单html输出到页面
response.getWriter().write(form);
response.getWriter().flush();
response.getWriter().close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
@NotNull
private static AlipayTradePagePayRequest getAlipayTradePagePayRequest() {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 支付宝异步回调地址
// 注意:需要关闭此接口的权限校验或放行网关白名单等一切可能导致请求被拦截、或无法访问的处理
// 并且需要运行在公网可以访问的地方,如果没有就需要一个内网穿透工具,推荐一个可以免费使用的:https://i.cpolar.com/m/5qAl
request.setNotifyUrl("https://5860f010.r31.cpolar.top/alipay/notify");
JSONObject bizContent = new JSONObject();
// 我们自己生成的订单编号
bizContent.set("out_trade_no", "777777");
// 订单的总金额
bizContent.set("total_amount", 0.01);
// 支付的名称
bizContent.set("subject", "水果16");
// 固定配置
bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
return request;
}
@Override
public void alipayNotify(HttpServletRequest request, HttpServletResponse response) {
// 获取消息内容
Map<String, String> params = getParams(request);
// params参数示例
// String gmtCreate = "2025-03-20 15:27:07";
// String charset = "utf-8";
// String gmtPayment = "2025-03-20 15:27:11";
// String notifyTime = "2025-03-20 15:27:12";
// String subject = "??16";
// String sign = "oIguKFbITbx5McZELQksdsadsaWKQjxedDSADSADSADHY8NLmIBwkn/ASDsadsadsadsadsdsa0pf5frnfazDk3SP89Rxd3NeXSY/pnFYCEK7SH/5pby+tHTBPXV4tds5a45d6saGY9qmnVnWGxBcyOJmMLoQnFzBQAChwanD87IWSiJJf3uPuqTaMhJW4Yrb3837G9AGlkTdroTDc9KgY1+wi4XD2tz1f3IGkaFvKyUBN2WPtCGTRL3NVRiVIzaRTlG7Ojh2zTaVsuAly06xOO+rRCXkYP6VS9dq2iw18XCqsapdospa23==";
// String merchantAppId = "123123123";
// String buyerOpenId = "0d32s1a2ds1a2d12sa3-dsads-ds1a2d1sa2d12";
// String invoiceAmount = "0.01";
// String version = "1.0";
// String notifyId = "d12s3a1d2s3a1d23sa12d3sa";
// String fundBillList = "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]";
// String notifyType = "trade_status_sync";
// String outTradeNo = "777777";
// String totalAmount = "0.01";
// // 交易状态
// String tradeStatus = "TRADE_SUCCESS";
// // 支付宝生成的订单号
// String tradeNo = "2025111111111111111111";
// String authAppId = "";
// String receiptAmount = "0.01";
// String pointAmount = "0.00";
// String buyerPayAmount = "0.01";
// String appId = "123123123123153153";
// String signType = "RSA2";
// String sellerId = "265256156156";
//验签
boolean signResult = false;
try {
signResult = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), CHARSET, SIGN_TYPE_RSA);
} catch (AlipayApiException e) {
log.error("支付宝异步通知消息验签异常:{}", e.getMessage());
}
if (signResult) {
log.info("收到支付宝发送的支付结果通知");
String outTradeNo = request.getParameter("out_trade_no");
log.info("交易流水号:{}", outTradeNo);
//交易状态
String tradeStatus = new String(request.getParameter("tradeStatus").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
//交易成功
switch (tradeStatus) {
case "TRADE_SUCCESS":
//支付成功的业务逻辑,比如落库,开vip权限等
log.info("订单:{} 交易成功", outTradeNo);
break;
case "TRADE_FINISHED":
log.info("交易结束,不可退款");
//其余业务逻辑
break;
case "TRADE_CLOSED":
log.info("超时未支付,交易已关闭,或支付完成后全额退款");
//其余业务逻辑
break;
case "WAIT_BUYER_PAY":
log.info("交易创建,等待买家付款");
//其余业务逻辑
break;
default:
break;
}
try {
//返回success给支付宝,表示消息我已收到,不用重调
response.getWriter().write("success");
} catch (IOException e) {
log.error(e.getMessage());
}
} else {
//返回fail给支付宝,表示消息我没收到,请重试
try {
response.getWriter().write("fail");
} catch (IOException e) {
log.error("支付宝异步通知消息返回fail给支付宝异常:{}", e.getMessage());
}
}
}
@Override
public String tradeRefund(HttpServletRequest request) {
log.info("收到支付宝订单退款请求");
// 构造请求参数以调用接口
AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
// 设置商户订单号
model.setOutTradeNo("777777");
// 设置支付宝交易号
model.setTradeNo("2025032022001430951415807279");
// 设置退款金额
model.setRefundAmount("0.01");
// 设置退款原因说明
model.setRefundReason("正常退款");
// 设置退款请求号
model.setOutRequestNo("777777");
// 设置退款包含的商品列表信息
List<RefundGoodsDetail> refundGoodsDetail = new ArrayList<>();
RefundGoodsDetail refundGoodsDetail0 = new RefundGoodsDetail();
refundGoodsDetail0.setOutSkuId("outSku_01");
refundGoodsDetail0.setOutItemId("outItem_01");
refundGoodsDetail0.setGoodsId("apple-01");
refundGoodsDetail0.setRefundAmount("19.50");
List<String> outCertificateNoList = new ArrayList<>();
outCertificateNoList.add("202407013232143241231243243423");
refundGoodsDetail0.setOutCertificateNoList(outCertificateNoList);
refundGoodsDetail.add(refundGoodsDetail0);
model.setRefundGoodsDetail(refundGoodsDetail);
// 设置查询选项
List<String> queryOptions = new ArrayList<>();
queryOptions.add("refund_detail_item_list");
model.setQueryOptions(queryOptions);
alipayRequest.setBizModel(model);
try {
String responseBody = alipayClient.execute(alipayRequest).getBody();
log.info("退款信息:{}", responseBody);
} catch (AlipayApiException e) {
log.error("退款失败,原因:{}", e.getMessage());
return "退款失败,原因:" + e.getMessage();
}
return "退款成功";
}
@NotNull
private static Map<String, String> getParams(HttpServletRequest request) {
Map<String, String> params = new HashMap<>(0);
//获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
Map<String, String[]> parameterMap = request.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String[] values = entry.getValue();
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(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
params.put(entry.getKey(), valueStr);
}
return params;
}
}
其他有用的参考文档:
保姆级教程:SpringBoot 对接支付宝完成扫码支付,完整流程梳理!