Java Integration with Alipay Sandbox for QR Code Payments

Environment Prerequisites

Access the Alipay Developer Platform and navigate to the Sandbox environment through the console. Locate the Sandbox Applications section to retrieve essential credentials including the Application ID, Gateway URL, and cryptographic keys. Download the Sandbox Wallet application for mobile testing and note the test merchant/buyer account credentials provided in the Sandbox Accounts section.

SDK Integration

Include the Alipay SDK dependency in your Maven configuration:

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

Configuration Setup

Create a configuration class to centralize payment parameters:

@Configuration
public class PaymentGatewayConfig {
    
    @Value("${alipay.gateway-url:https://openapi-sandbox.dl.alipaydev.com/gateway.do}")
    private String serverUrl;
    
    @Value("${alipay.app-id}")
    private String applicationId;
    
    @Value("${alipay.merchant-private-key}")
    private String merchantPrivateKey;
    
    @Value("${alipay.alipay-public-key}")
    private String platformPublicKey;
    
    @Value("${alipay.charset:UTF-8}")
    private String charset;
    
    @Value("${alipay.notify-url}")
    private String webhookEndpoint;
    
    @Value("${alipay.return-url}")
    private String redirectUrl;
    
    @Bean
    public AlipayClient alipayClient() {
        return new DefaultAlipayClient(
            serverUrl,
            applicationId,
            merchantPrivateKey,
            "json",
            charset,
            platformPublicKey,
            "RSA2"
        );
    }
    
    // Getters for configuration properties
    public String getWebhookEndpoint() { return webhookEndpoint; }
    public String getRedirectUrl() { return redirectUrl; }
    public String getCharset() { return charset; }
}

Paymant Request Implementation

Implement the payment controller to generate QR codes for sandbox transactions:

@RestController
@RequestMapping("/api/payment")
@Slf4j
public class PaymentController {

    @Autowired
    private AlipayClient alipayClient;
    
    @Autowired
    private PaymentGatewayConfig config;
    
    @Autowired
    private TransactionRepository transactionRepository;

    @PostMapping("/create")
    public ResponseEntity<String> initiatePayment(@RequestBody PaymentRequest request) {
        try {
            String transactionId = java.util.UUID.randomUUID().toString();
            
            AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
            alipayRequest.setReturnUrl(config.getRedirectUrl());
            alipayRequest.setNotifyUrl(config.getWebhookEndpoint());
            
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", transactionId);
            bizContent.put("total_amount", request.getAmount().toString());
            bizContent.put("subject", request.getProductName());
            bizContent.put("body", request.getDescription());
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
            
            alipayRequest.setBizContent(bizContent.toString());
            
            String paymentForm = alipayClient.pageExecute(alipayRequest).getBody();
            
            // Persist transaction record
            TransactionRecord record = new TransactionRecord();
            record.setOrderId(transactionId);
            record.setAmount(request.getAmount());
            record.setStatus("PENDING");
            transactionRepository.save(record);
            
            return ResponseEntity.ok(paymentForm);
            
        } catch (AlipayApiException e) {
            log.error("Payment initialization failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                   .body("Payment gateway error");
        }
    }
}

Asynchronous Notification Handler

Proces payment confirmations through the webhook endpoint:

@Controller
@Slf4j
public class PaymentWebhookController {

    @Autowired
    private PaymentGatewayConfig config;
    
    @Autowired
    private TransactionRepository transactionRepository;

    @PostMapping("/webhook/alipay")
    public ResponseEntity<String> handleNotification(HttpServletRequest request) {
        try {
            Map<String, String> parameters = extractParameters(request);
            
            boolean signatureValid = AlipaySignature.rsaCheckV1(
                parameters,
                config.getPlatformPublicKey(),
                config.getCharset(),
                "RSA2"
            );
            
            if (!signatureValid) {
                log.warn("Invalid signature received");
                return ResponseEntity.badRequest().body("Invalid signature");
            }
            
            String tradeStatus = parameters.get("trade_status");
            String orderNumber = parameters.get("out_trade_no");
            String transactionAmount = parameters.get("total_amount");
            
            log.info("Processing notification for order: {}, status: {}", 
                     orderNumber, tradeStatus);
            
            if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
                transactionRepository.findByOrderId(orderNumber).ifPresent(record -> {
                    record.setStatus("COMPLETED");
                    record.setPaymentTime(LocalDateTime.now());
                    transactionRepository.save(record);
                });
            }
            
            return ResponseEntity.ok("success");
            
        } catch (Exception e) {
            log.error("Webhook processing error", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                   .body("Processing error");
        }
    }
    
    private Map<String, String> extractParameters(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        Map<String, String[]> parameterMap = request.getParameterMap();
        
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            StringBuilder valueStr = new StringBuilder();
            
            for (int i = 0; i < values.length; i++) {
                valueStr.append(values[i]);
                if (i < values.length - 1) valueStr.append(",");
            }
            
            result.put(name, valueStr.toString());
        }
        
        return result;
    }
}

Synchronous Return Handler

Handle user redirection after payment completion:

@Controller
@RequestMapping("/payment")
public class PaymentReturnController {

    @GetMapping("/success")
    public String paymentComplete(Model model) {
        model.addAttribute("message", "Transaction completed successfully");
        model.addAttribute("timestamp", LocalDateTime.now());
        return "payment/success";
    }
}

Create the corresponding view template src/main/resources/templates/payment/success.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Payment Confirmation</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
        .success-icon { color: #52c41a; font-size: 48px; }
        .container { max-width: 600px; margin: 0 auto; }
    </style>
</head>
<body>
    <div class="container">
        <div class="success-icon">✓</div>
        <h1>Payment Successful</h1>
        <p th:text="${message}">Your transaction has been processed</p>
        <p>Processed at: <span th:text="${timestamp}"></span></p>
        <a href="/">Return to Homepage</a>
    </div>
</body>
</html>

Local Development Tunneling

For webhook testing during development, expose your local server using tunneling tools. Install a tunneling client and establish an HTTP tunnel to your application port:

# Example using ngrok or similar tools
ngrok http 8080

Configure the generated public URL (e.g., https://abc123.ngrok.io/webhook/alipay) as your notification endpoint in the Alipay Sandbox settings. Ansure the tunnel remains active during testing to receive asynchronous callbacks from the Alipay servers.

Cryptographic Key Generation

Use the Alipay Key Tool to generate RSA2 key pairs for sandbox authentication. Upload the public key to your Sandbox Application settings and store the private key securely in your application's configuration management system or environment variables, never committing it to version control.

Tags: alipay java Spring Boot Payment Integration Sandbox Environment

Posted on Tue, 11 Aug 2026 16:27:59 +0000 by bagnallc