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:
- Automatic Detection: The SDK detects if 3D Secure is necessary from the charge response
- 3D Secure UI: The SDK automatically displays the 3D Secure challenge page
- User Authentication: Users complete the 3D Secure verification process
- Token Generation: Upon successful verification,
brick_secure_tokenandbrick_charge_idare generated - Result Handling:
- If 3DS native challenge, the SDK will return
brick_secure_tokenandbrick_charge_idthroughBaseRespwithResponseCode.MERCHANT_PROCESSING. - If 3DS webview challenge,
brick_secure_tokenandbrick_charge_idare sent to yoursecure_redirect_url.
- If 3DS native challenge, the SDK will return
- Your backend processes the final charge with these parameters. Reference: here
- 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:
- 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.
- 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.
- 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
- Test thoroughly with all supported payment methods
- Set up webhook endpoints to handle payment notifications
- Implement proper error handling
- Configure your Terminal3 Dashboard with appropriate settings
- Test in the production environment before going live
For additional support and advanced features, visit the Terminal3 Developer Portal.