Basic concepts

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.

import MoneyHash from '@moneyhash/js-sdk/headless';

const moneyHash = new MoneyHash({
  type: "payment" | "payout",
  publicApiKey: "<account_public_api_key>", // available for payment type only
});

SDK States

One of our SDK features is offering a single state for each step until the completion. The state along with stateDetails can guide you through the actions and methods required to proceed and complete the flow. The table below describes a brief of each action related to each possible state value.

stateActionIntent Type
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.Payment Payout
FORM_FIELDSRender the form fields received inside stateDetails, whether card fields by rendering card fields or the billing and shipping fields using the received schema. Then submit them using moneyhash.submitForm.Payment Payout
URL_TO_RENDERRender the received url from stateDetails and based on the using the recommended renderStrategy. Use moneyhash.renderUrl listed here to render the URL.Payment Payout CardToken
SAVED_CARD_CVVRender an input field to collect CVV using the schema received from stateDetails under cvvField property. Also you can get basic card information that you are collecting the CVV for under card property. Then use moneyhash.submitCvv method to send the CVV and proceed with the payment.Payment
INSTALLMENT_PLANSShow list of installment plans are available for the card user entered. The user can select the preferred plan to continue the payment with. More on installment plans here .Payment
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.Payment Payout
INTENT_PROCESSEDRender your successful confirmation UI with the intent details.Payment Payout
TRANSACTION_FAILEDRender your failure UI with the intent details.Payment Payout
TRANSACTION_WAITING_USER_ACTIONRender your pending actions confirmation UI with the intent details and externalActionMessage if exists on Transaction.Payment Payout
EXPIREDRender your intent expired UI.Payment
CLOSEDRender your intent closed UI.Payment Payout
CARD_INTENT_SUCCESSFULRender your successful confirmation UI for the created card token.CardToken
CARD_INTENT_FAILEDRender your failure UI for the created card token.CardToken


States Handling

There are some intent states that you can use to customize your payment/payout experience.

Form Fields

FORM_FIELDS state indicates that there are form field values need to be collected. These fields could be card fields, shipping fields, or billing fields. If card fields are needed, accessToken will be found in stateDetails.formFields.card. It is used when submitting the form as mentioned here, and for shipping and billing fields, fields schema will be returned inside stateDetails.formFields.billing and stateDetails.formFields.shipping.

// intent details sample
{
  state: "FORM_FIELDS"
  stateDetails: {
    formFields: {
      card: {
        accessToken: string;
      },
      billing: Field[],
      shipping: Field[],
    },
  }
  // ... rest of details props
}

🚧

Note that accessToken is valid only for 5 minutes.

If card fields are needed refer to Rendering Card Fields for handling card form elements.

Billing and Shipping Fields Schema

type FieldType =
  | "text"
  | "number"
  | "email"
  | "date"
  | "phoneNumber"
  | "select";

type Field = {
  type: FieldType;
  name: string;
  label: string;
  hint: string;
  value: string;
  readOnly: boolean;
  validation: {
    required: boolean;
    minLength: number | null;
    maxLength: number | null;
  };
  dependsOn?: string; // Field.name of a field that this field value is depending on its value
  optionsList?: Array<{ label: string; value: string }>; // contains list of options for select fields
  optionsMap?: Record<string, Array<{ label: string; value: string }>>; // contains the values that select field depends on along with options associated to each value
};

Submitting Form Fields

We have moneyhash.submitForm method which allows you to submit required form fields to proceed with the payment intent. The method takes an object of type SubmitData that has the following properties:

NameType
intentIdstring
The intent ID that is being processed.
accessToken?string | null - optional
The accessToken received from the stateDetails.formFields.card if you want to submit card data.
billingData?Record<string, string | number> - optional
An object contains the values of billing data that are collected from the user, the object key is the Field.name of each field and the value is the data received from the user.
shippingData?Record<string, string | number> - optional
An object contains the values of shipping data that are collected from the user, the object key is the Field.name of each field and the value is the data received from the user.
saveCard?boolean - optional
A boolean value that can be used to tell whether to save the card information for future use or not. By default, the card information will not be saved unless this value is true.
paymentMethod?PaymentMethodSlugs - optional
Defaults to CARD can be used if submitForm is being used in another method other than CARD
// Example for submitting Form data

const res = await moneyHash.submitForm({
  intentId: "<intent-id>",
  accessToken: stateDetails?.formFields?.card?.accessToken,
  billingData: {
    name: "Jhon Doe",
    phoneNumber: "+20101234567",
    // ...rest of received billing data fields values
  },
  shippingData: {
    street: "",
    postalCode: "",
    // ...rest of received shipping data fields values
  },
  saveCard: true,
});

Validation and Submission Errors

When submitting form fields, a validation error or a submission error may occur. The errors will be received from the rejected promise returned from moneyhash.submitForm. The error object will contain name of the fields with errors along with error messages. When there is an error not related to card fields, general_error property will exist, as follows:

// Error object sample
try {
  await moneyhash.submitForm({...});
} catch(error) {
    console.error(error);
/*
error: {
    "card_holder_name": "<error-message>",
    "card_number": "<error-message>",
    "cvv": "<error-message>",
    "expiry_month": "<error-message>",
    "expiry_year": "<error-message>",
    "general_error": "<error-message>"
}
*/
}

URL to Render

URL_TO_RENDER state indicates you have a url to render, this url could be rendered as iframe, popup_iframe, or redirect. When this is the intent state, the stateDetails will be as following:

// intent details sample
{
  state: "URL_TO_RENDER"
  stateDetails: {
    url: string; // the url to render
    renderStrategy: "IFRAME" | "POPUP_IFRAME" | "REDIRECT", // recommended strategy to use for rendering
  },
  // ... rest of details props
}

You can use moneyhash.renderUrl method explained here which will handle the url rendering for you.

  • In case of IFRAME , it appends the iframe inside a container with id called #rendered-url-iframe-container and will throw an error if the element with this id doesn't exist. After completing the process, the iframe is removed from the DOM and onComplete or onFail method you passed while creating MoneyHash instance will be fired.
  • In case of POPUP_IFRAME, it will open the url in a popup window, and after the process is completed, onComplete or onFail method will be fired and the popup window will be closed. The method will throw an error if the browser blocks opening a popups.
  • In case of REDIRECT , it will open the url in the same window, and you can pass redirectToNewWindow flag in the method options to open the url in a new tab.

Render URL

moneyHash
  .renderUrl({
    intentId: "<intent_id>",
    url: "<rendered-url>",
  	renderStrategy: "<'IFRAME' | 'POPUP_IFRAME' | 'REDIRECT'>",
  });

Saved Card CVV

SAVED_CARD_CVV requires the CVV of a saved card to be collected to proceed with the payment. You will receive some info regarding the card which you are collecting the CVV for the user to know which card he is paying with. You have the responsibility to collect the CVV in any way you want, however, we give you the scheme of type Field for the CVV field that is supposed to be rendered as following:

// intent details sample
{
  state: "SAVED_CARD_CVV",
  stateDetails: {
    card: {
      "brand": "MasterCard",
      "brandIconUrl": "logo-url",
      "last4Digits": "0000",
    },
    cvvField: {
      type: "text",
      name: "cvv",
      label: "CVV",
      hint: null,
      value: "",
      readOnly: false,
      validation: {
        required: true,
        minLength: 3,
        maxLength: 4
      }
    }
  }
  // ... rest of details props
}

Submitting CVV

After collecting CVV value, use moneyhash.submitCvv to pass the CVV and proceed with the payment.

const res = await moneyhash.submitCvv({
  intentId: paymentIntentId,
  cvv: "123",
});

Intent Form

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

await moneyHash.renderForm({
  selector: "<container_css_selector>",
  intentId: "<intent_id>",
  // Optional detect moneyhash iframe height to update container
  onHeightChange: height => {
    document.querySelector("<container_css_selector>").style.height = `${height}px`;
  },
});

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, state, stateDetails, shippingData, productItems, nativePayData, recommendedMethods }) => {
    console.log("onComplete", {
      intent,
      transaction,
      selectedMethod,
      state,
      stateDetails,
      shippingData,
      productItems,
      nativePayData,
      recommendedMethods
    });
  },
});

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, state, stateDetails, shippingData, productItems, nativePayData, recommendedMethods }) => {
    console.log("onFail", {
      intent,
      transaction,
      selectedMethod,
      state,
      stateDetails,
      shippingData,
      productItems,
      nativePayData,
      recommendedMethods
    });
  },
});

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, state, stateDetails, shippingData, productItems, nativePayData, recommendedMethods }) => {
    console.log({
      intent,
      transaction,
      selectedMethod,
      state,
      stateDetails,
      shippingData,
      productItems,
      nativePayData,
      recommendedMethods,
    });
  });

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

Update Intent Discount

Use updateIntentDiscount method and pass the intentId and discount object to update the discount on intent level, knowing that the discount updating should adhere to the following:

  • Discounts can't be updated for intents that have transactions.
  • Can't update discount when the intent has product items that have a discount.
  • Can't update discount when the intent has fees that have a discount.
  • Discount value must not be more than the intent amount.
moneyHash
  .updateIntentDiscount({
  	intentId: "<intent-id>",
  	discount: {
      title: {
        en: "english title",
        ar: "عنوان بالعربية",
        fr: "french title",
      },
      type: "amount" | "percentage",
      value: 10,
    },
  })
  .then(({ amount, discount }) => {
    console.log({ amount, discount });
  });

Update Intent Fees

Use updateIntentFees passing the intentId and fees list to update the intent fees, knowing that fees can't be updated for intents that have transactions.

moneyHash
  .updateIntentFees({
  	intentId: "<intent-id>",
  	fees: [
      {
        title: {
          en: "english title",
          ar: "عنوان بالعربية",
          fr: "french title",
        },
        value: 10,
      },
      {
        title: {
          en: "english title",
          ar: "عنوان بالعربية",
          fr: "french title",
        },
        value: 10,
        discount: {
          title: {
            en: "english title",
            ar: "عنوان بالعربية",
            fr: "french title",
          },
          type: "amount" | "percentage",
          value: 10,
        },
      },
    ],
  })
  .then(({ amount, fees }) => {
    console.log({ amount, fees });
  });

On Intent Expiration

moneyhash.onExpirtaion can be used to fire a callback after the intent is expired. Pass the intent expiration date that you can get from moneyhash.getIntentDetails and the callback. Returns the cleanup function of the onExpiration listenener.

const intentDetails = await moneyhash.getIntentDetails("<intent-id>");
const { expirationDate } = intentDetails.intent;

const cleanUpFn = moneyHash.onExpiration(expirationDate, () => {
  console.log('intent expired!');
});

// use cleanUpFn where needed.

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: {
    input: {
      base: {},
      focus: {},
      error: {},
    },
    loader: {
      backgroundColor: '',
      color: '',
    },
    checkout: {
      hideAmountSidebar: boolean,
      hideHeader: boolean,
      hideLoaderMessage: boolean,
      hideFormHeaderMessage: boolean,
      hideNavigationToPaymentMethods: boolean,
      formOnly: boolean,
    }
  },
});

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 the inputs, 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;
}

Checkout

export interface CheckoutStyle {
  hideAmountSidebar?: boolean;
  hideHeader?: boolean;
  hideLoaderMessage?: boolean;
  hideFormHeaderMessage?: boolean;
  hideNavigationToPaymentMethods?: boolean;
  formOnly?: boolean;
}



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

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;
};

If you're looking for the documentation for the JS SDK version 0, you can find it here