# Getting Started

Accept NFC payments from your own merchant app using the MONEI Pay SDKs.

## How it works[​](#how-it-works "Direct link to How it works")

<!-- -->

1. Your **backend** generates a POS auth token using your MONEI API key
2. Your **merchant app** receives the token via your own API
3. The app calls `acceptPayment()` with the token and amount
4. The SDK opens MONEI Pay (or CloudCommerce on Android) for NFC tap-to-pay
5. The payment result is returned to your app

### Download MONEI Pay[​](#download-monei-pay "Direct link to Download MONEI Pay")

[![Download MONEI Pay on the App Store](/img/app-store-icon.svg)](https://apps.apple.com/app/monei-pay/id1624647668)[![Get MONEI Pay on Google Play](/img/google-play-icon.svg)](https://play.google.com/store/apps/details?id=com.monei.moneibusiness)

## Step 1: Get Your API Key[​](#step-1-get-your-api-key "Direct link to Step 1: Get Your API Key")

1. Sign up at [dashboard.monei.com](https://dashboard.monei.com/signup)
2. Go to **Settings → API Access**
3. Use the **test** key for development, **live** key for production

![API Access page in the MONEI Dashboard settings, showing your account ID and API key](/img/dashboard/en/settings-api-access.png)

API Access page in the MONEI Dashboard settings, showing your account ID and API key

## Step 2: Generate a POS Auth Token[​](#step-2-generate-a-pos-auth-token "Direct link to Step 2: Generate a POS Auth Token")

Your backend calls the MONEI API to create a token for each payment session. The token is valid for **24 hours** — you can reuse it for multiple transactions within that window. Never expose your API key in the merchant app. See the full [Create POS Auth Token](https://docs.monei.com/apis/rest/pos-auth-token-create/.md) API reference.

* cURL
* Node.js
* Python
* PHP

```
curl -X POST https://api.monei.com/v1/pos/auth-token \

  -H "Authorization: YOUR_API_KEY"

# Optional: scope to a specific terminal or store

#   -H "Content-Type: application/json" \

#   -d '{"pointOfSaleId": "pos_abc123", "storeId": "store_xyz"}'
```

server.js

```
import {Monei} from '@monei-js/node-sdk';



const monei = new Monei('YOUR_API_KEY');



const {token} = await monei.posAuthToken.create();

// Optional: .create({pointOfSaleId: 'pos_abc123', storeId: 'store_xyz'})

// Send `token` to your merchant app
```

server.py

```
import Monei



monei = Monei.MoneiClient(api_key="YOUR_API_KEY")



result = monei.pos_auth_token.create()

# Optional: monei.pos_auth_token.create(

#     CreatePosAuthTokenRequest(point_of_sale_id="pos_abc123", store_id="store_xyz")

# )

token = result.token

# Send `token` to your merchant app
```

server.php

```
<?php

require_once 'vendor/autoload.php';



use Monei\MoneiClient;



$monei = new MoneiClient('YOUR_API_KEY');



$result = $monei->posAuthToken->create();

// Optional: ->create(new CreatePosAuthTokenRequest(['point_of_sale_id' => 'pos_abc123', 'store_id' => 'store_xyz']))

$token = $result->getToken();

// Send $token to your merchant app

?>
```

### Request Parameters[​](#request-parameters "Direct link to Request Parameters")

| Parameter       | Type   | Required | Description                                                                                           |
| --------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `pointOfSaleId` | String | No       | A unique identifier of the Point of Sale. If specified the payment is attached to this Point of Sale. |
| `storeId`       | String | No       | A unique identifier of the Store. If specified the payment is attached to this Store.                 |

### Response[​](#response "Direct link to Response")

```
{

  "token": "eyJhbGciOiJSUzI1NiIs..."

}
```

The token is a JWT (RS256-signed) containing the account ID and merchant metadata. It expires after **24 hours**. Generate one per session — you don't need a new token for each transaction.

## Step 3: Integrate Your App[​](#step-3-integrate-your-app "Direct link to Step 3: Integrate Your App")

Install the SDK, configure your project, call `acceptPayment()` with the token from Step 2, and handle results. Each guide covers installation, platform setup, SDK reference, example app, and troubleshooting.

* **[iOS](https://docs.monei.com/monei-pay/app-integration/ios/.md)** — Swift SDK via SPM or CocoaPods
* **[Android](https://docs.monei.com/monei-pay/app-integration/android/.md)** — Kotlin SDK via GitHub Packages or JitPack, Direct vs Via MONEI Pay modes
* **[React Native](https://docs.monei.com/monei-pay/app-integration/react-native/.md)** — Expo module, cross-platform with one API
* **[Web](https://docs.monei.com/monei-pay/app-integration/web/.md)** — Browser-based POS. No SDK; opens the MONEI Pay deep link from Safari or Chrome

## Verify Payments Server-Side[​](#verify-payments-server-side "Direct link to Verify Payments Server-Side")

The SDK returns a `PaymentResult` to your app, but you should **always verify the payment from your backend** before fulfilling an order. This prevents client-side tampering.

Use the [Get Payment](https://docs.monei.com/apis/rest/payments-get/.md) endpoint with the `transactionId` returned in the result:

```
curl https://api.monei.com/v1/payments/{transactionId} \

  -H "Authorization: YOUR_API_KEY"
```

Alternatively, configure a `callbackUrl` when creating payments to receive asynchronous webhook notifications. See [Verify signature](https://docs.monei.com/guides/verify-signature/.md) for details on validating webhook payloads.

## Result Delivery[​](#result-delivery "Direct link to Result Delivery")

MONEI Pay returns payment results through **two independent channels**. They serve different purposes — use them together, never interchangeably.

| Channel        | Type                 | Trust       | Use for                                           |
| -------------- | -------------------- | ----------- | ------------------------------------------------- |
| `callback_url` | Signed HTTPS webhook | **Trusted** | Order fulfillment, business logic, reconciliation |
| `complete_url` | Client-side redirect | NOT trusted | UX redirect, returning the user to your flow      |

### `callback_url` — trusted webhook[​](#callback_url--trusted-webhook "Direct link to callback_url--trusted-webhook")

A signed HTTPS webhook delivered server-to-server with a verifiable `MONEI-Signature` HMAC. This is the **only** channel you should base business decisions on (fulfilling orders, granting access, sending receipts).

* Must be `https://` — `http`, private IPs, and localhost are rejected
* Max 2048 characters
* Optional per-payment override; if omitted, MONEI uses your Point of Sale's configured webhook
* Validate the signature server-side before trusting the payload — see [Verify signature](https://docs.monei.com/guides/verify-signature/.md)

### `complete_url` — UX redirect[​](#complete_url--ux-redirect "Direct link to complete_url--ux-redirect")

A URL opened on the user's device after the payment completes (success or failure). It's a one-shot redirect with the result encoded in query parameters. **It is not signed, can be spoofed, and must never drive business logic.**

* Accepts `https://`, custom URL schemes (`mymerchant://...`), Universal Links (iOS), and App Links (Android)
* Rejected schemes: `http`, `tel`, `sms`, `mailto`, `javascript`, `file`, `data`, `blob`, `intent`, plus platform-internal schemes
* Max 2048 characters
* Use it to bring the user back to your app or page — then confirm the actual outcome via [Get Payment](https://docs.monei.com/apis/rest/payments-get/.md) or the `callback_url` webhook

### Result query parameters on `complete_url` open[​](#result-query-parameters-on-complete_url-open "Direct link to result-query-parameters-on-complete_url-open")

When MONEI Pay opens `complete_url`, it appends the following query parameters:

**Success:**

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

**Failure / cancel:**

| Parameter | Description            |
| --------- | ---------------------- |
| `success` | `"false"`              |
| `error`   | Error code (see below) |

**Error codes** that may appear in `error`:

| Code                     | Meaning                                                            |
| ------------------------ | ------------------------------------------------------------------ |
| `CANCELLED`              | User cancelled, or your app navigated away mid-flow                |
| `TOKEN_EXPIRED`          | POS auth token has expired (>24h)                                  |
| `INVALID_TOKEN`          | POS auth token is malformed or signed by the wrong key             |
| `INVALID_AMOUNT`         | Amount is missing, non-numeric, or non-positive                    |
| `INVALID_CALLBACK_URL`   | `callback_url` is not strict https or exceeds 2048 chars           |
| `INVALID_COMPLETE_URL`   | `complete_url` uses a blocked scheme or exceeds 2048 chars         |
| `NOT_AUTHENTICATED`      | No POS auth token and the user is not signed into MONEI Pay        |
| `ACCOUNT_NOT_CONFIGURED` | MONEI Pay account exists but is missing required POS configuration |
| `PAYMENT_FAILED`         | Card declined, network error, or other downstream failure          |

### Recommended pattern[​](#recommended-pattern "Direct link to Recommended pattern")

Pair both channels. The webhook fires reliably from MONEI's servers even if the device goes offline; the redirect gives your user a clean return path.

```
https://pay.monei.com/accept-payment

  ?amount=1500

  &auth_token=eyJ...

  &order_id=order_42

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

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

`https://pay.monei.com/accept-payment` is a **Universal Link** (iOS) / **App Link** (Android): it opens MONEI Pay directly with no "Open in MONEI Pay?" confirmation. The legacy `monei-pay://accept-payment` URL scheme accepts the **same query parameters** and still works, but shows the iOS confirmation sheet — prefer the HTTPS link.

When `complete_url` opens, treat the query params as a hint only — confirm via webhook or [Get Payment](https://docs.monei.com/apis/rest/payments-get/.md) before fulfilling.

### Reconciliation with `order_id`[​](#reconciliation-with-order_id "Direct link to reconciliation-with-order_id")

Optional. Pass your own `order_id` to surface it as the payment's `orderId` in the signed `callback_url` webhook. Useful when your POS already has an order reference (table number, invoice number, internal cart ID) and you want to correlate the MONEI payment without an extra DB lookup. If omitted, MONEI generates one.

## Common issues[​](#common-issues "Direct link to Common issues")

| Problem                                                | Cause                                                                | Solution                                                                                                               |
| ------------------------------------------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| SDK returns "app not found"                            | MONEI Pay (or CloudCommerce on Android Direct mode) is not installed | Install the required app on the device                                                                                 |
| Token rejected / 401                                   | Token expired (>24h) or wrong API key environment                    | Generate a fresh token; ensure test key for test mode, live key for production                                         |
| NFC not working                                        | NFC disabled or device unsupported                                   | Check device settings; see [MONEI Pay overview](https://docs.monei.com/monei-pay/overview/.md) for device requirements |
| Payment succeeds on device but backend shows no record | Missing server-side verification                                     | Always verify via API or webhooks (see above)                                                                          |

## Next steps[​](#next-steps "Direct link to Next steps")

* [MONEI Pay overview](https://docs.monei.com/monei-pay/overview/.md) — product features and device requirements
* [MONEI REST API](https://docs.monei.com/apis/rest/.md) — full API reference
* [Testing](https://docs.monei.com/testing/.md) — test cards and sandbox environment
* [Verify signature](https://docs.monei.com/guides/verify-signature/.md) — validate webhook payloads
