Payment Issues
This page summarizes frequently asked questions related to payment transactions.
Payment Failures
What are the reasons for payment being declined?
Common decline reasons:
| Result Code | Reason | Solution |
|---|---|---|
| 005 | Insufficient funds | Use another card or add funds |
| 014 | Invalid card number | Check if card number is correct |
| 041 | Card reported lost | Contact issuing bank |
| 043 | Card stolen | Contact issuing bank |
| 051 | Insufficient funds | Add funds or use another card |
| 054 | Card expired | Use valid card |
| 057 | Transaction not permitted | Contact issuing bank |
| 061 | Exceeds limit | Lower amount or contact issuing bank |
See Transaction Result Codes for details
What to do if payment times out?
Causes:
- Network connection issues
- Slow terminal response
- Bank system busy
Handling Process:
- Don’t initiate payment again
- Query transaction status
QueryRequest request = QueryRequest.builder()
.transactionId(transactionId)
.build();
QueryResponse response = client.query(request);- Handle based on status:
SUCCESS: Payment successfulFAIL: Payment failed, can retryPROCESSING: Processing, continue waitingCLOSED: Closed, need to initiate again
How to query transaction status?
Query using transaction ID:
QueryRequest request = QueryRequest.builder()
.transactionId("TXN20250108001")
.build();
QueryResponse response = client.query(request);
System.out.println("Status: " + response.getTransactionStatus());Query using merchant request order ID:
QueryRequest request = QueryRequest.builder()
.transactionRequestId("PAY_REQ_20250108001")
.build();
QueryResponse response = client.query(request);
System.out.println("Status: " + response.getTransactionStatus());Payment Amount
What if payment amount is incorrect?
Check amount unit:
// ✅ Correct - Use smallest currency unit (cents)
SaleAmount amount = SaleAmount.builder()
.orderAmount(10000) // 100.00 USD = 10000 cents
.priceCurrency("USD")
.build();
// ❌ Wrong - Using decimal
.orderAmount(100) // Wrong: This represents 1.00 USD, not 100.00 USDCheck currency code:
- Use ISO 4217 standard codes
- Examples: USD, EUR, GBP, CNY
See Currency Codes for details
How to handle tips?
Method 1: Include tip in payment
SaleAmount amount = SaleAmount.builder()
.orderAmount(10000) // 100.00 USD
.tipAmount(1500) // 15.00 USD
.priceCurrency("USD")
.build();
SaleRequest request = SaleRequest.builder()
.amount(amount)
.build();Method 2: Adjust tip after payment
TipAdjustRequest request = TipAdjustRequest.builder()
.originalTransactionId(originalTransactionId)
.tipAmount(1500) // 15.00 USD, unit is cents
.build();What is partial authorization?
When card balance is insufficient to pay full amount, bank may approve partial amount:
Example:
- Requested amount: $100.00
- Card balance: $80.00
- Approved amount: $80.00
- Result code:
010(Partial authorization)
Handling:
if ("010".equals(response.getTransactionResultCode())) {
BigDecimal approvedAmount = response.getApprovedAmount();
BigDecimal remainingAmount = requestAmount.subtract(approvedAmount);
// Prompt user to pay remaining amount using other method
System.out.println("Authorized: " + approvedAmount);
System.out.println("Remaining: " + remainingAmount);
}Payment Methods
How to specify payment method?
Don’t specify (recommended):
// Terminal displays all available payment methods
SaleAmount amount = SaleAmount.builder()
.orderAmount(10000)
.priceCurrency("USD")
.build();
SaleRequest request = SaleRequest.builder()
.amount(amount)
.build();Specify payment method:
PaymentMethod paymentMethod = PaymentMethod.builder()
.category("CARD")
.id("VISA")
.build();
SaleAmount amount = SaleAmount.builder()
.orderAmount(10000)
.priceCurrency("USD")
.build();
SaleRequest request = SaleRequest.builder()
.amount(amount)
.paymentMethod(paymentMethod)
.build();See Payment Methods for details
How to handle EBT payments?
EBT has multiple account types:
SNAP: Food benefit accountBENEFIT: Cash benefit accountVOUCHER: Offline voucher
Initiate EBT payment:
PaymentMethod paymentMethod = PaymentMethod.builder()
.category("CARD")
.id("EBT")
.build();
SaleAmount amount = SaleAmount.builder()
.orderAmount(10000)
.priceCurrency("USD")
.build();
SaleRequest request = SaleRequest.builder()
.amount(amount)
.paymentMethod(paymentMethod)
.build();Notes:
- SNAP account can only purchase eligible food items
- Different account types have different usage restrictions
See Sub Payment Methods for details
How to handle multiple payment methods?
Scenario: Customer uses multiple cards or methods to pay
Method 1: Split payments
// First payment: Credit card $50
SaleAmount amount1 = SaleAmount.builder()
.orderAmount(5000) // 50.00 USD
.priceCurrency("USD")
.build();
SaleRequest request1 = SaleRequest.builder()
.amount(amount1)
.referenceOrderId("ORDER001-1")
.build();
// Second payment: Cash $50
// Record cash payment in POS systemMethod 2: Use partial authorization
- First try full amount payment
- If partial authorization, use other method to pay remaining amount
Card Information
How to get card information?
Available after successful payment:
QueryResponse response = client.query(request);
// Masked card number
String maskedPan = response.getMaskedPan(); // 411111******1111
// Card type
String cardType = response.getCardNetworkType(); // CREDIT/DEBIT
// Payment method
String paymentMethod = response.getPaymentMethodId(); // VISACannot get:
- Complete card number
- CVV
- PIN
How to save card information?
Don’t save sensitive information:
- ❌ Complete card number
- ❌ CVV/CVC
- ❌ PIN
- ❌ Magnetic stripe data
Can save:
- ✅ Token
- ✅ Masked card number
- ✅ Card type
- ✅ Expiration date
Use tokenization:
// SUNBAY automatically generates token
String token = response.getToken(); // tok_1234567890abcdef
// Use token for subsequent payments
SaleRequest request = SaleRequest.builder()
.token(token)
.amount(amount)
.build();Transaction Types
What’s the difference between authorization and sale?
| Feature | Authorization (Auth) | Sale |
|---|---|---|
| Funds | Frozen, not charged | Immediately charged |
| Purpose | Reserve funds | Direct collection |
| Follow-up | Need to complete or void | Can refund |
| Typical Scenarios | Hotels, car rentals | Retail, dining |
Authorization flow:
// 1. Authorization
AuthAmount authAmount = AuthAmount.builder()
.orderAmount(10000) // 100.00 USD
.priceCurrency("USD")
.build();
AuthRequest authRequest = AuthRequest.builder()
.amount(authAmount)
.build();
AuthResponse authResponse = client.auth(authRequest);
// 2. Post authorization (charge)
PostAuthAmount postAuthAmount = PostAuthAmount.builder()
.orderAmount(8000) // 80.00 USD, can be less than authorization amount
.priceCurrency("USD")
.build();
PostAuthRequest postAuthRequest = PostAuthRequest.builder()
.originalTransactionId(authResponse.getTransactionId())
.amount(postAuthAmount)
.build();When to use forced authorization?
Forced Authorization (Forced Auth) is used for:
- Already obtained authorization code via voice
- Recording offline transactions
- Special business scenarios
Usage:
ForcedAuthRequest request = ForcedAuthRequest.builder()
.amount(amount)
.authCode("AUTH123456") // Authorization code
.build();Notes:
- Need valid authorization code
- Risk borne by merchant
- Not commonly used
What is incremental authorization?
Incremental Authorization (Incremental Auth) is used to increase authorization amount:
Scenarios:
- Hotel guest extends stay
- Car rental extension
- Consumption exceeds expectations
Usage:
IncrementalAuthRequest request = IncrementalAuthRequest.builder()
.originalTransactionId(originalTransactionId)
.incrementAmount(new BigDecimal("5000")) // Increase $50
.build();Transaction Status
What are the transaction statuses?
| Status | Description | Follow-up Actions |
|---|---|---|
| INITIAL | Initial state | Wait for processing |
| PROCESSING | Processing | Continue waiting |
| SUCCESS | Successful | Can refund |
| FAIL | Failed | Can retry |
| CLOSED | Closed | Need to initiate again |
See Transaction Status for details
How to determine if transaction is successful?
Correct method:
// ✅ Use transactionStatus
if ("SUCCESS".equals(response.getTransactionStatus())) {
// Transaction successful
}
// ❌ Don't use transactionResultCode
if ("000".equals(response.getTransactionResultCode())) {
// Wrong! Result code cannot determine transaction status
}Important:
transactionStatusis the final transaction statustransactionResultCodeis only reference information
How to handle PROCESSING status?
Handling Process:
- Wait for a period (recommend 30-60 seconds)
- Query transaction status
Thread.sleep(30000); // Wait 30 seconds
QueryResponse response = client.query(request);- Handle based on status:
SUCCESS: Transaction successfulFAIL: Transaction failedPROCESSING: Continue waiting or contact technical support
Don’t:
- Immediately initiate payment again
- Query frequently (recommend at least 10 second intervals)