-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This document explains how to integrate AltaPay payments into an Android application using the provided SDKs.
It covers project setup, demo app usage, and two integration approaches:
- Merchant API(
altapayMerchant) - Checkout API (
altapayCheckout) - Handling Payment Callbacks: Web vs. Native app-to-app redirect flow
git clone https://github.com/AltaPay/android-payment-app.git
cd android-payment-appFile > Open > select android-payment-app
Wait for Gradle sync to complete.
- Select the app run configuration
- Connect a device or start an emulator
- Click Run
paymentClient = PaymentClient(
PaymentConfig(
username = "YOUR_API_USERNAME",
password = "YOUR_API_PASSWORD"
)
)Do not hardcode credentials in production apps.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />| Parameter | Description |
|---|---|
| terminal | Merchant terminal |
| shop_orderid | Order ID |
| amount | Payment amount |
| currency | Currency |
| otherparameter | value |
| callback_ok | Notifies the merchant the payment succeeded. See Handling Payment Callbacks. |
| callback_failure | Notifies the merchant the payment failed. See Handling Payment Callbacks. |
| callback_redirect | Styling for the loading page shown while the customer is redirected to a third party (e.g. 3-D Secure). Not a final redirect target (see below). |
callbacks.redirect tells Checkout where to send the customer back to once they're done interacting with the payment method (e.g. Bancontact). It can be an HTTPS URL or a custom app protocol, and App Links (Android) / Universal Links (iOS) are recommended over a bare custom scheme.
Which flow applies is controlled by isNativeFlow, a boolean root-level session parameter on CreateSessionRequest, not by what redirect is set to (redirect itself is used differently in each flow, described below):
val sessionRequest = CreateSessionRequest(
order = order,
callbacks = callbacks,
configuration = configuration,
isNativeFlow = true
)-
Web-Based Flow (
isNativeFlow = falseor unset): the payment page renders in a WebView. Once the payment method finishes, the Gateway callscallback_ok/callback_failureserver-to-server, then the Gateway navigates the WebView toredirect. On Android, intercept this viaPaymentWebViewHelper.attach'sonUrlChangedand close the WebView:val redirectUrl = "https://merchant.example.com/app-link" PaymentWebViewHelper.attach(webView) { url -> if (url.startsWith(redirectUrl)) { closeWebView() } }
-
Native App Flow (
isNativeFlow = true): Checkout skips the payment page entirely for app-based payment methods and redirects the customer straight into the payment method's app, usingAppUrl(a field returned in the Merchant API'screatePaymentRequestresponse that, when POSTed to with device info, returns a native redirect URL, e.g.mobilepayonline-test://...), instead of passingCallbackRedirectthrough to the Merchant API'scallback_redirect. The payment method's app then redirects back viaredirectas an OS-level deep link, which requires an<intent-filter>registered inAndroidManifest.xml:<activity android:name=".CheckoutActivity" android:exported="true"> <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="https" android:host="merchant.example.com" android:pathPrefix="/app-link" /> </intent-filter> </activity>
The official
AppUrldocs list MobilePay and Vipps; Bancontact support has since been added but isn't reflected on that page yet.
When integrating directly against the Merchant API, do not pass an app deep link as
callback_redirect: that page is shown while the customer is being redirected to a third party (e.g. 3-D Secure) and "should not do anything except tell the customer that they are being redirected." Forms and meta tags are stripped from it, so it can't even serve as a redirect target itself.
include(":altapayMerchant")dependencies {
implementation project(":altapayMerchant")
}val paymentClient = PaymentClient(
PaymentConfig(
username = "YOUR_API_USERNAME",
password = "YOUR_API_PASSWORD"
)
)paymentClient.createPaymentAsync(
paymentUrl = "https://gateway.altapay.com/payment",
params = getPaymentParams(),
callback = object : PaymentCallback { }
)dependencies {
implementation project(":altapayCheckout")
}val paymentClient = CheckoutPaymentClient(paymentConfig)Before starting the payment process, configure the payment client by providing the necessary API credentials and base URL.
val paymentConfig = PaymentConfig(
username = "your-username", // Your API username
password = "your-password", // Your API password
baseUrl = "https://testgateway.altapaysecure.com/" // Base URL for the payment gateway
)
val paymentClient = CheckoutPaymentClient(paymentConfig)Create a ViewModel to manage the payment state and operations. This ViewModel will interact with the CheckoutPaymentClient.
class PaymentViewModel(private val paymentClient: CheckoutPaymentClient) : ViewModel() {
private val _paymentMethod = MutableStateFlow<PaymentMethod?>(null)
val paymentMethod: StateFlow<PaymentMethod?> get() = _paymentMethod
private val _availablePaymentMethods = MutableStateFlow<List<PaymentMethod>?>(null)
val availablePaymentMethods: StateFlow<List<PaymentMethod>?> get() = _availablePaymentMethods
private val _paymentResult = MutableStateFlow<PaymentResultCheckout?>(null)
val paymentResult: StateFlow<PaymentResultCheckout?> get() = _paymentResult
private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> get() = _error
private val _loadingState = MutableStateFlow(LoadingState.IDLE)
val loadingState: StateFlow<LoadingState> get() = _loadingState
// Add methods to interact with the CheckoutPaymentClient here
}To initiate the payment process, follow these steps:
Authenticate the user and create a session.
val sessionRequest = CreateSessionRequest(
// Provide order details like order ID, amount, customer information, etc.
)
lifecycleScope.launch {
viewModel.authenticateAndCreateSession(sessionRequest)
}Once the session is created, fetch available payment methods.
viewModel.fetchPaymentMethods(sessionId)After fetching payment methods, let the user select one.
viewModel.selectPaymentMethod(selectedMethod)Once the user selects a payment method, proceed with creating the payment.
viewModel.createPayment()The UI flow is flexible and customizable. You can display payment methods in different formats:
- Dialog: A simple dialog for the user to select a payment method.
- RecyclerView: A list or grid view displaying payment methods.
- Custom UI: Custom UI components for payment method selection.
Once the user selects a payment method, call viewModel.selectPaymentMethod(selectedMethod).
After the payment is created, you can use a WebView to load the payment URL and process the payment.
// Load the payment URL in WebView for completing the payment
webView.loadUrl(paymentUrl)Monitor the WebView’s state to detect when the payment flow has finished, so you know when to close the WebView.
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
if (url.contains("payment-success")) {
// Handle success
} else if (url.contains("payment-failed")) {
// Handle failure
} else if (url.contains("payment-cancelled")) {
// Handle cancellation
}
}
}Do not treat these URL keywords as the source of truth for the payment result. As covered in Handling Payment Callbacks, the actual success/failure result is delivered to your backend via
CallbackSuccess/CallbackFailure, not through the WebView's navigation. Use URL matching only to decide when to close the WebView (see Handling the App Return URL on Android), and confirm the outcome by querying your backend's order status.