# 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](https://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[​](#payment-modes "Direct link to 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[​](#download-required-apps "Direct link to Download required apps")

**MONEI Pay** (Via MONEI Pay mode)

[![Get MONEI Pay on Google Play](/img/google-play-icon.svg)](https://play.google.com/store/apps/details?id=com.monei.moneibusiness)

**CloudCommerce** (Direct mode)

[![Get CloudCommerce on Google Play](/img/google-play-icon.svg)](https://play.google.com/store/apps/details?id=com.mastercard.cpos)

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* [MONEI account](https://dashboard.monei.com/signup)
* Android 8.0+ (API 26), NFC-capable device
* POS auth token from your backend — see [Getting Started](https://docs.monei.com/monei-pay/app-integration/getting-started.md)
* **Direct mode:** [CloudCommerce](https://play.google.com/store/apps/details?id=com.mastercard.cpos) installed
* **Via MONEI Pay mode:** [MONEI Pay](https://play.google.com/store/apps/details?id=com.monei.moneibusiness) installed

## Integration[​](#integration "Direct link to Integration")

### 1. Add the Dependency[​](#1-add-the-dependency "Direct link to 1. Add the Dependency")

* GitHub Packages (recommended)
* JitPack (no auth)

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:1.0.0")

}
```

settings.gradle.kts

```
dependencyResolutionManagement {

    repositories {

        google()

        mavenCentral()

        maven { url = uri("https://jitpack.io") }

    }

}
```

app/build.gradle.kts

```
dependencies {

    implementation("com.github.MONEI.monei-pay-android-sdk:sdk:v1.0.0")

}
```

[![JitPack](https://jitpack.io/v/MONEI/monei-pay-android-sdk.svg)](https://jitpack.io/#MONEI/monei-pay-android-sdk)

### 2. Configure AndroidManifest[​](#2-configure-androidmanifest "Direct link to 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[​](#3-accept-a-payment "Direct link to 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",

        callbackUrl = "https://merchant.com/webhook/monei",

        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",

        callbackUrl = "https://merchant.com/webhook/monei",

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

}
```

Trust model

`callbackUrl` is a **trusted, signed webhook** delivered server-to-server — use it to fulfill orders. The SDK's sync `PaymentResult` is also trustworthy because it comes back through the Android intent system from the originating payment app, but it should still be reconciled via the webhook or [Get Payment](https://docs.monei.com/apis/rest/payments-get.md) for record-keeping. See [Result Delivery](https://docs.monei.com/monei-pay/app-integration/getting-started.md#result-delivery).

### SDK Reference[​](#sdk-reference "Direct link to SDK Reference")

#### `MoneiPay.acceptPayment(...)`[​](#moneipayacceptpayment "Direct link to moneipayacceptpayment")

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                                                                                                                                                                     |
| `callbackUrl`     | `String?`     | No       | Trusted signed webhook URL. Must be strict `https://`, max 2048 chars. See [Result Delivery](https://docs.monei.com/monei-pay/app-integration/getting-started.md#result-delivery). |
| `orderId`         | `String?`     | No       | Your merchant order reference. Surfaced in the webhook callback for reconciliation. Max 2048 chars. If omitted, the SDK generates one.                                             |
| `transactionType` | `String?`     | No       | Optional transaction type: `SALE` (default), `AUTH`, `REFUND`, `CAPTURE`, `CANCEL`, `PAYOUT`, `VERIF`. Server-validated.                                                           |
| `mode`            | `PaymentMode` | No       | `DIRECT` (default) or `VIA_MONEI_PAY`                                                                                                                                              |

#### `PaymentResult`[​](#paymentresult "Direct link to 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[​](#error-types "Direct link to 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)[​](#manual-integration-intent "Direct link to Manual Integration (Intent)")

For integration without the SDK using Android intents directly.

Show manual intent integration

### Configure Your Manifest[​](#configure-your-manifest "Direct link to 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[​](#launch-the-payment-intent "Direct link to 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[​](#handle-the-result "Direct link to 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[​](#intent-parameters "Direct link to Intent Parameters")

#### Request Extras[​](#request-extras "Direct link to 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)[​](#result-extras-result_ok "Direct link to 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)[​](#result-extras-result_canceled "Direct link to 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                        |

## Manual Integration (Deep Link)[​](#manual-integration-deep-link "Direct link to Manual Integration (Deep Link)")

For native Android apps, prefer the intent flow above — and the **CloudCommerce direct intent** when you want to bypass the MONEI Pay app entirely. This deep-link path is for non-Android callers — including **web apps** (browser-based merchant terminals) — which open the MONEI Pay URL with the same shape as the iOS one. See [Web App Integration](https://docs.monei.com/monei-pay/app-integration/web.md) for the full browser-driven flow.

Open `https://pay.monei.com/accept-payment` — an **App Link** on Android (and a Universal Link on iOS). It launches MONEI Pay directly with no app-chooser or confirmation. The legacy `monei-pay://accept-payment` URL scheme accepts the **same query parameters** and still works. If MONEI Pay isn't installed, the HTTPS link opens the MONEI Pay web app sign-in instead of failing silently.

Show manual deep-link integration

### Open the Payment URL[​](#open-the-payment-url "Direct link to Open the Payment URL")

```
<a

  href="https://pay.monei.com/accept-payment

  ?amount=1500

  &auth_token=eyJ...

  &callback_url=https://merchant.com/webhook/monei

  &complete_url=https://merchant.com/checkout/done"

>

  Pay with MONEI Pay

</a>
```

Or programmatically:

```
window.location.href =

  'https://pay.monei.com/accept-payment' +

  '?amount=1500' +

  '&auth_token=' +

  encodeURIComponent(token) +

  '&callback_url=' +

  encodeURIComponent('https://merchant.com/webhook/monei') +

  '&complete_url=' +

  encodeURIComponent('https://merchant.com/checkout/done');
```

Android resolves the App Link via the installed MONEI Pay app. The user taps the link (or the browser opens it), MONEI Pay handles the NFC payment, then redirects to `complete_url` with the result.

### URL Parameters[​](#url-parameters "Direct link to URL Parameters")

| Parameter          | Type    | Required | Description                                                                                                                              |
| ------------------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`           | Integer | Yes      | Payment amount in cents (e.g. `1500` = 15.00 EUR)                                                                                        |
| `auth_token`       | String  | Yes      | POS auth token (raw JWT)                                                                                                                 |
| `callback_url`     | String  | No       | Trusted signed webhook URL. Must be strict `https://`, max 2048 chars. Receives a signed POST with `MONEI-Signature` HMAC on completion. |
| `complete_url`     | String  | No       | URL opened on the device after the payment completes. Accepts `https://`, custom URL schemes, App Links. Max 2048 chars. Not signed.     |
| `description`      | String  | No       | Payment description                                                                                                                      |
| `customer_name`    | String  | No       | Customer name                                                                                                                            |
| `customer_email`   | String  | No       | Customer email                                                                                                                           |
| `customer_phone`   | String  | No       | Customer phone                                                                                                                           |
| `order_id`         | String  | No       | Merchant order reference. Surfaced in the webhook callback for reconciliation. Max 2048 chars. If omitted, MONEI generates one.          |
| `transaction_type` | String  | No       | Optional transaction type: `SALE`, `AUTH`, `REFUND`, `CAPTURE`, `CANCEL`, `PAYOUT`, `VERIF`. Server-validated.                           |

### `complete_url` Redirect Parameters (Success)[​](#complete_url-redirect-parameters-success "Direct link to complete_url-redirect-parameters-success")

| Parameter            | Type   | Description                            |
| -------------------- | ------ | -------------------------------------- |
| `success`            | String | `"true"`                               |
| `transaction_id`     | String | MONEI transaction ID                   |
| `amount`             | String | Payment amount in cents                |
| `card_brand`         | String | Card brand (e.g. `visa`, `mastercard`) |
| `masked_card_number` | String | Masked card number (e.g. `****1234`)   |

### `complete_url` Redirect Parameters (Error)[​](#complete_url-redirect-parameters-error "Direct link to complete_url-redirect-parameters-error")

| Parameter | Type   | Description                                                                                                             |
| --------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `success` | String | `"false"`                                                                                                               |
| `error`   | String | Error code — see [error code list](https://docs.monei.com/monei-pay/app-integration/getting-started.md#result-delivery) |

Trust model

The query parameters on the `complete_url` redirect are **not signed** — they can be spoofed. Use `callback_url` (signed webhook) or [Get Payment](https://docs.monei.com/apis/rest/payments-get.md) to confirm the actual outcome before fulfilling an order. See [Result Delivery](https://docs.monei.com/monei-pay/app-integration/getting-started.md#result-delivery).

## Example app[​](#example-app "Direct link to Example app")

The [`examples/merchant-demo/`](https://github.com/MONEI/monei-pay-android-sdk/tree/master/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[​](#troubleshooting "Direct link to Troubleshooting")

### `CloudCommerceNotInstalled`[​](#cloudcommercenotinstalled "Direct link to cloudcommercenotinstalled")

CloudCommerce is a separate app from MONEI Pay. Install from [Google Play](https://play.google.com/store/apps/details?id=com.mastercard.cpos) and verify your manifest includes the `<package>` and `cloud_payment://` scheme in `<queries>`.

### `MoneiPayNotInstalled`[​](#moneipaynotinstalled "Direct link to moneipaynotinstalled")

Install MONEI Pay from [Google Play](https://play.google.com/store/apps/details?id=com.monei.moneibusiness). Verify your manifest includes the `com.monei.pay.ACCEPT_PAYMENT` action in `<queries>`.

### `InvalidToken`[​](#invalidtoken "Direct link to 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+[​](#payment-fails-on-api-30 "Direct link to Payment fails on API 30+")

Android 11+ requires `<queries>` for package visibility. Ensure your manifest has the full queries block from [Configure AndroidManifest](#2-configure-androidmanifest).

### Dependency resolution fails[​](#dependency-resolution-fails "Direct link to 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.
