• Docs
  • APIs
Sign in
Terminal3 Payments
Integration Overview
  • Widget API
    • Digital Goods
    • Subscription-Tokidoki
      • Create Subscription
      • Change Subscription
      • Cancel Subscription
      • Custom Settings
    • Virtual Currency
    • Offerwall
  • Checkout API
    • Onetime payment
    • Subscription
  • Direct API
    • Brick Direct
      • Create form
      • Charge
      • Subscription
      • Apply 3D Secure
      • Pingback
      • Error Codes
      • Sandbox
    • Mobiamo Direct
      • Payment flows
      • Mobiamo Carriers
    • MINT Direct
  • GamePay API
  • Modules and Platforms
  • SDKs
    • GamePay SDK - iOS
    • GamePay SDK - Android
    • AppPay SDK
    • Smart TV
    • Unity
Sandbox
  • Test payment
  • Pingback tool
  • Sandbox Reporting
Guides
  • Project Review
    • Technical Review
    • Website Standards
  • SpiderPipe
    • PayPal
    • Bitcoin Coinbase
    • Alipay
    • Wechat Pay
Payment Methods
Reference
  • Client-side Callback
  • Currency codes
  • Lang codes
  • Payment system codes
  • Product Inventory Management
    • Digital Goods
    • Virtual Currency
  • Pingback
    • Custom Parameters
  • PCI DSS Compliance
  • Signature Calculation
  • Widgets
    • Widget Errors
Terminal3 E-Commerce Shop
Accepting Payments
  • Shop Page
  • Virtual Currency Widget
  • Fast Checkout
Product Delivery
  • Uploaded Keys
  • File for Download
  • Webhooks
Pingbacks
Signature
☰

1 - Set up SDK

Server-side

First, you need a Terminal3 account. Register now.

This integration requires your Terminal3 project credentials:

  • Project Key: Your public project identifier
  • Secret Key: Your private project key (keep this secure on your server)

Client-side

To install the SDK, add gamepaysdk to your app/build.gradle file:

// In your app-level build.gradle
dependencies {
// Your existing dependencies
    implementation 'com.terminal3:gamepaysdk:1.2.1'
}

Next, add our registry to your project’s settings.gradle:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url "https://registry-sdk.terminal3.com/repository/maven-public/"
        }
    }
}

Required Permissions

Add these permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Define your theme

Add your custom theme to your themes.xml file:

<style name="Theme.Custom" parent="Theme.GamePaySDK">
</style>

Required Activities

Declare the payment activity in your AndroidManifest.xml:

Note: Please set android:screenOrientation="portrait" for a better payment user experience.

<activity
            android:name="com.terminal3.gamepaysdk.core.PaymentSelectionActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:launchMode="singleTask"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.Custom"
            android:windowSoftInputMode="stateVisible|adjustResize|adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="gpdemo" android:host="gamepay-redirect" />
            </intent-filter>
        </activity>
    

Setup return deep link

For certain payment methods, users must complete their payment in an external browser or app. To bring them back to your app afterward, configure the clientURL with your app’s deeplink.

You need to define the deeplink in your AndroidManifest.xml and also set it in the UnifiedRequest :

  • AndroidManifest.xml:
<activity
            android:name="com.terminal3.gamepaysdk.core.PaymentSelectionActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:launchMode="singleTask"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.Custom"
            android:windowSoftInputMode="stateVisible|adjustResize|adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="gpdemo" android:host="gamepay-redirect" />
            </intent-filter>
        </activity>
  • UnifiedRequest:

      request.returnUrl = "gpdemo://gamepay-redirect"
    

Make sure to replace the sample scheme (gpdemo) with your app’s custom scheme.

2 - Enable Payment Methods

Your payment methods are configured through your Terminal3 Dashboard. Ensure you have enabled the payment methods you want to support:

  • Brick: Credit and debit cards
  • Google Pay: Google’s digital wallet
  • PayAlto: Local payment methods based on user location


3 - Build Payment Request

Import Required Classes


import com.terminal3.gamepaysdk.core.UnifiedRequest

Create Payment Request & Handle GPAPI events

class MainActivityViewModel : ViewModel(), IGPAPIEventHandler {

		init {
		// Initialize and register this class as the GPApi event handler
        GPApi.setEventHandler(this)
    }

    fun createPaymentRequest(): UnifiedRequest {
        val request = UnifiedRequest()

		// Configure project keys
        request.pwProjectKey = getPwProjectKey()
        request.pwSecretKey = getPwProjectKey()

		// Configure merchant details
        request.merchantName = Constants.MERCHANT_NAME
        request.merchantTermsOfServiceURL = Constants.TERMS_OF_SERVICE_URL
        request.merchantPrivacyPolicyURL = Constants.PRIVACY_POLICY_URL
        request.returnUrl = "gpdemo://gamepay-redirect"

		// Configure payment request
        request.amount = chargeAmount
        request.currency = chargeCurrency
        request.userId = uid
        request.userEmail = Constants.USER_EMAIL
        request.itemId = Constants.ITEM_GEM_ID
        request.itemName = Constants.ITEM_NAME
        request.countryCode = country
        request.timeout = 60000
        request.signVersion = 3

		// Add custom parameters
        request.addCustomParam("referral_code", "AUB8712364")
        request.addCustomParam("history_id", "87782627")
        
        return request
    }
    
        // Handle responses from GPApi
		override fun onResp(resp: BaseResp?) {
	    when (resp?.resultCode) {

        // Payment completed successfully
        ResponseCode.SUCCESSFUL -> {
            paymentStatus = PaymentStatus.Success("Payment completed successfully!")
        }

        // Payment is still being processed on GPApi side
        ResponseCode.PROCESSING -> {
            paymentStatus = PaymentStatus.Processing("Payment is being processed!")
        }

        // Payment failed
        ResponseCode.FAILED -> {
            paymentStatus = PaymentStatus.Failed("Payment failed!")
        }

        // User cancelled the payment
        ResponseCode.CANCEL -> {
            paymentStatus = PaymentStatus.Cancelled("Payment cancelled!")
        }

        // Error returned from GPApi
        ResponseCode.ERROR -> {
            val errMsg = resp.data.getStringExtra("error_message") ?: "An error occurred!"
            paymentStatus = PaymentStatus.Unknown(errMsg)
        }

        // Merchant side is processing the payment (e.g., Brick flow)
        ResponseCode.MERCHANT_PROCESSING -> {
            val serviceType = resp.data.getStringExtra(GPApi.KEY_SERVICE_TYPE) ?: ""
            if (GPApi.SERVICE_TYPE_BRICK == serviceType) {

                // Extract Brick payment details
                val amount = resp.data.getDoubleExtra(GPApi.KEY_BRICK_CHARGE_AMOUNT, chargeAmount)
                val email = resp.data.getStringExtra(GPApi.KEY_BRICK_EMAIL).orEmpty()
                val token = resp.data.getStringExtra(GPApi.KEY_BRICK_TOKEN).orEmpty()
                val refId = resp.data.getStringExtra(GPApi.KEY_BRICK_REF_ID).orEmpty()
                val chargeId = resp.data.getStringExtra(GPApi.KEY_BRICK_CHARGE_ID).orEmpty()
                val brickSecureToken = resp.data.getStringExtra(GPApi.KEY_BRICK_SECURE_TOKEN).orEmpty()
                val firstName = resp.data.getStringExtra(GPApi.KEY_BRICK_CARD_HOLDER_FIRST_NAME).orEmpty()
                val lastName = resp.data.getStringExtra(GPApi.KEY_BRICK_CARD_HOLDER_LAST_NAME).orEmpty()

                // Extract custom parameters
                val customParams = resp.data.getBundleExtra(GPApi.KEY_CUSTOM_PARAMS) ?: Bundle()
                val referralCode = customParams.getString("referral_code")
                val historyId = customParams.getString("history_id")
                val customParams = mapOf(
                        "referral_code" to referralCode,
                        "history_id" to historyId
                    )

                processPayment(token, amount, firstName, lastName,
                                email, refId, chargeId, brickSercureToken,
                                customParams,
                                context)
            }
        }
    }
}
}


4 - Add Payment Methods

4.1 - Add Brick (Credit Cards)

Step 1: Handle One-time Token

The SDK automatically obtains a one-time token during the payment process. To handle this token, you need to implement the IGPAPIEventHandler interface and extract the token from the BaseResp data.

class MainActivityViewModel : ViewModel(), IGPAPIEventHandler {

		override fun onResp(resp: BaseResp?) {
	    when (resp?.resultCode) {

        // Payment completed successfully
        ResponseCode.SUCCESSFUL -> {
            paymentStatus = PaymentStatus.Success("Payment completed successfully!")
        }

        // Payment is still being processed on GPApi side
        ResponseCode.PROCESSING -> {
            paymentStatus = PaymentStatus.Processing("Payment is being processed!")
        }

        // Payment failed
        ResponseCode.FAILED -> {
            paymentStatus = PaymentStatus.Failed("Payment failed!")
        }

        // User cancelled the payment
        ResponseCode.CANCEL -> {
            paymentStatus = PaymentStatus.Cancelled("Payment cancelled!")
        }

        // Error returned from GPApi
        ResponseCode.ERROR -> {
            val errMsg = resp.data.getStringExtra("error_message") ?: "An error occurred!"
            paymentStatus = PaymentStatus.Unknown(errMsg)
        }

        // Merchant side is processing the payment (e.g., Brick flow)
        ResponseCode.MERCHANT_PROCESSING -> {
            val serviceType = resp.data.getStringExtra(GPApi.KEY_SERVICE_TYPE) ?: ""
            if (GPApi.SERVICE_TYPE_BRICK == serviceType) {

                // Extract Brick payment details
                val token = resp.data.getStringExtra(GPApi.KEY_BRICK_TOKEN).orEmpty()
                val amount = resp.data.getDoubleExtra(GPApi.KEY_BRICK_CHARGE_AMOUNT, chargeAmount)
                val email = resp.data.getStringExtra(GPApi.KEY_BRICK_EMAIL).orEmpty()
                
                val firstName = resp.data.getStringExtra(GPApi.KEY_BRICK_CARD_HOLDER_FIRST_NAME).orEmpty()
                val lastName = resp.data.getStringExtra(GPApi.KEY_BRICK_CARD_HOLDER_LAST_NAME).orEmpty()
                
                val refId = resp.data.getStringExtra(GPApi.KEY_BRICK_REF_ID).orEmpty()
                val chargeId = resp.data.getStringExtra(GPApi.KEY_BRICK_CHARGE_ID).orEmpty()
                val brickSecureToken = resp.data.getStringExtra(GPApi.KEY_BRICK_SECURE_TOKEN).orEmpty()

                // Extract custom parameters
                val customParams = resp.data.getBundleExtra(GPApi.KEY_CUSTOM_PARAMS) ?: Bundle()
                val referralCode = customParams.getString("referral_code")
                val historyId = customParams.getString("history_id")
                val customParams = mapOf(
                        "referral_code" to referralCode,
                        "history_id" to historyId
                    )

                processPayment(token, amount, firstName, lastName,
                                email, refId, chargeId, brickSercureToken,
                                customParams,
                                context)
            }
        }
    }
}
}

Then you can use the one-time token to create a charge.

Step 2: Create a charge

After obtaining a one-time token, send a request from your backend to Terminal3 server to create a charge.

POST request to: https://api.paymentwall.com/api/brick/charge

Parameters and description can be found here.

Step 3: Handle Charge Response

After creating a charge request with the one-time token, you need to handle the response and pass it to the SDK for processing.

private fun handleChargeRequest() {
    try {
        // Execute your charge request to your backend/payment server
        val response = executeChargeRequest() // Your implementation
        
        // Get the response body as string
        val responseBody = getResponseBody(conn.inputStream)
        Log.i("CHARGE_RESPONSE", responseBody)
        
        // Pass the response to the SDK for processing
        GPApi.setBrickResponse(responseBody)
        
    } catch (e: Exception) {
        // Handle any errors during charge processing
        val errorMessage = e.message ?: "Payment processing failed"
        GPApi.setBrickError(errorMessage)
        Log.e("PaymentProcessor", "Charge request failed", e)
    }
}
  • Response Handling: Always pass the complete charge response to GPApi.setBrickResponse(response)
  • Error Handling: Use GPApi.setBrickError() for any exceptions or failures

3D Secure Integration

The SDK automatically handles 3D Secure authentication when required:

  1. Automatic Detection: The SDK detects if 3D Secure is necessary from the charge response
  2. 3D Secure UI: The SDK automatically displays the 3D Secure challenge page
  3. User Authentication: Users complete the 3D Secure verification process
  4. Token Generation: Upon successful verification, brick_secure_token and brick_charge_id are generated
  5. Result Handling:
    • If 3DS native challenge, the SDK will return brick_secure_token and brick_charge_id through BaseResp with ResponseCode.MERCHANT_PROCESSING.
    • If 3DS webview challenge, brick_secure_token and brick_charge_id are sent to your secure_redirect_url.
  6. Your backend processes the final charge with these parameters. Reference: here
  7. SDK receives the final payment result

Step 5: Handle payment result

Once the second charge is completed successfully, the SDK will send the payment result back in onResp(resp: BaseResp?)

4.2 - Add Google Pay

Step 1: Enable Google Pay

First, enable Google Pay in your AndroidManifest.xml:

<application>
<!-- Your existing application content -->

    <meta-data
        android:name="com.google.android.gms.wallet.api.enabled"
        android:value="true" />
</application>

Step 3: Going live with Google Pay

To move your integration from test to production, follow these steps:

  1. Request Production Access
    • Visit Google Pay’s publish guide.
    • Choose Gateway as the integration type when prompted.
    • Upload screenshots of your app showing the Google Pay integration.
    • Submit the form for review and wait for approval from Google’s onboarding team.
  2. App Signing Requirement
    • Make sure your APK is signed with your release key — the same key uploaded in the Google Pay Console.
    • ⚠️ Debug keys will not work in production.
  3. Configure Production Environment
    • Ensure you’re using production API keys
    • Test Google Pay from a signed, release build of your app.

4.3 - Add PayAlto

Step 1: The user selects a PayAlto payment method

Based on the payment systems enabled by the merchant in the Merchant Area, the SDK displays a list of available payment methods. Each method includes specific instructions that the user must follow to proceed.

Step 2: The user completes the required instructions

The user is prompted to provide payment information according to the method’s requirements. This may involve entering details directly within the application or being redirected to an external browser or app for authorization.

To ensure the user is redirected back to the application after completing authorization in an external flow, set request.returnUrl with your app’s deeplink.

Step 3: Process the payment result

After the user completes the required steps, the SDK returns the payment result to the application.

5 - Launch Payment Flow

Start Payment Activity

class MainActivity : ComponentActivity() {
		private fun createPaymentRequest() {
        val request = vm.createPaymentRequest(this)
        GPApi.sendReq(this, request)
    }
}

Handle Payment Result

class MainActivityViewModel : ViewModel(), IGPAPIEventHandler {

override fun onResp(resp: BaseResp?) {
        when (resp?.resultCode) {
            ResponseCode.SUCCESSFUL -> {
                paymentStatus = PaymentStatus.Success("Payment completed successfully!")
            }

            ResponseCode.FAILED -> {
                paymentStatus = PaymentStatus.Failed("Payment failed!")
            }

            ResponseCode.CANCEL -> {
                paymentStatus = PaymentStatus.Cancelled("Payment cancelled!")
            }

            ResponseCode.ERROR -> {
                paymentStatus = PaymentStatus.Unknown("An error occurred!")
            }

            ResponseCode.MERCHANT_PROCESSING -> {
                // Handle merchant-side processing here.  
                // For example:
                // - Extract Brick payment details (token, amount, email, etc.)  
                // - If 3DS 2.0, read `brick_secure_token` and `brick_charge_id` from resp.data  
                // - Forward these values to your backend for final charge processing
            }
        }
    }
}


6 - Test the Integration

Test Cards for Brick

Test your project to make sure everything works smoothly before launching your project.

Terminal3 provides you with a standard Visa test card to test payments:

  • 4242 4242 4242 4242
  • 4000 0000 0000 0002

You should use a valid expiration date to test.

CVV/CSC could be set to any 3 or 4 digits number to test payments normally. Using CVV/VSC code below will result in a different outcome while performing a test payments.

CVV/CSC Description
111 Error: Please ensure the CVV/CSC number is correct.
222 Error: Please contact your credit card issuing bank to check your balance.
333 Error: Please contact your credit card issuing bank to approve your payment.
555 Review: Your payment is under risk review and will be accept automatically after 2 mins
556 Review: Your payment is under risk review and will be declined automatically after 2 mis

Test Google Pay

  • We just support Google Pay’s production environment now
  • Ensure you have already added at least 1 payment method to Google Wallet
  • Test on a physical device in a supported country

Test PWLocal

  • Test with different country codes to see local payment methods
request.countryCode = country
  • Verify that appropriate local payment methods appear based on the currency

Next Steps

  1. Test thoroughly with all supported payment methods
  2. Set up webhook endpoints to handle payment notifications
  3. Implement proper error handling
  4. Configure your Terminal3 Dashboard with appropriate settings
  5. Test in the production environment before going live

For additional support and advanced features, visit the Terminal3 Developer Portal.

Questions?

Common questions are covered in the FAQ.
For integration and API questions, feel free to reach out Integration Team via integration@terminal3.com.
For business support, email us at sales@terminal3.com.
To contact sales, email sales@terminal3.com.