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.
state | Action | Intent Type |
---|---|---|
METHOD_SELECTION | Use 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_FIELDS | Render 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_RENDER | Render 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_CVV | Render 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_PLANS | Show 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_FORM | Use 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_PROCESSED | Render your successful confirmation UI with the intent details. | Payment Payout |
TRANSACTION_FAILED | Render your failure UI with the intent details. | Payment Payout |
TRANSACTION_WAITING_USER_ACTION | Render your pending actions confirmation UI with the intent details and externalActionMessage if exists on Transaction . | Payment Payout |
EXPIRED | Render your intent expired UI. | Payment |
CLOSED | Render your intent closed UI. | Payment Payout |
CARD_INTENT_SUCCESSFUL | Render your successful confirmation UI for the created card token. | CardToken |
CARD_INTENT_FAILED | Render 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:
Name | Type |
---|---|
intentId | string 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 andonComplete
oronFail
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
oronFail
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 passredirectToNewWindow
flag in the methodoptions
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
Updated about 2 months ago