Skip to main content

Overview

The PayMaya Android SDK returns payment results through the standard Android Activity result mechanism. Each payment type has its own result class with Success, Cancel, and Failure states.

Activity Result Pattern

All payment methods follow the same pattern:
  1. Start payment activity with startXxxActivityForResult()
  2. Override onActivityResult() in your Activity
  3. Call the appropriate SDK onActivityResult() method
  4. Handle the returned result object

Checkout Results

PayMayaCheckoutResult

The Checkout payment returns one of three result types:
PayMayaCheckoutResult.kt
sealed class PayMayaCheckoutResult(open val checkoutId: String?) {
    
    /**
     * Success result of the Checkout payment.
     */
    class Success internal constructor(
        override val checkoutId: String
    ) : PayMayaCheckoutResult(checkoutId)

    /**
     * Canceled result of the Checkout payment.
     */
    class Cancel internal constructor(
        override val checkoutId: String? = null
    ) : PayMayaCheckoutResult(checkoutId)

    /**
     * Failed result of the Checkout payment.
     */
    class Failure internal constructor(
        override val checkoutId: String? = null,
        val exception: Exception
    ) : PayMayaCheckoutResult(checkoutId)
}

Handling Checkout Results

Example from README.md
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    payMayaCheckoutClient.onActivityResult(requestCode, resultCode, data)?.let {
        processCheckoutResult(it)
    }
}

private fun processCheckoutResult(result: PayMayaCheckoutResult) {
    when (result) {
        is PayMayaCheckoutResult.Success -> {
            val message = "Success, checkoutId: ${result.checkoutId}"
            Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }

        is PayMayaCheckoutResult.Cancel -> {
            val message = "Canceled, checkoutId: ${result.checkoutId}"
            Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }

        is PayMayaCheckoutResult.Failure -> {
            val message = "Failure, checkoutId: ${result.checkoutId}, exception: ${result.exception}"
            if (result.exception is BadRequestException) {
                Log.d(TAG, (result.exception as BadRequestException).error.toString())
            }
            Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
    }
}
The checkoutId is always available in Success results, but may be null in Cancel or Failure results if the payment didn’t progress far enough to receive an ID.

Pay With PayMaya Results

Single Payment Result

SinglePaymentResult.kt
sealed class SinglePaymentResult : PayWithPayMayaResult() {

    /**
     * Success result of the Single Payment.
     */
    class Success internal constructor(
        val paymentId: String
    ) : SinglePaymentResult()

    /**
     * Canceled result of the Single Payment.
     */
    class Cancel internal constructor(
        val paymentId: String? = null
    ) : SinglePaymentResult()

    /**
     * Failed result of the Single Payment.
     */
    class Failure internal constructor(
        val paymentId: String? = null,
        val exception: Exception
    ) : SinglePaymentResult()
}

Handling Single Payment Results

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    PayWithPayMaya.onActivityResult(requestCode, resultCode, data)?.let { result ->
        when (result) {
            is SinglePaymentResult.Success -> {
                // Payment completed successfully
                val paymentId = result.paymentId
                Log.d(TAG, "Payment successful: $paymentId")
                updateOrderStatus(paymentId, "PAID")
            }
            
            is SinglePaymentResult.Cancel -> {
                // User canceled the payment
                Log.d(TAG, "Payment canceled: ${result.paymentId}")
                showMessage("Payment was canceled")
            }
            
            is SinglePaymentResult.Failure -> {
                // Payment failed
                Log.e(TAG, "Payment failed: ${result.exception.message}")
                handlePaymentFailure(result.exception)
            }
        }
    }
}
CreateWalletLinkResult.kt
sealed class CreateWalletLinkResult : PayWithPayMayaResult() {

    /**
     * Success result of the creation of the wallet link.
     */
    class Success internal constructor(
        val linkId: String
    ) : CreateWalletLinkResult()

    /**
     * Canceled result of the creation of the wallet link.
     */
    class Cancel internal constructor(
        val linkId: String? = null
    ) : CreateWalletLinkResult()

    /**
     * Failed result of the creation of the wallet link.
     */
    class Failure internal constructor(
        val linkId: String? = null,
        val exception: Exception
    ) : CreateWalletLinkResult()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    PayWithPayMaya.onActivityResult(requestCode, resultCode, data)?.let { result ->
        when (result) {
            is CreateWalletLinkResult.Success -> {
                // Wallet link created successfully
                val linkId = result.linkId
                saveWalletLinkId(linkId)
                Log.d(TAG, "Wallet link created: $linkId")
            }
            
            is CreateWalletLinkResult.Cancel -> {
                Log.d(TAG, "Wallet link creation canceled")
            }
            
            is CreateWalletLinkResult.Failure -> {
                Log.e(TAG, "Wallet link creation failed: ${result.exception.message}")
            }
        }
    }
}

Vault Results

PayMayaVaultResult

PayMayaVaultResult.kt
sealed class PayMayaVaultResult {

    /**
     * Success result of the card tokenization process.
     */
    class Success internal constructor(
        val paymentTokenId: String,
        val state: String,
        val createdAt: String,
        val updatedAt: String,
        val issuer: String
    ) : PayMayaVaultResult()

    /**
     * Canceled result of the card tokenization process.
     */
    object Cancel : PayMayaVaultResult()
}
Vault does not return a Failure result. Card validation errors are shown in the vault UI.

Handling Vault Results

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    payMayaVaultClient.onActivityResult(requestCode, resultCode, data)?.let { result ->
        when (result) {
            is PayMayaVaultResult.Success -> {
                // Card tokenized successfully
                val tokenId = result.paymentTokenId
                val cardIssuer = result.issuer
                
                Log.d(TAG, "Token ID: $tokenId")
                Log.d(TAG, "Issuer: $cardIssuer")
                Log.d(TAG, "State: ${result.state}")
                
                // Store token for future payments
                savePaymentToken(tokenId)
            }
            
            is PayMayaVaultResult.Cancel -> {
                Log.d(TAG, "Card tokenization canceled")
                showMessage("Card was not added")
            }
        }
    }
}

Exception Handling

The SDK provides several exception types for different failure scenarios:

BadRequestException

Thrown when the server returns 400 (Bad Request) or 401 (Unauthorized):
BadRequestException.kt
class BadRequestException internal constructor(
    message: String,
    val error: BaseError
) : Exception(message)
Common causes:
  • Invalid API key
  • Malformed request data
  • Missing required fields
  • Invalid authentication
when (result) {
    is PayMayaCheckoutResult.Failure -> {
        when (val exception = result.exception) {
            is BadRequestException -> {
                // Access detailed error information
                Log.e(TAG, "Bad request: ${exception.message}")
                Log.e(TAG, "Error details: ${exception.error}")
                showMessage("Payment request was invalid")
            }
            else -> handleOtherException(exception)
        }
    }
}

HttpException

Thrown for other HTTP errors:
HttpException.kt
class HttpException internal constructor(
    val code: Int,
    message: String
) : Exception(message)
Common causes:
  • Network connectivity issues
  • Server errors (500+)
  • Timeout errors
when (val exception = result.exception) {
    is HttpException -> {
        Log.e(TAG, "HTTP error ${exception.code}: ${exception.message}")
        when (exception.code) {
            500, 502, 503 -> showMessage("Server error. Please try again later.")
            408 -> showMessage("Request timeout. Please check your connection.")
            else -> showMessage("An error occurred: ${exception.message}")
        }
    }
}

InternalException

Thrown when an unexpected response is received:
InternalException.kt
class InternalException internal constructor(
    message: String,
    cause: Throwable? = null
) : Exception(message, cause)

PaymentFailedException

Thrown when a payment explicitly fails:
PaymentFailedException.kt
object PaymentFailedException : Exception()

Complete Exception Handling Example

private fun handlePaymentFailure(exception: Exception) {
    when (exception) {
        is BadRequestException -> {
            Log.e(TAG, "Bad request: ${exception.error}")
            showError("Invalid payment request. Please check your details.")
        }
        
        is HttpException -> {
            Log.e(TAG, "HTTP ${exception.code}: ${exception.message}")
            showError("Connection error. Please try again.")
        }
        
        is InternalException -> {
            Log.e(TAG, "Internal error: ${exception.message}", exception.cause)
            showError("An unexpected error occurred.")
        }
        
        is PaymentFailedException -> {
            Log.e(TAG, "Payment failed")
            showError("Payment could not be processed.")
        }
        
        else -> {
            Log.e(TAG, "Unknown error: ${exception.message}")
            showError("An error occurred: ${exception.message}")
        }
    }
}

Automatic Status Checking

When users close a payment activity (e.g., by pressing Back), the SDK automatically checks the payment status if a transaction ID was already received.

Checkout Status Mapping

For Checkout payments:
  • PAYMENT_SUCCESSPayMayaCheckoutResult.Success
  • AUTH_FAILED or PAYMENT_FAILEDPayMayaCheckoutResult.Failure
  • Other statuses → PayMayaCheckoutResult.Cancel

Pay With PayMaya Status Mapping

For Pay With PayMaya:
  • PAYMENT_SUCCESSSinglePaymentResult.Success
  • AUTH_FAILED or PAYMENT_FAILEDSinglePaymentResult.Failure
  • Other statuses → SinglePaymentResult.Cancel

Best Practices

Always Check for Null

The SDK onActivityResult() returns nullable results. Always use safe calls or null checks.

Store Transaction IDs

Save checkoutId or paymentId immediately for reconciliation and support.

Handle All States

Implement handling for Success, Cancel, and Failure to provide clear user feedback.

Log Exceptions

Always log exception details for debugging and monitoring.

User Feedback

Provide clear, user-friendly messages for each result type.

Status Verification

For critical transactions, verify payment status on your backend.

Next Steps

Payment Status

Learn how to manually check payment status

Testing Guide

Test your result handling with sandbox environment

Build docs developers (and LLMs) love