Skip to Content
ResourcesFAQPayment Issues

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 CodeReasonSolution
005Insufficient fundsUse another card or add funds
014Invalid card numberCheck if card number is correct
041Card reported lostContact issuing bank
043Card stolenContact issuing bank
051Insufficient fundsAdd funds or use another card
054Card expiredUse valid card
057Transaction not permittedContact issuing bank
061Exceeds limitLower 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:

  1. Don’t initiate payment again
  2. Query transaction status
QueryRequest request = QueryRequest.builder() .transactionId(transactionId) .build(); QueryResponse response = client.query(request);
  1. Handle based on status:
    • SUCCESS: Payment successful
    • FAIL: Payment failed, can retry
    • PROCESSING: Processing, continue waiting
    • CLOSED: 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 USD

Check 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 account
  • BENEFIT: Cash benefit account
  • VOUCHER: 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 system

Method 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(); // VISA

Cannot 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?

FeatureAuthorization (Auth)Sale
FundsFrozen, not chargedImmediately charged
PurposeReserve fundsDirect collection
Follow-upNeed to complete or voidCan refund
Typical ScenariosHotels, car rentalsRetail, 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?

StatusDescriptionFollow-up Actions
INITIALInitial stateWait for processing
PROCESSINGProcessingContinue waiting
SUCCESSSuccessfulCan refund
FAILFailedCan retry
CLOSEDClosedNeed 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:

  • transactionStatus is the final transaction status
  • transactionResultCode is only reference information

How to handle PROCESSING status?

Handling Process:

  1. Wait for a period (recommend 30-60 seconds)
  2. Query transaction status
Thread.sleep(30000); // Wait 30 seconds QueryResponse response = client.query(request);
  1. Handle based on status:
    • SUCCESS: Transaction successful
    • FAIL: Transaction failed
    • PROCESSING: Continue waiting or contact technical support

Don’t:

  • Immediately initiate payment again
  • Query frequently (recommend at least 10 second intervals)
Last updated on