Integrating Alipay Sandbox Payment in a Java Backend with Ngrok Tunneling
This guide explains how to configure Alipay sandbox credentials, expose a local Java Spring Boot service via a tunneling tool, add the Alipay SDK dependency, set up configuration files, implement payment and notification endpoints, and handle common initialization errors for seamless backend payment integration.
Prerequisite: create an Alipay sandbox account, obtain the sandbox public and private keys, gateway URL, APPID, and SDK information.
Because the service runs locally, use a tunneling tool (e.g., ngrok) to expose a public address so Alipay can call back to your server.
Add the Alipay SDK dependency to your Maven project:
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.9.28.ALL</version>
</dependency>Configure the application (e.g., application.yml ) with server port and Alipay credentials:
server:
port: 9090
alipay:
# appid
appId: 9021000135634074
# 应用私钥
appPrivateKey: MIIEvQIBADAN...(省略)
# 支付宝公钥
alipayPublicKey: MIIBIjAN...(省略)
# 回调接口地址
notifyUrl: http://v6tqyw.natappfree.cc/alipay/notifyCreate a configuration class to bind these properties and initialize the Alipay SDK:
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
private String appId;
private String appPrivateKey;
private String alipayPublicKey;
private String notifyUrl;
@PostConstruct
public void init() {
Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi-sandbox.dl.alipaydev.com";
config.signType = "RSA2";
config.appId = this.appId;
config.merchantPrivateKey = this.appPrivateKey;
config.alipayPublicKey = this.alipayPublicKey;
config.notifyUrl = this.notifyUrl;
Factory.setOptions(config);
System.out.println("=======支付宝SDK初始化成功=======");
}
}Define a value object for the payment request parameters:
public class PayVO {
private String out_trade_no; // 商户订单号 必填
private String subject; // 订单名称 必填
private BigDecimal total_amount; // 付款金额 必填
private String body; // 商品描述 可空
}Implement the payment controller that builds an AlipayClient , creates an AlipayTradePagePayRequest , sets the business JSON, executes the request, and writes the returned HTML form to the browser:
@RestController
@RequestMapping("/alipay")
@Transactional(rollbackFor = Exception.class)
public class AliPayController {
@Resource
AliPayConfig aliPayConfig;
@Resource
private ShopOrderDao shopOrderMapper;
private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
private static final String FORMAT = "JSON";
private static final String CHARSET = "utf-8";
private static final String SIGN_TYPE = "RSA2";
@GetMapping("/pay")
public void pay(PayVO aliPay, HttpServletResponse httpResponse) throws Exception {
AlipayClient alipayClient = new DefaultAlipayClient(
GATEWAY_URL,
aliPayConfig.getAppId(),
aliPayConfig.getAppPrivateKey(),
FORMAT,
CHARSET,
aliPayConfig.getAlipayPublicKey(),
SIGN_TYPE);
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(aliPayConfig.getNotifyUrl());
aliPay.setOut_trade_no(UUID.randomUUID().toString());
String out_trade_no = aliPay.getOut_trade_no();
BigDecimal total_amount = aliPay.getTotal_amount();
String subject = aliPay.getSubject();
String body = aliPay.getBody();
request.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\",\"total_amount\":\"" + total_amount + "\",\"subject\":\"" + subject + "\",\"body\":\"" + body + "\",\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
request.setReturnUrl("http://localhost:9090/hello/pay");
String form = "";
try {
form = alipayClient.pageExecute(request).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
httpResponse.setContentType("text/html;charset=" + CHARSET);
httpResponse.getWriter().write(form);
httpResponse.getWriter().flush();
httpResponse.getWriter().close();
}
}Test the endpoint with a URL such as http://localhost:9090/alipay/pay?subject=测试商品&total_amount=1000 . The browser will display Alipay’s sandbox login page where you can enter the test credentials and confirm payment.
Implement the asynchronous notification endpoint to receive Alipay callbacks, verify the signature, extract parameters, and update order status in the database:
@PostMapping("/notify") // 必须是 POST 接口
public String payNotify(HttpServletRequest request) throws Exception {
if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
System.out.println("=========支付宝异步回调========");
Map
params = new HashMap<>();
Map
requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
params.put(name, request.getParameter(name));
}
System.out.println(params);
String tradeNo = params.get("out_trade_no");
String gmtPayment = params.get("gmt_payment");
if (Factory.Payment.Common().verifyNotify(params)) {
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"));
ShopOrder order = new ShopOrder();
order.setId(tradeNo);
order.setStatus("1");
order.setZhhifuTime(gmtPayment);
shopOrderMapper.save(order);
}
}
return "success";
}To avoid the NullPointerException shown in the original article, ensure the SDK is initialized in AliPayConfig.init() as demonstrated above.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.