# Migration Guide

- [v4 to v5](#v4-to-v5) - **start here if you are on v4**, `jail-monkey` can block live checkouts
- [v1 to v4](#v1-to-v4) - for merchants currently using v1
- [v3 to v4](#v3-to-v4) - for merchants currently using v3
- [v1 to v3](#v1-to-v3) - historical reference


## v4 to v5

> For the complete v5 API reference, see the [React Native SDK v5 documentation](/platform/sdk/react-native-sdk/v5).
For the full list of changes, see the [Changelog](/platform/sdk/react-native-sdk/changelog).


### Start here: `jail-monkey` is now required, and it can block live checkouts

This is the one change that can take a working integration off the air, so do it before anything else.

`jail-monkey` was an optional peer dependency in v4. The SDK warned when it was missing and carried on. In v5 it is a required peer dependency and the compliance gate **fails closed**: a device whose security the SDK cannot verify is refused on a `live_` key rather than assumed clean.

```bash
npm install jail-monkey
cd ios && pod install
```

Then rebuild the app. Installing without rebuilding leaves the native module unlinked, which the SDK treats exactly as if the package were absent.

If you skip this, every live checkout returns `false` from `start()` and fires `onSdkError` with `type: "device_forbidden"`. Mock and test keys are unaffected, so this will not show up in a `test_` key QA pass - check with a live key before you ship.

**Emulators are no longer a problem.** v4 relied on `jail-monkey`'s aggregate verdict, which reports stock Android emulator images as rooted because they ship AOSP test-keys and debuggable system properties. v5 computes its own verdict from the per-check breakdown and ignores those two signals, so integration-testing with a live key on an emulator now works. Every strong root indicator is still enforced.

### Renamed callbacks

| v4 | v5 |
|  --- | --- |
| `onUserRefusedCameraPermissions` | `onUserRefusedDevicePermissions` |


The rename is mechanical - the signature is unchanged and the compiler points at every call site. The new name reflects what the callback has always covered: a device permission the bank's page asked for, rather than the camera specifically.

### `onUserRefusedDevicePermissions` now works on LRS checkouts

Previously the unified `GlomoCheckout` accepted this callback but never forwarded it to the LRS flow, so it could not fire on an LRS order - and LRS had no camera support at all on either platform. Both are fixed. If your integration is LRS-only and you skipped this callback because it never fired, it is now worth supplying.

It remains Android-only: on iOS the WebView raises its own permission prompt and never reports the answer back, so the SDK cannot know what the user chose.

### `paymentId` and `signature` are now optional on payment payloads

```ts
interface GlomoCheckoutPayload {
  orderId: string;
  paymentId?: string; // was: string
  signature?: string; // was: string
}
```

This is a widening, not a new failure mode. The checkout page has always been able to report a terminal outcome before the backend resolved those ids - open banking completes on the bank's side first - and v4 responded by **dropping the outcome entirely** behind a `devMode`-only log. In a release build that meant no callback, no telemetry, and no trace of a payment that actually happened. v5 reports it with the fields it has.

With `strict` on, this surfaces at compile time anywhere you forward either value to something expecting a `string`:

```tsx
onPaymentSuccess={(payload) => {
  // v4: compiled
  // v5: Type 'string | undefined' is not assignable to type 'string'
  confirmOnOurBackend(payload.paymentId);

  // v5: narrow first
  if (payload.paymentId && payload.signature) {
    confirmOnOurBackend(payload.paymentId, payload.signature);
  } else {
    awaitWebhookFor(payload.orderId);
  }
}}
```

That error is the migration, and it is the point: the alternative was forwarding `undefined` to your backend at runtime.

### `onPaymentTerminate` now receives a payload

```ts
interface GlomoTerminationPayload {
  source: TERMINATION_SOURCES; // "checkout_closed" | "device_back_button" | "modal_dismiss"
}
```

**Nothing breaks.** A handler may take fewer parameters than the signature provides, so your existing `() => void` stays assignable and compiles unchanged. Take the payload when you want to tell the page's own close control apart from a device gesture.

The back chevron over a bank flow still does **not** fire this callback, deliberately. It returns the customer to the checkout with the session intact and the ability to pay. If you cancel the order on terminate, wiring the chevron here would cancel live orders every time a customer backed out of one bank to try another.

### A `mock_` public key now runs in mock mode

`isValidPublicKey` accepted `live_`, `mock_` and `test_`, but only `test_` selected mock mode - so a `mock_` key passed validation and then ran against live. If you have been using a `mock_` key and compensating for it, stop.

### New: document downloads (optional)

Some checkout flows offer a document for the customer to save. Install both packages and rebuild to enable it:

```bash
npm install react-native-blob-util @react-native-documents/picker
cd ios && pod install
```

On React Native < 0.79, pin `@react-native-documents/picker` to `10.1.x`.

Leaving them out is supported: the SDK does not advertise the capability, the page keeps its own download behaviour, and nothing breaks. `start()` warns on the console, and `onSdkError` fires once per mount - see below, because that last part changes what `onSdkError` means.

### `onSdkError` can now report a missing capability, not just a misconfiguration

**Check this if your `onSdkError` handler aborts the checkout or shows a blocking error.**

In v4, `onSdkError` only ever meant the integration was misconfigured - a malformed `publicKey`, a missing `orderId` - so treating any error on that callback as fatal was a reasonable reading. In v5 it also fires once per mount, with `type: "validation_error"` and `field: "file.save"`, when the two document-download packages above are not installed. Nothing is wrong in that case: the checkout works and the page keeps its own download path.

If you treat every `onSdkError` as fatal and you have not installed those packages, your checkout will now stop on a condition that is not an error. Filter on `field` rather than on `type`:

```tsx
onSdkError={(errors) => {
  const blocking = errors.filter((e) => e.field !== "file.save");
  if (blocking.length > 0) {
    // your existing handling
  }
}}
```

The report is deliberately not tied to whether a page actually requests a download: whether this build can write a file to the device is a property of the build, so you learn it once at integration rather than the first time a customer taps download in production.

## v1 to v4

v4 replaces the LRS-only `GlomoLrsCheckout` with a unified `GlomoCheckout` that auto-detects order type (LRS, Standard, or Subscriptions) and provides a single `onUserJourneyCompleted` callback for all asynchronous payment flows.

> For the complete v4 API reference, see the [React Native SDK v4 documentation](/platform/sdk/react-native-sdk/v4).
For the full list of changes, see the [Changelog](/platform/sdk/react-native-sdk/changelog).


### Renamed Exports

| v1 | v4 |
|  --- | --- |
| `GlomoLrsCheckout` | `GlomoCheckout` |
| `GlomoLrsCheckoutRef` | `GlomoCheckoutRef` |
| `GlomoLrsCheckoutProps` | `GlomoCheckoutProps` |
| `GlomoLrsCheckoutPayload` | `GlomoCheckoutPayload` |
| `GlomoLrsServer` | `GlomoServer` |
| `LrsCheckoutStatus` | `CheckoutStatus` |
| `useLrsCheckout` | `useGlomoCheckout` (deprecated - use component ref instead) |


### Breaking Changes

- **`start()` is now async** - returns `Promise<boolean>` instead of `boolean`. Update call sites to `await ref.current?.start()`.
- **Asynchronous payment callbacks** - v4 uses a single `onUserJourneyCompleted` callback with an `ASYNC_PAYMENT_EVENTS` enum instead of separate per-flow callbacks. (These callbacks did not exist in v1, so no migration needed - just be aware of the v4 API when adding them.)
- **New statuses** - `detecting_order_type`, `bank_transfer_submitted`, and `pay_via_bank_completed` are additions to `CheckoutStatus`.
- **`onSdkError` is now required** - was optional in v1. Must be provided in v4 to surface validation errors, device compliance failures, and configuration issues.


### Before / After

**v1:**

```tsx
import { GlomoLrsCheckout, type GlomoLrsCheckoutRef } from "@glomopay/react-native-sdk";

const ref = useRef<GlomoLrsCheckoutRef>(null);
const started = ref.current?.start(); // boolean
```

**v4:**

```tsx
import {
  GlomoCheckout,
  ASYNC_PAYMENT_EVENTS,
  type GlomoCheckoutRef,
} from "@glomopay/react-native-sdk";

const ref = useRef<GlomoCheckoutRef>(null);
const started = await ref.current?.start(); // Promise<boolean>

// In your component JSX:
<GlomoCheckout
  ref={ref}
  publicKey="live_pk_abc123"
  orderId="order_xyz789"
  onPaymentSuccess={(payload) => console.log("Success:", payload.paymentId)}
  onPaymentFailure={(payload) => console.log("Failed:", payload.paymentId)}
  onUserJourneyCompleted={(payload) => {
    switch (payload.journeyType) {
      case ASYNC_PAYMENT_EVENTS.BANK_TRANSFER_SUBMITTED:
        console.log("Transfer ref:", payload.transactionReference);
        break;
      case ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED:
        console.log("Pay via bank:", payload.status);
        break;
    }
  }}
/>
```

### Platform Configuration

Checkout flows with camera-based bank authentication require native permissions:

- **Android** (`AndroidManifest.xml`): `<uses-permission android:name="android.permission.CAMERA" />`
- **iOS** (`Info.plist`): `NSCameraUsageDescription` key


These are only required for flows that use camera-based bank authentication.

### New in v4 (no v1 equivalent)

- **Subscriptions checkout** - pass `subscriptionId` instead of `orderId`. See [Subscriptions Checkout](/platform/sdk/react-native-sdk/v4#subscriptions-checkout).
- **Asynchronous payment flows** - `onUserJourneyCompleted` callback with `ASYNC_PAYMENT_EVENTS` enum. See [Asynchronous Payment Flows](/platform/sdk/react-native-sdk/v4#asynchronous-payment-flows).
- **Camera permissions handling** - `onUserRefusedCameraPermissions` callback. See [Camera Permissions](/platform/sdk/react-native-sdk/v4#camera-permissions).
- **Order type auto-detection** - the SDK determines the checkout flow automatically. The `detecting_order_type` status is emitted during detection.
- **Mock mode extended** - `mock_` prefix keys now supported in addition to `test_`.


## v3 to v4

v4 consolidates the asynchronous payment flow callbacks into a single generic callback.

> For the full list of changes, see the [Changelog](/platform/sdk/react-native-sdk/changelog).
For the complete v4 API reference, see the [React Native SDK v4 documentation](/platform/sdk/react-native-sdk/v4).


### Removed Callbacks

| v3 | v4 |
|  --- | --- |
| `onBankTransferSubmitted` | Use `onUserJourneyCompleted` with `journeyType: ASYNC_PAYMENT_EVENTS.BANK_TRANSFER_SUBMITTED` |
| `onPayViaBankCompleted` | Use `onUserJourneyCompleted` with `journeyType: ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED` |
| `onPayViaBankBankConnectionSuccessful` | Removed (no replacement) |


### Removed Types

| v3 | v4 |
|  --- | --- |
| `GlomoBankTransferPayload` | Use `GlomoUserJourneyCompletedPayload` |
| `GlomoPayViaBankConnectionPayload` | Removed |


### New Types

| Type | Purpose |
|  --- | --- |
| `ASYNC_PAYMENT_EVENTS` | Enum of asynchronous payment event types |
| `GlomoUserJourneyCompletedPayload` | Payload for `onUserJourneyCompleted` callback |


### `onSdkError` is now required

`onSdkError` was optional in v3. In v4 it is required. If you were not passing it, add it:

```tsx
onSdkError={(errors) => {
  errors.forEach((e) => console.error(e.type, e.message, e.field));
}}
```

### Unchanged

- `CheckoutStatus` values `bank_transfer_submitted` and `pay_via_bank_completed` remain unchanged. `getStatus()` continues to return granular status values.


### Before / After

**v3:**

```tsx
<GlomoCheckout
  onBankTransferSubmitted={(payload) => {
    console.log("Transfer ref:", payload?.transactionReference);
  }}
  onPayViaBankCompleted={(payload) => {
    console.log("Pay via bank:", payload.status);
  }}
  onPayViaBankBankConnectionSuccessful={(payload) => {
    console.log("Bank connected:", payload?.bankName);
  }}
  // ...other props
/>
```

**v4:**

```tsx
import { ASYNC_PAYMENT_EVENTS } from "@glomopay/react-native-sdk";

<GlomoCheckout
  onUserJourneyCompleted={(payload) => {
    switch (payload.journeyType) {
      case ASYNC_PAYMENT_EVENTS.BANK_TRANSFER_SUBMITTED:
        console.log("Transfer ref:", payload.transactionReference);
        break;
      case ASYNC_PAYMENT_EVENTS.PAY_VIA_BANK_COMPLETED:
        console.log("Pay via bank:", payload.status);
        break;
    }
  }}
  // ...other props
/>
```

## v1 to v3

v3 replaces the LRS-only `GlomoLrsCheckout` with a unified `GlomoCheckout` that auto-detects order type (LRS, Standard, or Subscriptions).

> For the complete v3 API reference, see the [React Native SDK v3 documentation](/platform/sdk/react-native-sdk/v3).
For the full list of changes, see the [Changelog](/platform/sdk/react-native-sdk/changelog).


### Renamed Exports

| v1 | v3 |
|  --- | --- |
| `GlomoLrsCheckout` | `GlomoCheckout` |
| `GlomoLrsCheckoutRef` | `GlomoCheckoutRef` |
| `GlomoLrsCheckoutProps` | `GlomoCheckoutProps` |
| `GlomoLrsCheckoutPayload` | `GlomoCheckoutPayload` |
| `GlomoLrsServer` | `GlomoServer` |
| `LrsCheckoutStatus` | `CheckoutStatus` |
| `useLrsCheckout` | `useGlomoCheckout` |


### Breaking Changes

- **`start()` is now async**: Returns `Promise<boolean>` instead of `boolean`. Update call sites to `await ref.current?.start()`.
- **New statuses**: `bank_transfer_submitted` and `pay_via_bank_completed` are new additions to `CheckoutStatus`.
- **New callbacks**: `onBankTransferSubmitted`, `onPayViaBankCompleted`, `onPayViaBankBankConnectionSuccessful`, and `onUserRefusedCameraPermissions` are available.


### Before / After

**v1:**

```tsx
import { GlomoLrsCheckout, type GlomoLrsCheckoutRef } from "@glomopay/react-native-sdk";

const ref = useRef<GlomoLrsCheckoutRef>(null);
const started = ref.current?.start(); // boolean
```

**v3:**

```tsx
import { GlomoCheckout, type GlomoCheckoutRef } from "@glomopay/react-native-sdk";

const ref = useRef<GlomoCheckoutRef>(null);
const started = await ref.current?.start(); // Promise<boolean>
```

### Platform Configuration (New in v3)

Checkout flows with camera-based bank authentication require native permissions:

- **Android** (`AndroidManifest.xml`): `<uses-permission android:name="android.permission.CAMERA" />`
- **iOS** (`Info.plist`): `NSCameraUsageDescription` key


These are only required for flows that use camera-based bank authentication.

For the full changelog, see the [Changelog](/platform/sdk/react-native-sdk/changelog).

### New in v3

The following features are new in v3 and have no v1 equivalent. No migration is needed for these - they are additive.

- **Subscriptions checkout** - pass `subscriptionId` instead of `orderId` to process subscription payments. See [Subscriptions Checkout](/platform/sdk/react-native-sdk/v3#subscriptions-checkout).
- **Bank transfer support** - `onBankTransferSubmitted` callback for bank transfer flows. See [Bank Transfer Support](/platform/sdk/react-native-sdk/v3#bank-transfer-support).
- **Pay via bank support** - `onPayViaBankCompleted` and `onPayViaBankBankConnectionSuccessful` callbacks. See [Pay via Bank Support](/platform/sdk/react-native-sdk/v3#pay-via-bank-support).
- **Camera permissions handling** - `onUserRefusedCameraPermissions` callback. See [Camera Permissions](/platform/sdk/react-native-sdk/v3#camera-permissions).
- **Order type auto-detection** - the SDK determines the checkout flow automatically. The `detecting_order_type` status is emitted during detection.
- **Mock mode extended** - `mock_` prefix keys now supported in addition to `test_`.


## Related

- [React Native SDK v5 documentation](/platform/sdk/react-native-sdk/v5) - complete v5 API reference
- [React Native SDK v4 documentation (Archived)](/platform/sdk/react-native-sdk/v4) - v4 API reference
- [React Native SDK v3 documentation (Archived)](/platform/sdk/react-native-sdk/v3) - v3 API reference
- [Changelog](/platform/sdk/react-native-sdk/changelog) - all release notes
- [React Native SDK v1 (Archived)](/platform/sdk/react-native-sdk/v1) - legacy version documentation