Performance Optimization
This document introduces performance optimization recommendations for merchants integrating SUNBAY API.
Use SDK
Recommended to Use Official SDK
The SDK provided by SUNBAY has built-in connection pools, retry mechanisms, timeout controls, and other optimizations. It is strongly recommended to use the SDK rather than calling the API directly.
SDK has already handled for you:
- ✅ HTTP connection pool management
- ✅ Connection reuse
- ✅ Reasonable timeout settings
- ✅ Automatic retry mechanism
See SDK Documentation for usage details.
Cache Query Results
For data that doesn’t change frequently, you can cache query results to reduce API calls.
Data Suitable for Caching
- ✅ Merchant configuration information
- ✅ Payment method list
- ✅ Completed transaction details (status is final)
Data Not Suitable for Caching
- ❌ Transaction status in processing (
PROCESSING,INITIAL) - ❌ Real-time balance information
Caching Example
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
public class TransactionCache {
// Cache completed transactions (final states)
private final Cache<String, TransactionDetail> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000)
.build();
/**
* Get transaction details (with cache)
*/
public TransactionDetail getTransaction(String transactionId) {
return cache.get(transactionId, id -> {
// Cache miss, call API to query
QueryResponse response = client.query(
QueryRequest.builder()
.transactionId(id)
.build()
);
TransactionDetail detail = response.getData();
// Only cache final state transactions
if (isFinalStatus(detail.getTransactionStatus())) {
return detail;
}
return null; // Don't cache non-final states
});
}
private boolean isFinalStatus(String status) {
return "SUCCESS".equals(status) ||
"FAIL".equals(status) ||
"CLOSED".equals(status);
}
}Asynchronous Webhook Processing
Webhook processing should return 200 response quickly to avoid timeouts.
SUNBAY’s Webhook request timeout is 10 seconds; if timeout occurs, it will trigger retry.
Recommended Approach: Return Immediately, Process Asynchronously
@RestController
@RequestMapping("/webhook")
public class WebhookController {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
@PostMapping("/payment")
public ResponseEntity<String> handlePaymentWebhook(
@RequestBody String payload,
@RequestHeader("X-SUNBAY-Signature") String signature) {
// 1. Quick signature verification
if (!webhookValidator.verifySignature(payload, signature)) {
return ResponseEntity.status(401).body("Invalid signature");
}
// 2. Return 200 immediately
executorService.submit(() -> {
try {
// 3. Process event asynchronously
processWebhookEvent(payload);
} catch (Exception e) {
log.error("Failed to process webhook", e);
}
});
return ResponseEntity.ok("OK");
}
}Use Message Queue (Optional)
For high-concurrency scenarios, you can use message queues:
@PostMapping("/payment")
public ResponseEntity<String> handlePaymentWebhook(
@RequestBody String payload,
@RequestHeader("X-SUNBAY-Signature") String signature) {
// Verify signature
if (!webhookValidator.verifySignature(payload, signature)) {
return ResponseEntity.status(401).body("Invalid signature");
}
// Put into message queue
messageQueue.send("webhook-events", payload);
// Return immediately
return ResponseEntity.ok("OK");
}Batch Queries
If you need to query multiple transactions, you can use batch queries to reduce the number of API calls.
public class BatchQueryService {
/**
* Batch query transactions
*/
public List<TransactionDetail> batchQuery(List<String> transactionIds) {
List<TransactionDetail> results = new ArrayList<>();
// Query in batches (max 50 per batch)
for (int i = 0; i < transactionIds.size(); i += 50) {
List<String> batch = transactionIds.subList(
i,
Math.min(i + 50, transactionIds.size())
);
// Concurrent queries
List<CompletableFuture<TransactionDetail>> futures = batch.stream()
.map(id -> CompletableFuture.supplyAsync(() ->
queryTransaction(id), executorService))
.collect(Collectors.toList());
// Wait for all queries to complete
futures.forEach(future -> {
try {
results.add(future.get());
} catch (Exception e) {
log.error("Query failed", e);
}
});
}
return results;
}
}Avoid Frequent Polling
Don’t poll transaction status frequently; this wastes resources and may trigger rate limiting.
❌ Not Recommended: Frequent Polling
// Don't do this
while (true) {
QueryResponse response = client.query(request);
if (isFinalStatus(response.getData().getTransactionStatus())) {
break;
}
Thread.sleep(1000); // Query once per second
}✅ Recommended: Use Webhook
Configure Webhook URL to let SUNBAY proactively notify transaction results:
SaleRequest request = SaleRequest.builder()
.amount(amount)
.notifyUrl("https://your-domain.com/webhook/payment") // Configure Webhook
.build();✅ Recommended: Exponential Backoff Polling
If you must poll, use exponential backoff algorithm:
public TransactionDetail waitForResult(String transactionId, int maxRetries) {
int retries = 0;
long waitTime = 2000; // Initial wait 2 seconds
while (retries < maxRetries) {
QueryResponse response = client.query(
QueryRequest.builder()
.transactionId(transactionId)
.build()
);
String status = response.getData().getTransactionStatus();
if (isFinalStatus(status)) {
return response.getData();
}
// Wait then retry
Thread.sleep(waitTime);
// Exponentially increase wait time: 2s, 4s, 8s, 16s...
waitTime = Math.min(waitTime * 2, 60000); // Max wait 60 seconds
retries++;
}
throw new TimeoutException("Transaction timeout");
}Performance Optimization Checklist
SDK Usage
- Use official SDK (built-in optimizations)
- Reuse SDK client instances (don’t create new instance each time)
Caching Strategy
- Cache query results for final state transactions
- Cache merchant configuration information
- Don’t cache transaction status in processing
Webhook Processing
- Webhook processing returns 200 quickly
- Use asynchronous processing or message queues
- Implement idempotency control
Query Optimization
- Use Webhook instead of frequent polling
- Use exponential backoff algorithm when polling
- Control concurrency when batch querying