<!--
 # license: Copyright © 2026 Fiuu. All Rights Reserved. 
 -->

<img src="https://user-images.githubusercontent.com/38641542/74424311-a9d64000-4e8c-11ea-8d80-d811cfe66972.jpg">

# fiuu-mobile-xdk-reactnative

This is a Fiuu React Native payment module, ready to be implemented in any React Native project via npm install. An example application project  
(MOLPayReactExampleProject) is provided for Fiuu XDK framework integration reference.

**Merchant integration (Android + iOS):** see [MERCHANT_GUIDE.md](./MERCHANT_GUIDE.md) for the step-by-step install, native setup, `startFiuu` usage, Google Pay, Apple Pay, callbacks, and checksum.

## Recommended configurations

- Node.js Version: 22.13.0++ (for React Native 0.87 example app)

- Minimum Android SDK Version: 26 ++

- Minimum Android compile SDK: 37 (required by Fiuu Android Library 3.34.41+)

- Minimum React Native version: 0.76 ++

- Xcode version: 15 ++

- Minimum target version: iOS 16

- Fiuu native SDK versions bundled by this library:
  - Android: `Mobile-XDK-Fiuu_Android_Library` **3.34.41** (JitPack)
  - iOS: `FiuuXDKSwift` **1.1.1** (CocoaPods)

## Installation

Follow **[MERCHANT_GUIDE.md](./MERCHANT_GUIDE.md)** for the full merchant walkthrough. Short version:

### Android

1) npm install fiuu-mobile-xdk-reactnative

2) Rebuild the Android app. The library autolinks via React Native CLI (RN 0.76+).

TypeScript types ship with the package (`index.d.ts`). No manual `declare module` stub is required.

### iOS

1) npm install fiuu-mobile-xdk-reactnative

2) cd ios && pod install && cd ..

   `pod install` pulls `FiuuXDKSwift` 1.1.1 automatically via this library's podspec. If CocoaPods reports a Swift/xcframework linking error, enable static frameworks in your Podfile:

   ```
   ENV['USE_FRAMEWORKS'] = 'static'
   ```

3) Add 'App Transport Security Settings > Allow Arbitrary Loads > YES' to the application project info.plist

4) Add 'NSPhotoLibraryUsageDescription' > 'Payment images' to the application project info.plist

5) Add 'NSPhotoLibraryAddUsageDescription' > 'Payment images' to the application project info.plist

6) For Apple Pay, enable the Apple Pay capability on your app target and set `mp_ap_merchant_ID` to your Apple Pay merchant identifier.

## Sample Result

Both Android and iOS return the **raw backend payload as a plain JavaScript object** in your callbacks — not an encoded/double-stringified JSON string.

```
=========================================
Sample successful transaction (JS object):
=========================================

{
  "StatCode": "00",
  "StatName": "captured",
  "TranID": "30824452",
  "Amount": "1.11",
  "Domain": "",
  "VrfKey": "7c34xxxxxxxxxxxxxxxxxxxxxxxx2000",
  "Channel": "credit",
  "OrderID": "1717661730213",
  "Currency": "MYR",
  "ErrorCode": null,
  "ErrorDesc": null
}

Parameter and meaning:

"StatCode" - "00" for Success, "11" for Failed, "22" for Pending.

"Amount" - The transaction amount
"OrderID" - The transaction order ID
"Channel" - The transaction channel description
"TranID" - The transaction ID generated by Fiuu
"Domain" - Your Merchant ID

"VrfKey" - You can verify payment using this formula -> VrfKey = md5(Amount+secret_key+Domain+TranID+StatCode)

* Note: secret_key = Your account Secret Key in https://portal.fiuu.com/

=====================================
Sample failed transaction (JS object):
=====================================

{
  "StatCode": "11",
  "StatName": "failed",
  "TranID": "3930144154",
  "Amount": "1.01",
  "Domain": "",
  "Channel": "GooglePay",
  "OrderID": "1786112102436041689",
  "Currency": "MYR",
  "ErrorCode": "GOOGLEPAY_PE",
  "ErrorDesc": "Payment aborted.\nError Code: 405\nMessage : Signing key fingerprints ..."
}

Failed/canceled payments that include a backend payload are delivered to `errorCallback` as the same raw object shape above (so you can read `ErrorCode` / `ErrorDesc` directly).

=====================================
Sample validation / setup error (string):
=====================================

"Missing or invalid required payment field(s): mp_merchant_ID, mp_verification_key. Please provide valid Fiuu merchant credentials before starting a payment."

These pre-flight errors (missing native module, invalid credentials, no active Activity/window) are plain strings because no backend transaction was created yet.

## Prepare the Payment detail object

```
var paymentDetails = {
    // Optional, REQUIRED when use online Sandbox environment and account credentials.
    'mp_dev_mode': false,

    // Mandatory String. Values obtained from Razer Merchant Services.
    'mp_username' : 'username',
    'mp_password' : 'password',
    'mp_merchant_ID' : 'merchantid',
    'mp_app_name' : 'appname',
    'mp_verification_key' : 'vkey123', 

    // Mandatory String. Payment values.
    'mp_amount' : '1.10',, // Minimum 1.01
    'mp_order_ID' : 'orderid123', 
    'mp_currency' : 'MYR',
    'mp_country' : 'MY',  
    
    // Optional String.
    'mp_channel' : '', // Use 'multi' for all available channels option. For individual channel seletion, please refer to https://github.com/RazerMS/molpay-mobile-xdk-examples/blob/master/channel_list.tsv.
    'mp_bill_description' : '',
    'mp_bill_name' : '',
    'mp_bill_email' : '',
    'mp_bill_mobile' : '',
    'mp_channel_editing' : false, // Option to allow channel selection.
    'mp_editing_enabled' : false, // Option to allow billing information editing.
    
    // Optional, but required payment values. User input will be required when values not passed.
    'mp_channel' : 'multi', // Use 'multi' for all available channels option. For individual channel seletion, please refer to https://github.com/RazerMS/molpay-mobile-xdk-examples/blob/master/channel_list.tsv.
    'mp_bill_description' : 'billdesc',
    'mp_bill_name' : 'billname',
    'mp_bill_email' : 'email@domain.com',
    'mp_bill_mobile' : '+1234567',

    // Optional, allow channel selection. 
    'mp_channel_editing' : false,

    // Optional, allow billing information editing.
    'mp_editing_enabled' : false,

    // Optional, for Escrow.
    'mp_is_escrow': '0', // Put "1" to enable escrow

    // Optional, for credit card BIN restrictions and campaigns.
    'mp_bin_lock' : ['414170', '414171'],   

    // Optional, for mp_bin_lock alert error.
    'mp_bin_lock_err_msg': 'Only UOB allowed',
    
    // WARNING! FOR TRANSACTION QUERY USE ONLY, DO NOT USE THIS ON PAYMENT PROCESS.
    // Optional, provide a valid cash channel transaction id here will display a payment instruction screen. Required if mp_request_type is 'Receipt'.
    'mp_transaction_id': '',
    // Optional, use 'Receipt' for Cash channels, and 'Status' for transaction status query.
    'mp_request_type': '',

    // Optional, use this to customize the UI theme for the payment info screen, the original XDK custom.css file can be obtained at https://github.com/RazerMS/molpay-mobile-xdk-examples/blob/master/custom.css.
    'mp_custom_css_url': '',

    // Optional, set the token id to nominate a preferred token as the default selection, set "new" to allow new card only.
    'mp_preferred_token': '',

    // Optional, credit card transaction type, set "AUTH" to authorize the transaction.
    'mp_tcctype': '',

    // Optional, required valid credit card channel, set true to process this transaction through the recurring api, please refer the Razer Merchant Services Recurring API pdf. 
    'mp_is_recurring': false,

    // Optional, show nominated channels.
    'mp_allowed_channels': ['credit', 'credit3'],

    // Optional, simulate offline payment, set boolean value to enable. 
    'mp_sandbox_mode': true,

    // Optional, required a valid mp_channel value, this will skip the payment info page and go direct to the payment screen.
    'mp_express_mode': true,

    // Optional, extended email format validation based on W3C standards.
    'mp_advanced_email_validation_enabled': true,

    // Optional, extended phone format validation based on Google i18n standards.
    'mp_advanced_phone_validation_enabled': true,

    // Optional, explicitly force disable user input.
    'mp_bill_name_edit_disabled': true,
    'mp_bill_email_edit_disabled': true,
    'mp_bill_mobile_edit_disabled': true,
    'mp_bill_description_edit_disabled': true,

    // Optional, EN, MS, VI, TH, FIL, MY, KM, ID, ZH.
    'mp_language': 'EN',

    // Optional, Cash channel payment request expiration duration in hour.
    'mp_cash_waittime': 48,
    
    // Optional, allow bypass of 3DS on some credit card channels.
    'mp_non_3DS': true,

    // Optional, disable card list option.
    'mp_card_list_disabled': true,

    // Optional for channels restriction, this option has less priority than mp_allowed_channels.
    'mp_disabled_channels': ['credit']  
    
};
```

## Start the payment module

Always provide **both** callbacks so your app can handle success, failure, and cancellation reliably.

```
//import Fiuu payment package
import fiuupayment from 'fiuu-mobile-xdk-reactnative';

//start Fiuu payment
fiuupayment.startFiuu(
  paymentDetails,
  function (data) {
    // Verify the transaction on your backend. Do not log raw payloads in production.
    if (__DEV__) {
      const statCode =
        typeof data === 'object' && data !== null
          ? String(data.StatCode ?? data.status_code ?? '')
          : '';
      console.log('[Fiuu] Payment success callback', {
        payloadType: typeof data,
        statCode: statCode || '(none)',
      });
    }
  },
  function (error) {
    if (__DEV__) {
      const statCode =
        typeof error === 'object' && error !== null
          ? String(error.StatCode ?? error.status_code ?? '')
          : '';
      console.log('[Fiuu] Payment error callback', {
        payloadType: typeof error,
        statCode: statCode || '(none)',
      });
    }
  }
);
```

## Payment callbacks & response format

| Callback | When it fires | Payload type | Example |
|----------|---------------|--------------|---------|
| `successCallback` | `StatCode` is `"00"` | **Object** | `{ StatCode: "00", TranID: "...", ... }` |
| `errorCallback` | `StatCode` is `"22"` (pending) | **Object** (raw backend JSON) | `{ StatCode: "22", TranID: "...", ... }` |
| `errorCallback` | `StatCode` is `"11"` or other failure codes | **Object** (raw backend JSON) | `{ StatCode: "11", ErrorCode: "...", ErrorDesc: "..." }` |
| `errorCallback` | Missing credentials, native module not linked, no Activity/window | **String** | `"Missing or invalid required payment field(s): ..."` |

**Platform parity (Android & iOS):**

- Both platforms normalize responses to a **plain JS object** before invoking your callback.
- Do **not** call `JSON.stringify()` on the callback argument unless you explicitly need a string for logging/storage — the library already parses the backend JSON for you.
- Route business logic using `StatCode`:
  - `"00"` → `successCallback` — payment captured/successful
  - `"22"` → `errorCallback` with object payload — pending (cash channels); not a final success
  - `"11"` → `errorCallback` — failed/canceled — inspect `ErrorCode` and `ErrorDesc`

**Example app reference:** see `example/App.tsx` for a merchant-style integration that captures both callbacks into app state and displays the last response.

## React Native android below than 0.47.1 build error

Navigate to `node_modules/fiuu-mobile-xdk-reactnative/android/src/main/java/com/fiuu/xdk/reactnative/FiuuPaymentPackage.java` and add the below code. 

```
    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }
```

## Environment Configuration

The library supports multiple environments. You can configure which environment to use by setting the `mp_core_env` value.

`mp_core_env` is a string ("1", "2", "3", "4")

Each value maps to a specific environment base URL.

If no value is set, the default environment will be used.

| `mp_core_env` Value | Environment     | Base URL                            |
|---------------------|-----------------|-------------------------------------|
| `1`                 | Production - V1 | `https://pay.fiuu.com/RMS/API/xdk/` |
| `2`                 | Production - V2 | `https://xdk.fiuu.com/`             |
| `3`                 | UAT - V2        | `https://uat-xdk.fiuu.com/`         |
| `4`                 | Sandbox -V2     | `https://sandbox-xdk.fiuu.com/`     |
| *Default*           | Production - V2 | `https://xdk.fiuu.com/`             |

## Google Pay integration (Android)

Google Pay is **Android-only**. Omit `mp_channel` to launch the Google Pay flow automatically.

Prepare paymentDetails :

```
// TODO: Add these on Google Pay tap action

var paymentDetails = {
    /*
    TODO: Follow Google’s instructions to request production access for your app: https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access
    *
    Choose the integration type Gateway when prompted, and provide screenshots of your app for review.
    After your app has been approved, test your integration in production by set mp_sandbox_mode = false & use production mp_verification_key & mp_merchant_ID.
    Then launching Google Pay from a signed, release build of your app.

    Your app package name + signing certificate SHA-1 fingerprint must be registered with
    Google Pay / Fiuu for the merchant account — debug builds will fail with signing-key
    mismatch errors until registered.
    */
    'mp_sandbox_mode': true,
    
    // TODO : Enter your credentials
    'mp_merchant_ID': '',
    'mp_verification_key': '',
    
    'mp_order_ID': 'order1234567890', // Unique order id — generate a new one per attempt
    'mp_currency': 'MYR',
    'mp_country': 'MY',
    
    'mp_amount': '1.23', // Minimum 1.00 must be in 2 decimal points format
    'mp_bill_description': 'Test Google Pay',
    'mp_bill_name': 'Google Pay',
    'mp_bill_email': 'testing@gmail.com',
    'mp_bill_mobile': '123456789',
    
    'mp_extended_vcode': false, // Optional : Set true if your account enabled extended Verify Payment
};
```

```
import fiuupayment from 'fiuu-mobile-xdk-reactnative';

fiuupayment.startFiuu(
  paymentDetails,
  (data) => console.log('Google Pay success:', data),   // raw object
  (error) => console.log('Google Pay error:', error)     // raw object OR string
);
```

## Apple Pay integration (iOS)

Apple Pay is **iOS-only**.

Prepare paymentDetails :

```
var paymentDetails = {
    'mp_express_mode': true,
    'mp_allowed_channels': ['ApplePay'],
    'mp_channel': 'ApplePay',

    // TODO : Enter your credentials
    'mp_merchant_ID': '',
    'mp_verification_key': '',
    // TODO : Apple Pay merchant ID from Apple Developer portal
    'mp_ap_merchant_ID': '',

    'mp_order_ID': 'order1234567890', // Unique order id — generate a new one per attempt
    'mp_currency': 'MYR',
    'mp_country': 'MY',

    'mp_amount': '1.23',
    'mp_bill_description': 'Test Apple Pay',
    'mp_bill_name': 'Apple Pay',
    'mp_bill_email': 'testing@gmail.com',
    'mp_bill_mobile': '123456789',

    'mp_extended_vcode': false,
};
```

```
import fiuupayment from 'fiuu-mobile-xdk-reactnative';

fiuupayment.startFiuu(
  paymentDetails,
  (data) => console.log('Apple Pay success:', data),   // raw object
  (error) => console.log('Apple Pay error:', error)     // raw object OR string
);
```

Requirements:

1. Valid Fiuu merchant account with Apple Pay enabled
2. Apple Pay capability enabled on your app bundle ID
3. `mp_ap_merchant_ID` set to your Apple Pay merchant identifier
4. Test on a real device with Apple Pay configured (simulator support is limited)

## Payment results - Google Pay / Apple Pay

```
=========================================
Sample transaction result (JS object):
=========================================

{
    "StatCode":"00",
    "StatName":"captured",
    "TranID":"30824452",
    "Amount":"1.11",
    "Domain":"",
    "VrfKey":"7c34xxxxxxxxxxxxxxxxxxxxxxxx2000",
    "Channel":"credit",
    "OrderID":"1717661730213",
    "Currency":"MYR",
    "ErrorCode":null,
    "ErrorDesc":null
}

Your `successCallback` receives this object directly — access fields with `data.StatCode`, `data.TranID`, etc.

Parameter and meaning:

"StatCode" - "00" for Success, "11" for Failed, "22" for Pending. 

"Amount" - The transaction amount
"OrderID" - The transaction order ID
"Channel" - The transaction channel description
"TranID" - The transaction ID generated by Fiuu
"Domain" - Your Merchant ID

"VrfKey" - You can verify payment using this formula -> VrfKey = md5(Amount+secret_key+Domain+TranID+StatCode)

* Note: secret_key = Your account Secret Key in https://portal.fiuu.com/
* Notes: You may ignore other parameters and values not stated above

=====================================
Sample error result (JS object):
=====================================

{
    "StatCode": "11",
    "StatName": "failed",
    "TranID": "3930144154",
    "Amount": "1.01",
    "Domain": "",
    "Channel": "GooglePay",
    "OrderID": "1786112102436041689",
    "Currency": "MYR",
    "ErrorCode": "GOOGLEPAY_PE",
    "ErrorDesc": "Payment aborted."
}

Your `errorCallback` receives this object directly when the backend returns a failed payload.

{
    "status":false,
    "error_code":"P03",
    "error_desc":"Your payment info format not correct."
}

{
    "error_code" = A01;
    "error_desc" = "Fail to detokenize Google Pay Token given";
    status = 0;
}

Parameter and meaning:

Error P03 - Your payment info format not correct   
1) Need makesure all required parameters filled correctly.
2) Need set mp_extended_vcode = true if enabled extended Verify Payment.

Error A01 - "Fail to detokenize Google Pay Token given" - Error starting a payment process due to several possible reasons, please contact Razer Merchant Services support should the error persists.
1) Misconfigure GooglePay setup
2) API credentials (username, password, merchant id, verify key)
3) Razer Merchant Services server offline.
```

## Example app UI notes

The bundled `example/` app demonstrates merchant-style callback handling (`example/App.tsx`).

**Android:** the example uses `Theme.AppCompat.Light.NoActionBar` to prevent the native action bar from flashing when returning from the Fiuu payment Activity. Merchants embedding the library in their own app should use a `NoActionBar` theme (or hide the action bar in their host Activity) for the same reason.

**iOS:** the library presents the Fiuu SDK inside a full-screen modal with the navigation bar hidden to reduce UI flicker when the payment sheet closes.

## Cash channel payment process (How does it work?)

    This is how the cash channels work on XDK:
    
    1) The user initiate a cash payment, upon completed, the XDK will pause at the “Payment instruction” screen, the results would return a pending status.
    
    2) The user can then click on “Close” to exit the Razer Merchant Services XDK aka the payment screen.
    
    3) When later in time, the user would arrive at say 7-Eleven to make the payment, the host app then can call the XDK again to display the “Payment Instruction” again, then it has to pass in all the payment details like it will for the standard payment process, only this time, the host app will have to also pass in an extra value in the payment details, it’s the “mp_transaction_id”, the value has to be the same transaction returned in the results from the XDK earlier during the completion of the transaction. If the transaction id provided is accurate, the XDK will instead show the “Payment Instruction" in place of the standard payment screen.
    
    4) After the user done the paying at the 7-Eleven counter, they can close and exit Razer Merchant Services XDK by clicking the “Close” button again.

## XDK built-in checksum validator caveats 

    All XDK come with a built-in checksum validator to validate all incoming checksums and return the validation result through the "mp_secured_verified" parameter. However, this mechanism will fail and always return false if merchants are implementing the private secret key (which the latter is highly recommended and prefereable.) If you would choose to implement the private secret key, you may ignore the "mp_secured_verified" and send the checksum back to your server for validation. 

## Private Secret Key checksum validation formula

    chksum = MD5(mp_merchant_ID + results.msgType + results.txn_ID + results.amount + results.status_code + merchant_private_secret_key)

## Development & testing (maintainers)

Run these from the repository root before publishing:

```bash
npm test              # JS validation + callback wiring tests
npm run pack:check    # verify only library files ship to npm (no example/)
npm pack              # runs tests + pack:check, then creates *.tgz
```

Run the example app against the local library (`example/package.json` uses `"file:.."`):

```bash
cd example
npm install
npm start             # Metro bundler
npm run android       # or: npm run ios
```

Android build verification (no device required):

```bash
cd example/android && ./gradlew app:assembleDebug
```

On device/emulator, install and exercise both demo buttons:

```bash
cd example/android && ./gradlew app:installDebug
adb shell am start -n com.fiuu.xdkandroid/.MainActivity
```

Use real sandbox credentials in `example/App.tsx` for a live payment test. Placeholder credentials should show a validation error alert instead of crashing.

## Support

Submit issue to this repository or email to our support-sa@fiuu.com

Merchant Technical Support / Customer Care : support@fiuu.com<br>
Sales/Reseller Enquiry : sales@fiuu.com<br>
Marketing Campaign : marketing@fiuu.com<br>
Channel/Partner Enquiry : channel@fiuu.com<br>
Media Contact : media@fiuu.com<br>
R&D and Tech-related Suggestion : technical@fiuu.com<br>
Abuse Reporting : abuse@fiuu.com
