Overview
The PayMaya SDK provides a sandbox environment for comprehensive testing of all payment flows without processing real transactions. This guide covers testing strategies, test cards, and common scenarios.
Sandbox Environment Setup
Before testing, ensure your client is configured for the sandbox environment:
val payMayaCheckoutClient = PayMayaCheckout. newBuilder ()
. clientPublicKey ( "pk-test-xxxxxxxxxxxxxxxx" ) // Sandbox public key
. environment (PayMayaEnvironment.SANDBOX)
. logLevel (LogLevel.DEBUG) // Verbose logging for testing
. build ()
Sandbox transactions do not process real payments. Never use production keys in test environments.
Test Credit Cards
PayMaya provides test credit card numbers for different scenarios in the sandbox environment.
Successful Payment Cards
Use these cards to test successful payment flows:
Visa Card Number: 4123450131001381
Expiry: Any future date
CVV: Any 3 digits
Result: Payment success
Mastercard Card Number: 5123450000000008
Expiry: Any future date
CVV: Any 3 digits
Result: Payment success
Failed Payment Cards
Test payment failure scenarios:
Declined Card Card Number: 4571736000000075
Result: Payment declined/failed
Test Card Details
For all test cards, you can use:
Expiry Date: Any future date (e.g., 12/25, 01/26)
CVV: Any 3-digit number (e.g., 123, 456)
Cardholder Name: Any name
Test cards only work in the sandbox environment. They will be rejected in production.
Testing Checkout Flow
Basic Checkout Test
fun testCheckoutPayment () {
// 1. Create checkout request
val request = CheckoutRequest (
totalAmount = TotalAmount (
value = BigDecimal ( "100.00" ),
currency = "PHP"
),
buyer = null , // Optional for testing
items = listOf (
Item (
name = "Test Product" ,
quantity = 1 ,
amount = TotalAmount (
value = BigDecimal ( "100.00" ),
currency = "PHP"
)
)
),
requestReferenceNumber = "TEST- ${ System. currentTimeMillis () } " ,
redirectUrl = RedirectUrl (
success = "http://success.test" ,
failure = "http://failure.test" ,
cancel = "http://cancel.test"
),
metadata = null
)
// 2. Start checkout
payMayaCheckoutClient. startCheckoutActivityForResult ( this , request)
}
// 3. Handle result
override fun onActivityResult (requestCode: Int , resultCode: Int , data : Intent ?) {
payMayaCheckoutClient. onActivityResult (requestCode, resultCode, data )?. let { result ->
when (result) {
is PayMayaCheckoutResult.Success -> {
Log. d (TAG, "✓ Test passed: Successful payment" )
Log. d (TAG, "Checkout ID: ${ result.checkoutId } " )
}
is PayMayaCheckoutResult.Cancel -> {
Log. d (TAG, "Test: Payment canceled" )
}
is PayMayaCheckoutResult.Failure -> {
Log. e (TAG, "✗ Test failed: ${ result.exception.message } " )
}
}
}
}
Test Scenarios
Launch checkout with valid test card (4123450131001381)
Complete the payment flow
Verify PayMayaCheckoutResult.Success is received
Confirm checkoutId is present
Check payment status shows PAYMENT_SUCCESS
Launch checkout with declined card (4571736000000075)
Complete the payment flow
Verify PayMayaCheckoutResult.Failure is received
Check exception details
Launch checkout
Press back button or close the activity
Verify PayMayaCheckoutResult.Cancel is received
Create checkout with invalid data (e.g., negative amount)
Attempt to start checkout
Verify BadRequestException is thrown or returned
Testing Pay With PayMaya
Single Payment Test
fun testSinglePayment () {
val request = SinglePaymentRequest (
totalAmount = TotalAmount (
value = BigDecimal ( "50.00" ),
currency = "PHP"
),
requestReferenceNumber = "PWPM- ${ System. currentTimeMillis () } " ,
redirectUrl = RedirectUrl (
success = "http://success.test" ,
failure = "http://failure.test" ,
cancel = "http://cancel.test"
),
metadata = null
)
payWithPayMayaClient. startSinglePaymentActivityForResult ( this , request)
}
override fun onActivityResult (requestCode: Int , resultCode: Int , data : Intent ?) {
PayWithPayMaya. onActivityResult (requestCode, resultCode, data )?. let { result ->
when (result) {
is SinglePaymentResult.Success -> {
Log. d (TAG, "✓ Single payment successful: ${ result.paymentId } " )
}
is SinglePaymentResult.Cancel -> {
Log. d (TAG, "Single payment canceled" )
}
is SinglePaymentResult.Failure -> {
Log. e (TAG, "✗ Single payment failed: ${ result.exception } " )
}
}
}
}
Wallet Link Test
fun testWalletLink () {
val request = CreateWalletLinkRequest (
requestReferenceNumber = "LINK- ${ System. currentTimeMillis () } " ,
redirectUrl = RedirectUrl (
success = "http://success.test" ,
failure = "http://failure.test" ,
cancel = "http://cancel.test"
),
metadata = null
)
payWithPayMayaClient. startCreateWalletLinkActivityForResult ( this , request)
}
override fun onActivityResult (requestCode: Int , resultCode: Int , data : Intent ?) {
PayWithPayMaya. onActivityResult (requestCode, resultCode, data )?. let { result ->
when (result) {
is CreateWalletLinkResult.Success -> {
Log. d (TAG, "✓ Wallet link created: ${ result.linkId } " )
}
is CreateWalletLinkResult.Cancel -> {
Log. d (TAG, "Wallet link creation canceled" )
}
is CreateWalletLinkResult.Failure -> {
Log. e (TAG, "✗ Wallet link failed: ${ result.exception } " )
}
}
}
}
Testing Vault (Card Tokenization)
fun testCardTokenization () {
payMayaVaultClient. startTokenizeCardActivityForResult ( this )
}
override fun onActivityResult (requestCode: Int , resultCode: Int , data : Intent ?) {
payMayaVaultClient. onActivityResult (requestCode, resultCode, data )?. let { result ->
when (result) {
is PayMayaVaultResult.Success -> {
Log. d (TAG, "✓ Card tokenized successfully" )
Log. d (TAG, "Token ID: ${ result.paymentTokenId } " )
Log. d (TAG, "Issuer: ${ result.issuer } " )
Log. d (TAG, "State: ${ result.state } " )
Log. d (TAG, "Created: ${ result.createdAt } " )
}
is PayMayaVaultResult.Cancel -> {
Log. d (TAG, "Card tokenization canceled" )
}
}
}
}
Vault Test Cases
Valid Card Entry
Enter test card details
Verify successful tokenization
Confirm token ID is returned
Invalid Card
Enter invalid card number
Verify validation error is shown
Confirm user can retry
Cancel Tokenization
Start tokenization flow
Press back or cancel
Verify PayMayaVaultResult.Cancel is received
Testing Payment Status
fun testPaymentStatusCheck (checkoutId: String ) {
lifecycleScope. launch (Dispatchers.IO) {
val result = payMayaCheckoutClient. checkPaymentStatus (checkoutId)
withContext (Dispatchers.Main) {
when (result) {
is CheckPaymentStatusResult.Success -> {
Log. d (TAG, "✓ Status check successful: ${ result.status } " )
verifyExpectedStatus (result.status)
}
is CheckPaymentStatusResult.Failure -> {
Log. e (TAG, "✗ Status check failed: ${ result.exception } " )
}
else -> {
Log. d (TAG, "Status check canceled" )
}
}
}
}
}
private fun verifyExpectedStatus (actual: PaymentStatus ) {
// Verify status matches expected value
val expected = PaymentStatus.PAYMENT_SUCCESS
if (actual == expected) {
Log. d (TAG, "✓ Status matches expected: $expected " )
} else {
Log. e (TAG, "✗ Status mismatch. Expected: $expected , Got: $actual " )
}
}
Exception Handling Tests
class PaymentExceptionTests {
@Test
fun testBadRequestException () {
// Test with invalid API key
val client = PayMayaCheckout. newBuilder ()
. clientPublicKey ( "invalid-key" )
. environment (PayMayaEnvironment.SANDBOX)
. build ()
// Attempt checkout - should fail with BadRequestException
// Verify exception is caught and handled properly
}
@Test
fun testNetworkFailure () {
// Disable network
// Attempt payment
// Verify appropriate exception handling
}
@Test
fun testInvalidAmount () {
// Create request with invalid amount
// Verify validation or exception
}
}
Common Testing Scenarios
Happy Path Test successful payment flow with valid test cards from start to finish.
Network Issues Test behavior during network interruptions or timeouts.
User Cancellation Verify proper handling when users cancel at different stages.
Invalid Input Test with invalid data to ensure proper validation and error messages.
Back Button Test pressing back button during payment flow.
App Backgrounding Test behavior when app goes to background during payment.
Multiple Requests Test handling of rapid successive payment requests.
Status Polling Test status checking for various payment states.
Automated Testing Example
class PayMayaCheckoutTest {
private lateinit var client: PayMayaCheckout
@Before
fun setup () {
client = PayMayaCheckout. newBuilder ()
. clientPublicKey ( "pk-test-xxxxxxxxxxxxxxxx" )
. environment (PayMayaEnvironment.SANDBOX)
. logLevel (LogLevel.DEBUG)
. build ()
}
@Test
fun testSuccessfulCheckout () {
val request = createTestCheckoutRequest ()
// Use ActivityScenario or Espresso to test UI flow
val scenario = launchActivity < CheckoutActivity >()
// Perform checkout
// Enter test card details
// Verify success result
scenario. onActivity { activity ->
val result = /* get result from activity */
assertTrue (result is PayMayaCheckoutResult.Success)
}
}
@Test
fun testCheckoutCancellation () {
// Test cancellation flow
}
@Test
fun testInvalidRequest () {
// Test with invalid data
}
private fun createTestCheckoutRequest (): CheckoutRequest {
return CheckoutRequest (
totalAmount = TotalAmount ( BigDecimal ( "100.00" ), "PHP" ),
items = listOf (
Item ( "Test Item" , 1 , TotalAmount ( BigDecimal ( "100.00" ), "PHP" ))
),
requestReferenceNumber = "TEST- ${ System. currentTimeMillis () } " ,
redirectUrl = RedirectUrl (
success = "http://success.test" ,
failure = "http://failure.test" ,
cancel = "http://cancel.test"
)
)
}
}
Testing Checklist
Before going to production, ensure you’ve tested:
Debugging Tips
Set LogLevel.VERBOSE to see complete HTTP request and response details: . logLevel (LogLevel.VERBOSE)
Filter logcat by PayMaya tag: adb logcat | grep -i paymaya
Ensure you’re using sandbox keys (prefix: pk-test-) in test environment.
Use Android Studio’s Network Profiler or device developer options to simulate poor network conditions.
Best Practices
Separate Test Keys Use dedicated test keys for development separate from shared sandbox keys.
Test All Paths Test success, failure, and cancellation paths for complete coverage.
Automated Tests Create automated tests for regression testing during development.
Log Everything Use verbose logging during testing to understand SDK behavior.
Edge Cases Test edge cases like rapid clicks, back button, and app lifecycle events.
Document Results Keep track of test results and any issues discovered.
Resources
Next Steps
Environment Setup Configure production environment after testing
Result Handling Handle payment results in your app