Skip to main content

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

No special signing needed

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.
DirectVia MONEI Pay
POS auth tokenRequiredRequired
MONEI Pay appNot neededRequired
CloudCommerce appRequiredNot required
Best forCloudCommerce-only setupsFull MONEI Pay merchant flow

Download Required Apps

MONEI Pay (Via MONEI Pay mode)

Get MONEI Pay on Google Play

CloudCommerce (Direct mode)

Get CloudCommerce on Google Play

Prerequisites

Integration

1. Add the Dependency

Requires a PAT with read:packages (or GITHUB_TOKEN in CI):

gradle.properties
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_TOKEN
settings.gradle.kts
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()
}
}
}
}
app/build.gradle.kts
dependencies {
implementation("com.monei:monei-pay-sdk:0.2.0")
}

2. Configure AndroidManifest

AndroidManifest.xml
<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

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}")
}

SDK Reference

MoneiPay.acceptPayment(...)

Suspending function — call from a coroutine scope.

ParameterTypeRequiredDescription
contextContextYesActivity or Application context
tokenStringYesRaw JWT auth token (no "Bearer " prefix)
amountIntYesAmount in cents
descriptionString?NoPayment description
customerNameString?NoCustomer name
customerEmailString?NoCustomer email
customerPhoneString?NoCustomer phone
modePaymentModeNoDIRECT (default) or VIA_MONEI_PAY

PaymentResult

PropertyTypeDescription
transactionIdStringMONEI transaction ID
successBooleanWhether the payment was approved
amountInt?Payment amount in cents
cardBrandString?Card brand (e.g. visa, mastercard)
maskedCardNumberString?Masked card number (e.g. ****1234)

Error Types

ExceptionDescription
MoneiPayNotInstalledMONEI Pay not installed (VIA_MONEI_PAY mode)
CloudCommerceNotInstalledCloudCommerce not installed (DIRECT mode)
PaymentInProgressAnother payment is already active
PaymentCancelledUser cancelled
PaymentFailedPayment declined or failed (has reason)
InvalidParametersInvalid input (e.g. non-positive amount)
InvalidTokenAuth token invalid or expired

Manual Integration (Intent)

For integration without the SDK using Android intents directly.

Show manual intent integration

Configure Your Manifest

AndroidManifest.xml
<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

ExtraTypeRequiredDescription
amount_centsIntYesPayment amount in cents (e.g. 1500 = 15.00 EUR)
auth_tokenStringYesPOS auth token (raw JWT)
descriptionStringNoPayment description
customer_nameStringNoCustomer name
customer_emailStringNoCustomer email
customer_phoneStringNoCustomer phone

Result Extras (RESULT_OK)

ExtraTypeDescription
transaction_idStringMONEI transaction ID
successBooleantrue if approved, false if declined
amountIntPayment amount in cents
card_brandStringCard brand (e.g. visa, mastercard)
masked_card_numberStringMasked card number (e.g. ****1234)

Result Extras (RESULT_CANCELED)

ExtraTypeDescription
error_codeStringError code (e.g. PAYMENT_FAILED, NOT_AUTHENTICATED)
error_messageStringHuman-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(), and https://jitpack.io to settings.gradle.kts under dependencyResolutionManagement.
  • GitHub Packages: Check gpr.user / gpr.key in gradle.properties and the maven.pkg.github.com/MONEI/monei-pay-android-sdk repository block.