Android App Integration
Accept NFC payments from your Android app using the MONEI Pay Android SDK. The SDK handles intent launching, result parsing, and error handling.
Source: github.com/MONEI/monei-pay-android-sdk
Uses standard Android intents — no special signing, entitlements, or platform approvals required.
Payment Modes
The SDK supports two modes:
- Direct (
PaymentMode.DIRECT) — Launches CloudCommerce directly. MONEI Pay is not required. - Via MONEI Pay (
PaymentMode.VIA_MONEI_PAY) — Launches MONEI Pay, which handles the NFC payment.
| Direct | Via MONEI Pay | |
|---|---|---|
| POS auth token | Required | Required |
| MONEI Pay app | Not needed | Required |
| CloudCommerce app | Required | Not required |
| Best for | CloudCommerce-only setups | Full MONEI Pay merchant flow |
Download Required Apps
Prerequisites
- MONEI account
- Android 8.0+ (API 26), NFC-capable device
- POS auth token from your backend — see Getting Started
- Direct mode: CloudCommerce installed
- Via MONEI Pay mode: MONEI Pay installed
Integration
1. Add the Dependency
- GitHub Packages (recommended)
- JitPack (no auth)
Requires a PAT with read:packages (or GITHUB_TOKEN in CI):
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_TOKEN
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/MONEI/monei-pay-android-sdk")
credentials {
username = providers.gradleProperty("gpr.user")
.orElse(providers.environmentVariable("GITHUB_ACTOR"))
.get()
password = providers.gradleProperty("gpr.key")
.orElse(providers.environmentVariable("GITHUB_TOKEN"))
.get()
}
}
}
}
dependencies {
implementation("com.monei:monei-pay-sdk:0.2.0")
}
2. Configure AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<intent>
<action android:name="com.monei.pay.ACCEPT_PAYMENT" />
</intent>
<package android:name="com.mastercard.cpos" />
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="cloud_payment" />
</intent>
</queries>
</manifest>
3. Accept a Payment
- Direct Mode
- Via MONEI Pay
import com.monei.pay.sdk.MoneiPay
import com.monei.pay.sdk.PaymentMode
import com.monei.pay.sdk.MoneiPayException
try {
val result = MoneiPay.acceptPayment(
context = this,
token = "eyJ...",
amount = 1500,
description = "Order #123",
customerName = "John Doe",
customerEmail = "john@example.com",
customerPhone = "+34600000000",
mode = PaymentMode.DIRECT
)
if (result.success) {
println("Payment approved: ${result.transactionId}")
println("Card: ${result.cardBrand} ${result.maskedCardNumber}")
}
} catch (e: MoneiPayException.CloudCommerceNotInstalled) {
// Prompt user to install CloudCommerce
} catch (e: MoneiPayException.PaymentCancelled) {
// User cancelled
} catch (e: MoneiPayException.InvalidToken) {
// Token expired or invalid
} catch (e: MoneiPayException.PaymentFailed) {
println("Payment failed: ${e.reason}")
} catch (e: MoneiPayException) {
println("Error: ${e.message}")
}
import com.monei.pay.sdk.MoneiPay
import com.monei.pay.sdk.PaymentMode
import com.monei.pay.sdk.MoneiPayException
try {
val result = MoneiPay.acceptPayment(
context = this,
token = "eyJ...",
amount = 1500,
description = "Order #123",
customerName = "John Doe",
customerEmail = "john@example.com",
customerPhone = "+34600000000",
mode = PaymentMode.VIA_MONEI_PAY
)
if (result.success) {
println("Payment approved: ${result.transactionId}")
println("Card: ${result.cardBrand} ${result.maskedCardNumber}")
}
} catch (e: MoneiPayException.MoneiPayNotInstalled) {
// Prompt user to install MONEI Pay
} catch (e: MoneiPayException.PaymentCancelled) {
// User cancelled
} catch (e: MoneiPayException.InvalidToken) {
// Token expired or invalid
} catch (e: MoneiPayException) {
println("Payment failed: ${e.message}")
}
SDK Reference
MoneiPay.acceptPayment(...)
Suspending function — call from a coroutine scope.
| Parameter | Type | Required | Description |
|---|---|---|---|
context | Context | Yes | Activity or Application context |
token | String | Yes | Raw JWT auth token (no "Bearer " prefix) |
amount | Int | Yes | Amount in cents |
description | String? | No | Payment description |
customerName | String? | No | Customer name |
customerEmail | String? | No | Customer email |
customerPhone | String? | No | Customer phone |
mode | PaymentMode | No | DIRECT (default) or VIA_MONEI_PAY |
PaymentResult
| Property | Type | Description |
|---|---|---|
transactionId | String | MONEI transaction ID |
success | Boolean | Whether the payment was approved |
amount | Int? | Payment amount in cents |
cardBrand | String? | Card brand (e.g. visa, mastercard) |
maskedCardNumber | String? | Masked card number (e.g. ****1234) |
Error Types
| Exception | Description |
|---|---|
MoneiPayNotInstalled | MONEI Pay not installed (VIA_MONEI_PAY mode) |
CloudCommerceNotInstalled | CloudCommerce not installed (DIRECT mode) |
PaymentInProgress | Another payment is already active |
PaymentCancelled | User cancelled |
PaymentFailed | Payment declined or failed (has reason) |
InvalidParameters | Invalid input (e.g. non-positive amount) |
InvalidToken | Auth token invalid or expired |
Manual Integration (Intent)
For integration without the SDK using Android intents directly.
Show manual intent integration
Configure Your Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="com.monei.pay.permission.ACCEPT_PAYMENT" />
<queries>
<intent>
<action android:name="com.monei.pay.ACCEPT_PAYMENT" />
</intent>
</queries>
</manifest>
Launch the Payment Intent
class MainActivity : AppCompatActivity() {
private val paymentLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
when (result.resultCode) {
RESULT_OK -> handleSuccess(result.data)
RESULT_CANCELED -> handleError(result.data)
}
}
fun acceptPayment(amountInCents: Int) {
val intent = Intent("com.monei.pay.ACCEPT_PAYMENT").apply {
setPackage("com.monei.moneibusiness")
putExtra("amount_cents", amountInCents)
putExtra("auth_token", "eyJ...")
}
if (intent.resolveActivity(packageManager) == null) {
return
}
paymentLauncher.launch(intent)
}
}
Handle the Result
private fun handleSuccess(data: Intent?) {
val success = data?.getBooleanExtra("success", false) ?: false
val transactionId = data?.getStringExtra("transaction_id")
val amount = data?.getIntExtra("amount", 0)
val cardBrand = data?.getStringExtra("card_brand")
val maskedCard = data?.getStringExtra("masked_card_number")
}
private fun handleError(data: Intent?) {
val errorCode = data?.getStringExtra("error_code") ?: "UNKNOWN"
val errorMessage = data?.getStringExtra("error_message") ?: "Payment failed"
}
Intent Parameters
Request Extras
| Extra | Type | Required | Description |
|---|---|---|---|
amount_cents | Int | Yes | Payment amount in cents (e.g. 1500 = 15.00 EUR) |
auth_token | String | Yes | POS auth token (raw JWT) |
description | String | No | Payment description |
customer_name | String | No | Customer name |
customer_email | String | No | Customer email |
customer_phone | String | No | Customer phone |
Result Extras (RESULT_OK)
| Extra | Type | Description |
|---|---|---|
transaction_id | String | MONEI transaction ID |
success | Boolean | true if approved, false if declined |
amount | Int | Payment amount in cents |
card_brand | String | Card brand (e.g. visa, mastercard) |
masked_card_number | String | Masked card number (e.g. ****1234) |
Result Extras (RESULT_CANCELED)
| Extra | Type | Description |
|---|---|---|
error_code | String | Error code (e.g. PAYMENT_FAILED, NOT_AUTHENTICATED) |
error_message | String | Human-readable error description |
Example App
The examples/merchant-demo/ directory contains a minimal app demonstrating the full flow. Run ./gradlew assembleDebug and install on an NFC-capable device. The project uses includeBuild to reference the SDK locally, so SDK changes apply immediately.
Troubleshooting
CloudCommerceNotInstalled
CloudCommerce is a separate app from MONEI Pay. Install from Google Play and verify your manifest includes the <package> and cloud_payment:// scheme in <queries>.
MoneiPayNotInstalled
Install MONEI Pay from Google Play. Verify your manifest includes the com.monei.pay.ACCEPT_PAYMENT action in <queries>.
InvalidToken
The POS auth token has a 24h lifetime. Generate a fresh token from your backend. Pass the raw JWT string — not "Bearer ...".
Payment fails on API 30+
Android 11+ requires <queries> for package visibility. Ensure your manifest has the full queries block from Configure AndroidManifest.
Dependency resolution fails
- JitPack: Add
google(),mavenCentral(), andhttps://jitpack.iotosettings.gradle.ktsunderdependencyResolutionManagement. - GitHub Packages: Check
gpr.user/gpr.keyingradle.propertiesand themaven.pkg.github.com/MONEI/monei-pay-android-sdkrepository block.