Javascript SDK

The MoneyHash JavaScript SDK allows developers to integrate MoneyHash payment processing into their web applications. The SDK provides a simple and convenient way to accept customer payments, manage subscriptions, and send payouts.

Prerequisites

Below, you will find all you need to do before integrating MoneyHash with Javascript SDK:

  1. Get Started with MoneyHash to access your own Organization.
  2. Create an Account within your Organization.
  3. Connect providers to your new Account.
  4. Set up your Payment Defaults.
  5. Get your API keys in the dashboard to be able to make API calls.

Installing

To install MoneyHash's Javascript SDK, you need to use one of the commands below:

$ npm install @moneyhash/js-sdk

# or

$ yarn add @moneyhash/js-sdk

Integrating

After installing MoneyHash's Javascript SDK into your application, you can start the integration. Below, you will find the essential steps to use this integration:

  1. First, create an intent with the Payment intent endpoint. This step does not use MoneyHash's Javascript SDK and is standard for all MoneyHash's integrations, except for HPP. This endpoint requires authentication to be executed properly. You need to provide the Account API Key as a header and send the required data in the request body. Check the Create an Intent page for further explanation.

POST
/api/v1.1/payments/intent/
  1. Create a moneyHash instance using MoneyHash constructor. At this step, you need to define whether you will create a payment or a payout.
import MoneyHash from "@moneyhash/js-sdk/headless";

const moneyHash = new MoneyHash({ type: "payment" | "payout" });
  1. Get intent details: Calling the getIntentDetails method with the intent_id from the intent created at Step 1 as the parameter, you can access your intent details. The state returned can guide you through the actions and methods required to proceed and complete the payment or payout. The table below describes each action related to each possible state value.
moneyHash
  .getIntentDetails("<intent_id>")
  .then(({ intent, transaction, selectedMethod, redirect, state }) => {
    console.log({ intent, transaction, selectedMethod, redirect, state });
  });
stateAction
METHOD_SELECTIONUse moneyHash.getIntentMethods to get the different payment methods available for the intent. You can render them natively with your own styles and use moneyHash.proceedWith to proceed with one of them after the user selection.
INTENT_FORMUse moneyHash.renderForm to render the SDK embed to let MoneyHash handle the payments for you. Use the available onComplete and onFail callbacks on the MoneyHash instance to track the end of the process.
INTENT_PROCESSEDRender your successful confirmation UI with the intent details.
TRANSACTION_FAILEDRender your failure UI with the intent details.
TRANSACTION_WAITING_USER_ACTIONRender your pending actions confirmation UI with the intent details and externalActionMessage if exists on Transaction.
EXPIREDRender your intent expired UI.
CLOSEDRender your intent closed UI.

Render SDK embed forms and payment integrations

If the state returned is INTENT_FORM you need to call the renderForm method to let MoneyHash handle the payment/payout. You need to provide the CSS selector to define where to render the forms and the intent identifier.

await moneyHash.renderForm({ selector: "<container_css_selector>", intentId: "<intent_id>", });
  1. Get intent methods: To access the available pay-in/pay-out methods, saved cards, and customer balances, call the getIntentMethods sending the intent_id as the parameter. For example, you could use this information to predefine a payment method. Or choose which paymentMethods to display to give the customer the option to choose their preferred method.
moneyHash
  .getIntentMethods("<intent_id>")
  .then(({ paymentMethods, expressMethods, savedCards, customerBalances }) => {
    console.log({
      paymentMethods,
      expressMethods,
      savedCards,
      customerBalances,
    });
  });
  1. Proceed with payment: Using the proceedWith method, you can proceed with the payment process. You are required to inform the intentId, type and id of the payment method to execute this method.
moneyHash
  .proceedWith({
    intentId: "<intent_id>",
    type: "method" | "savedCard" | "customerBalance",
    id: "<method_id>" | "<card_id>" | "<customer_balance_id>",
    metaData: {
      cvv: "<cvv>", // required for customer saved cards that requires cvv
    },
  })
  .then(({ intent, transaction, selectedMethod, redirect, methods, state }) => {
    console.log({
      intent,
      transaction,
      selectedMethod,
      redirect,
      methods,
      state,
    });
  });

Other available Javascript SDK methods

In addition to the essential steps and methods previously described, the Javascript SDK provides other methods to customize the user experience. These additional methods are presented and described next.

  • Reset selected method: Use the resetSelectedMethod method for resetting the payment method selected by the user. You can provide this option to:
    • Give the customer a button as an option to go back after they already selected a payment method.
    • Offer a retry button so your customer can select a different payment method after a failed transaction.
moneyHash
  .resetSelectedMethod("<intent_id>")
  .then(({ intent, transaction, selectedMethod, methods, state }) => {
    console.log({
      intent,
      transaction,
      selectedMethod,
      methods,
      state,
    });
  });
  • Delete card: Call the deleteCard method to delete a customer's saved card from the system. You can use this option when listing the existing customer saved cards.
moneyHash
  .deleteCard({
    cardId: "<card_id>",
    intentSecret: "<intent_secret>",
  })
  .then(({ message }) => {
    console.log({ message });
  });

Customization

With MoneyHash's Javascript SDK integrated with your application, you can customize styles of inputs and buttons. To customize them, you can send the parameter styles with configurations to each button when creating the MoneyHash instance, as shown below:

const moneyHash = new MoneyHash({
  styles: {
    submitButton: {
      base: {},
      hover: {},
      focus: {},
    },
    input: {
      base: {},
      focus: {},
      error: {},
    },
    loader: {
      backgroundColor: '',
      color: '',
    }
  },
});

Submit Button

When using the customization option for the submit buttons, you need to follow the TS interfaces provided below, which also present the available customization options.

interface TextStyle {
  color?: string;
  fontFamily?: string;
  fontWeight?: string;
  fontSize?: string;
  fontSmoothing?: string;
  lineHeight?: string;
  textTransform?: string;
  letterSpacing?: string;
}

interface BlockStyle {
  background?: string;
  borderRadius?: number | string;
  boxShadow?: string;
  borderStyle?: string;
  borderColor?: number | string;
  borderWidth?: number | string;
}

export interface ButtonStyle {
  base?: TextStyle & BlockStyle;
  hover?: TextStyle & BlockStyle;
  focus?: TextStyle & BlockStyle;
}

Input

When using the customization option for theinputs, you need to follow the TS interfaces provided below, which also present the available customization options.

interface AllowedInputStyle {
  height?: number;
  padding?: number;

  background?: string;
  borderRadius?: number | string;
  boxShadow?: string;

  borderStyle?: string;
  borderColor?: string;
  borderWidth?: number | string;

  color?: string;
  fontFamily?: string;
  fontWeight?: string;
  fontSize?: string;
  fontSmoothing?: string;
  lineHeight?: string;
}

Loader

When using the customization option for the loader, you need to follow the TS interfaces provided below, which also present the available customization options.

interface LoaderStyle {
  backgroundColor: string;
  color: string;
}

Event Listeners

To stay up to date with the process, you can use the onComplete and/or onFail callback methods when creating the MoneyHash instance.

onComplete

The onComplete will be called if the payment/payout process is completed successfully. The next code block presents an example of how to use it.

const moneyHash = new MoneyHash({
  onComplete: ({ intent, transaction, selectedMethod, redirect, state }) => {
    console.log("onComplete", {
      intent,
      transaction,
      selectedMethod,
      redirect,
      state,
    });
  },
});

onFail

The onFail will be called if the payment/payout fails due to some problem. The next code block presents an example of how to use it.

const moneyHash = new MoneyHash({
  onFail: ({ intent, transaction, selectedMethod, redirect, state }) => {
    console.log("onFail", {
      intent,
      transaction,
      selectedMethod,
      redirect,
      state,
    });
  },
});

Use predefined locale

When creating MoneyHash's instance, you can predefine the locale of the checkout you are about to handle. Provide this information through the locale field.

const moneyHash = new MoneyHash({
  type: "payment" | "payout",
  locale: "ar-EG",
});

Change intent language programmatically

You can choose to change the language of the experience within your application. To do so, you need to call the setLocale method and pass as its only parameter a string of the <locale_code>. Currently, we support three languages: English, Arabic, and Français.

await moneyHash.setLocale("<locale_code>");

Native Pay

Apple pay

Based on the provider, apple pay could require billing data before showing Apple pay dialog, so we recommend sending all billing data while creating the intent.

Billing data is provided while creating the intent

In this case, rendering Apple pay button with onClick handler that calls moneyHash.payWithApplePay directly should show Apple Pay dialog allowing the customer to pay.

🚧

payWithApplePay uses ApplePaySession native method under the hood that exits in safari browsers, which requires ApplePaySession to be called right after gesture handler.

moneyHash.payWithApplePay({
  intentId,
  amount: <<TOTAL_PRICE>>,
  currency: "CURRENCY",
  countryCode: "<<COUNTRY_CODE>>",
  onCancel: () => {},
  onComplete: () => {

  },
  onError: () => {

  },
});

📘

If there are any missing billing data while trying to call payWithApplePay, MoneyHash SDK will throw an error Billing data is missing while calling payWithApplePay

Billing data is not provided while creating the intent

Pass billing data while calling payWithApplePay

MoneyHash informs you beforehand, the required billing data in expressMethods, the payload shows as follows:

{
  "id": "APPLE_PAY",
  "title": "Apple Pay",
  "icons": [
    "https://cdn.moneyhash.io/images/checkout_icons/ApplePay.svg"
  ],
  "isSelected": false,
  "confirmationRequired": false,
  "requiredBillingFields": [
    {
      "type": "EmailField",
      "field_name": "email",
      "value": "",
      "choices": null,
      "label": "Email",
      "max_length": null,
      "help_text": "Customer's email address",
      "required": true,
      "min_length": null,
      "read_only": false,
      "error_messages": {
        "required": "This field is required.",
        "null": "This field may not be null.",
        "invalid": "Enter a valid email address.",
        "blank": "This field may not be blank.",
        "max_length": "Ensure this field has no more than {max_length} characters.",
        "min_length": "Ensure this field has at least {min_length} characters."
      }
    }
  ]
}

Notice requiredBillingFields which you can use to pass the required billing data to payWithApplePay

moneyHash.payWithApplePay({
  intentId,
  amount: totalPrice,
  currency,
  countryCode: "AE",
  onCancel: () => {},
  onComplete: () => {},
  billingData: {
    // pass required billing data here
    email: "[email protected]"
  },
  onError: () => {},
});

📘

requiredBillingFields is a dynamic set of fields that varies from a provider to another, make sure all billing fields mentioned are passed, other wise MoneyHash SDK will throw an error Billing data is missing while calling payWithApplePay

Show MoneyHash form to collect billing data

If you don't wish to collect customer's billing data as part of your app, you can use moneyhash.renderForm method which will fire onComplete event once the user fills in their billing data, and state should be NATIVE_PAY

moneyHash = new MoneyHash({
  type: "payment",
  onComplete: data => {
    // Now you can show Apple pay button once the filling of the form is done
    // Add a button to the dom or show a confirmation modal that contains Apple pay button
    console.log("onComplete", data);
  },
  onFail: ({ intent, transaction }) => {
    console.log("onFail", { intent, transaction });
  },
});

The onClick handler should be moneyhash.payWithApplePay

Error Responses

You may face issues and receive error codes and messages when using the Javascript SDK. To help you proceed and organize your code, we provide all possible error response types for the methods you can use on the Javascript SDK.

export type ErrorResponse = {
  code: number;
  message: string;
};
export type IntentType = "payment" | "payout";

export type IntentStatus =
  | "PROCESSED"
  | "UNPROCESSED"
  | "CLOSED"
  | "TIME_EXPIRED"
  | "PENDING"
  | "EXPIRED";

export type PaymentMethodSlugs =
  | "CASH_OUTLET"
  | "MOBILE_WALLET"
  | "CARD"
  | "USSD"
  | "KNET"
  | "CASH_COLLECTION"
  | "AMAN_MASARY"
  | "PAYPAL"
  | "PAY_AT_FAWRY"
  | "VALU"
  | "SHAHRY"
  | "CASH_ON_DELIVERY"
  | "BANK_INSTALLMENTS"
  | "BANK_TRANSFERS"
  | "REFERENCE_NUMBER"
  | "SELFSERVE_WALLET"
  | "APPLE_PAY"
  | "GOOGLE_PAY"
  | "M_PESA"
  | "MOBILE_MONEY"
  | "CRYPTO_WALLET"
  | "NAPS"
  | "FORSA"
  | "SYMPL"
  | "TABBY"
  | "SOUHOOLA"
  | "GETGO"
  | "SAMSUNG_PAY"
  | "QPAY"
  | "TAMARA"
  | "BENEFIT"
  | "STC"
  | "BANK_ACCOUNT"
  | "CASH";

export type IntentState =
  | "METHOD_SELECTION"
  | "INTENT_FORM"
  | "INTENT_PROCESSED"
  | "TRANSACTION_WAITING_USER_ACTION"
  | "TRANSACTION_FAILED"
  | "EXPIRED"
  | "CLOSED"
  | "NATIVE_PAY";

export type PurchaseOperationStatus =
  | "pending"
  | "pending_authentication"
  | "pending_external_action"
  | "pending_online_external_action"
  | "pending_authorization"
  | "failed"
  | "successful";

export type AuthorizeOperationStatus =
  | "pending"
  | "pending_authentication"
  | "failed"
  | "successful";

export type CaptureOperationStatus =
  | "pending"
  | "pending_authentication"
  | "failed"
  | "successful";

export type VoidOperationStatus = "pending" | "failed" | "successful";
export type RefundOperationStatus = "pending" | "failed" | "successful";

type TransactionOperationStatusMap = {
  purchase: PurchaseOperationStatus;
  authorize: AuthorizeOperationStatus;
  capture: CaptureOperationStatus;
  refund: RefundOperationStatus;
  void: VoidOperationStatus;
  increase_authorization: AuthorizeOperationStatus;
};

type TransactionStatus = {
  [k in keyof TransactionOperationStatusMap]: `${k}.${TransactionOperationStatusMap[k]}`;
}[keyof TransactionOperationStatusMap];

type TransactionOperation = {
  [k in keyof TransactionOperationStatusMap]: {
    id: string;
    type: k;
    status: `${TransactionOperationStatusMap[k]}`;
    amount: {
      value: number;
      currency: string;
    };
    statuses: {
      id: string;
      value: `${TransactionOperationStatusMap[k]}`;
      code: string;
      message: string;
      created: string;
    }[];
    refund_type?: "full" | "partial" | null;
  };
}[keyof TransactionOperationStatusMap];

export interface AbstractIntent {
  id: string;
  status: IntentStatus;
  amount: {
    value: string;
    currency: string;
    formatted: number;
    maxPayout?: number | null;
  };
  secret: string;
  isLive: boolean;
}

export interface PaymentIntent extends AbstractIntent {
  expirationDate: string | null;
}

export interface PayoutIntent extends AbstractIntent {}

export interface Transaction {
  id: string;
  status: TransactionStatus;
  operations: TransactionOperation[];
  createdDate: string;
  billingData: {
    apartment: string | null;
    building: string | null;
    city: string | null;
    country: string | null;
    email: string | null;
    first_name: string | null;
    floor: string | null;
    last_name: string | null;
    name: string | null;
    phone_number: string | null;
    postal_code: string | null;
    state: string | null;
    street: string | null;
  };
  customFields: Record<string, unknown> | null;
  providerTransactionFields: Record<string, unknown>;
  externalActionMessage: string[];
}

export interface PaymentTransaction extends Transaction {
  amount: {
    value: number;
    currency: string;
  };
  paymentMethodName: string;
  paymentMethod: PaymentMethodSlugs;
  customFormAnswers: {
    formFields: Record<string, string | number | boolean>;
  } | null;
}

export interface PayoutTransaction extends Transaction {
  amount: {
    value: string;
    currency: string;
  };
  payoutMethodName: string;
  payoutMethod: PaymentMethodSlugs;
}

export interface Redirect {
  redirectUrl: string;
}

export interface Method {
  id: PaymentMethodSlugs;
  title: string;
  icons: string[];
  isSelected: boolean;
  confirmationRequired: boolean;
  requiredBillingFields: FormField[] | null;
}
export interface Card {
  id: string;
  brand: string;
  logo: string;
  last4: string;
  expiryMonth: string;
  expiryYear: string;
  country: string | null;
  requiresCvv: boolean;
}

export type CustomerBalances = [
  {
    id: "SELFSERVE_WALLET";
    balance: number;
    icon: string;
    isSelected: boolean;
  },
];

export type IntentMethods<TType extends IntentType> = TType extends "payment"
  ? {
      paymentMethods: Method[];
      expressMethods: Method[];
      savedCards: Card[];
      customerBalances: CustomerBalances;
    }
  : {
      payoutMethods: Method[];
    };

export type FormField = {
  choices?: Record<string, string> | null;
  error_messages: {
    blank: string;
    null?: string;
    required: string;
    invalid: string;
    min_length: string;
    max_length: string;
  };
  field_name: string;
  help_text?: string | null;
  label?: string;
  max_length?: number | null;
  min_length?: number | null;
  read_only: boolean;
  required: boolean;
  type:
    | "PhoneNumberField"
    | "ChoiceField"
    | "CharField"
    | "IntegerField"
    | "EmailField"
    | "DateField";
  value: string;
};


Embed Experience

With the Javascript SDK, you still have the option to choose a more simple solution by applying the embedded version of the Javascript SDK. This option requires fewer configurations and steps, which will work similarly to our Embedded integration.

To use the Embed Experience, you can follow the next steps. Some of them are similar or equal to the ones presented earlier on this page.

  1. First, create an intent with the Payment intent endpoint. This step does not use MoneyHash's Javascript SDK and is standard for all MoneyHash's integrations, except for HPP. This endpoint requires authentication to be executed properly. You need to provide the Account API Key as a header and send the required data in the request body. Check the Create an Intent page for further explanation.

POST
/api/v1.1/payments/intent/
  1. Create a moneyHash instance using MoneyHash constructor. At this step, you need to define whether you will create a payment or a payout.
import MoneyHash from "@moneyhash/js-sdk";

const moneyHash = new MoneyHash({ type: "payment" | "payout" });
  1. Render the iframe: The entire payment process will be rendered inside the iframe. To use it, you need to call the start method. You also need to define the CSS selector where the iframe should be rendered, along with the intentId. After calling the start method, the payment checkout will be available for your customer.
await moneyHash.start({
  selector: "<container_css_selector>",
  intentId: "<intent_id>",
});

Event listeners

When using the Embed Experience, you are still able to set event listeners. The procedure is the same as presented above.

Notifications

After integrating with MoneyHash through the Embedded Experience, it's recommended you learn how to configure and use Webhooks and Redirects to be able to receive notifications and automatically redirect your customer.