支付宝沙箱环境支付集成
前置准备
在开始之前,需要完成以下准备工作:
- 注册沙箱支付宝账号,并获取到对应的商家信息和买家信息。
- 为了测试回调接口,下载并配置
natapp工具。完成实名认证后,购买免费隧道服务,将本地端口映射到公网地址(此端口为后端服务器运行的端口)。启动命令示例:natapp.exe -authtoken=xxxx。
后端实现
1. 添加依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.110.ALL</version>
</dependency>
2. 配置文件
在 application.yml 中添加以下内容:
alipay:
appId: xxx
privateKey: xxx
publicKey: xxx
notifyUrl: http://your-public-url/notify
returnUrl: http://localhost:8080/return
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
format: JSON
charset: UTF-8
signType: RSA2
3. 配置类
创建一个用于加载配置的类:
@Component
@ConfigurationProperties(prefix = "alipay")
@Data
public class AlipayConfig {
private String appId;
private String privateKey;
private String publicKey;
private String gatewayUrl;
private String notifyUrl;
private String returnUrl;
private String format;
private String charset;
private String signType;
}
4. 初始化客户端
通过 Spring 的 @Configuration 注解初始化 AlipayClient:
@Configuration
public class AlipayClientConfig {
@Bean
public AlipayClient alipayClient(AlipayConfig config) {
return new DefaultAlipayClient(
config.getGatewayUrl(),
config.getAppId(),
config.getPrivateKey(),
config.getFormat(),
config.getCharset(),
config.getPublicKey(),
config.getSignType()
);
}
}
5. 控制器与服务实现
以下是支付接口与回调处理的实现:
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Autowired
private PaymentService paymentService;
@PostMapping("/pay")
public ResponseEntity<String> pay(@RequestBody PaymentRequest request) throws Exception {
String formHtml = paymentService.createPayment(request);
return ResponseEntity.ok(formHtml);
}
@PostMapping("/notify")
public ResponseEntity<String> notify(HttpServletRequest request) {
try {
Map<String, String> params = parseRequestParams(request);
if (params.isEmpty()) {
return ResponseEntity.badRequest().body("failure");
}
String result = paymentService.handleNotify(params);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("failure");
}
}
private Map<String, String> parseRequestParams(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (values.length > 0) {
params.put(key, values[0]);
}
});
return params;
}
}
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private AlipayClient alipayClient;
@Autowired
private AlipayConfig alipayConfig;
public String createPayment(PaymentRequest request) {
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", request.getOrderNo());
bizContent.put("total_amount", request.getAmount());
bizContent.put("subject", request.getSubject());
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(alipayConfig.getReturnUrl());
alipayRequest.setNotifyUrl(alipayConfig.getNotifyUrl());
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
alipayRequest.setBizContent(bizContent.toString());
try {
return alipayClient.pageExecute(alipayRequest).getBody();
} catch (AlipayApiException e) {
throw new RuntimeException("支付请求生成失败", e);
}
}
@Override
public String handleNotify(Map<String, String> params) {
boolean isValid = verifySignature(params);
if (!isValid) {
return "fail";
}
// 处理业务逻辑,如更新订单状态等
System.out.println("Order ID: " + params.get("out_trade_no"));
return "success";
}
private boolean verifySignature(Map<String, String> params) {
try {
return AlipaySignature.rsaCheckV1(
params,
alipayConfig.getPublicKey(),
alipayConfig.getCharset(),
alipayConfig.getSignType()
);
} catch (AlipayApiException e) {
throw new RuntimeException("签名验证失败", e);
}
}
}
6. 安全配置
确保支付相关接口不受安全限制:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/payment/**", "/notify").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable();
}
}
