@motopays/pay-form
v2.0.1
Published
motopays ui, card payments, pay form
Downloads
1,653
Readme
@motopays/pay-form
Installation
npm i @motopays/pay-form
#or
yarn add @motopays/pay-form
Usage
Html + JS
<head>
...
<link rel="stylesheet" href="motopays/pay-form/styles.css" />
...
</head>
<body>
...
<div id="moto-payment-form"></div>
...
</body>
<script src="motopays/pay-form/index.js" type="text/javascript"></script>
<script type="text/javascript">
const targetElement = document.getElementById("moto-payment-form");
const paymentFormElement = document.createElement("moto-payment-form");
paymentFormElement.id = "moto-payment-form";
paymentFormElement.payment = {...};
paymentFormElement.settings = {...};
paymentFormElement.addEventListener("close", (event) => {
console.log(event);
});
paymentFormElement.addEventListener("analyticsTracked", (event) => {
console.log(event);
});
paymentFormElement.addEventListener("paid", (event) => {
console.log(event);
});
paymentFormElement.addEventListener("error", (event) => {
console.log(event);
});
targetElement.parentNode.replaceChild(formElement, targetElement);
</script>
React
import './App.css';
import '@motopays/pay-form/index.js';
import { IPaymentSettings, ISettings } from '@motopays/pay-form/index.js';
import '@motopays/pay-form/styles.css';
import { useEffect, useRef } from 'react';
interface MotoPaymentFormProps extends React.HTMLAttributes<HTMLElement> {
payment: string;
settings: string;
}
declare global {
namespace JSX {
interface IntrinsicElements {
'moto-payment-form': React.DetailedHTMLProps<
MotoPaymentFormProps,
HTMLElement
>;
}
}
}
const handleClose = () => console.log('close');
const handleAnalyticsTracked = (e: any) => console.log(e);
const handleError = (e: any) => console.log(e);
const handlePaid = (e: any) => console.log(e);
function App() {
const motoPaymentFormRef = useRef<HTMLElement>(null);
useEffect(() => {
const motoPaymentFormElement = motoPaymentFormRef.current;
if (motoPaymentFormElement) {
motoPaymentFormElement.addEventListener('close', handleClose);
motoPaymentFormElement.addEventListener('analyticsTracked', handleAnalyticsTracked);
motoPaymentFormElement.addEventListener('paid', handlePaid);
motoPaymentFormElement.addEventListener('error', handleError);
return () => {
motoPaymentFormElement.removeEventListener('close', handleClose);
motoPaymentFormElement.removeEventListener('analyticsTracked', handleAnalyticsTracked);
motoPaymentFormElement.removeEventListener('paid', handlePaid);
motoPaymentFormElement.removeEventListener('error', handleError);
};
}
}, []);
const payment: IPaymentSettings = {...};
const settings: ISettings = {...};
const paymentJSON: string = JSON.stringify(payment);
const settingsJSON: string = JSON.stringify(settings);
return (
<div className="App">
<moto-payment-form ref={motoPaymentFormRef} payment={paymentJSON} settings={settingsJSON}></moto-payment-form>
</div>
);
}
export default App;
Angular
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
import '@motopays/pay-form/index.js';
import '@motopays/pay-form/styles.css';
import { IPaymentSettings, ISettings } from '@motopays/pay-form/index.js';
@Component({
selector: 'app-root',
standalone: true,
template: `
<moto-payment-form
[payment]="payment"
[settings]="settings"
(close)="onClose()"
(analyticsTracked)="onAnalyticsTracked($event)"
(error)="onError($event)"
(paid)="onPaid($event)">
</moto-payment-form>
`,
styleUrl: './app.component.scss',
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppComponent {
protected payment: IPaymentSettings = {...};
protected settings: ISettings = {...};
protected onClose(): void {
console.log('close');
}
protected onAnalyticsTracked(e: any): void {
console.log(e);
}
protected onPaid(e: any): void {
console.log(e);
}
protected onError(e: any): void {
console.log(e);
}
}
Payment interface
| Field | Type | Description | | ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | userId *required | string | External end-user Id generated on the merchant side (not Motopays) | | paymentId *required | string | External unique payment Id generated on the merchant side (not Motopays) | | amount *required | number | Payment amount | | currency *required | string | Payment currency in ISO_4217 format. For example: gbp, eur, usd | | merchantId *required | string | Merchant's identifier, issued by Motopays. | | email *required | string | End-customer e-mail. | | userAuthToken | string | End-user authorization token for accessing cards that have previously been used, as well as for accessing Apple Pay | | amountType | TAmountType | Type of amount can be only one of the following values: { 'gross-without-gst', 'net', 'gross' } | | tax | number | Payment tax | | orderType | TOrderType | Type of order can be only one of the following values: { 'regular', 'free-trial', 'gems' } | | initiator | TPaymentInitiator | Identifies who of 2 types initiated the payment. Currently accepted: {'customer', 'merchant'} | | webhookUrl | string | URL where the Motopays will send hooks about the payment status changes | | returnUrl | string | The URL to which the user will be redirected after completing some types of payment, such as PayPal | | description | string | Short order description for the customer | | details | ISimple | Additional information about payment | | phoneNumber | string | End-customer phone number (can be changed if a customer fill the number by themselves) | | ipAddress | string | End-customer ip address | | billingAddress | BillingAddress | The address linked to a customer bank account | | billingAddress.addressLine | string | Street, building apartment and etc in one line | | billingAddress.city | string | City of a customer bank account | | billingAddress.state | string | State of a customer bank account | | billingAddress.country | string | Country of a customer bank account (ISO 3166-1 alpha-2 country code). Example: US | | billingAddress.zip | string | Zip of a customer bank account | | signature | string | The signature of the payment. For details contact technical support |
Settings interface
| Field | Type | Description | |-----------------------------|---------|-----------------------------------------------------------------------------| | urls *required | IServicesUrls | The necessary services urls | | urls.processing *required | string | The processing service URL | | urls.billingProfiles *required | string | The billing profiles service URL | | isSignatureRequired | boolean | Whether to show an error and deny access to the payment form if the signature field in the payment model is not set | | isCloseButtonVisible | boolean | Whether to show a window close button in the top right corner of the screen | | isHeaderVisible | boolean | Whether to show a header in the top of the screen | | closePaymentRedirect | boolean | Whether to close a window that was open on 3ds or apm pages after a redirect to returnUrl | | isProblemTipsListVisible | boolean | Whether to show tips on the payment result page when the payment is rejected, if tips exist for the error status code that occurred | | languageSettings | ILanguageSettings | The language settings | languageSettings.code | TLanguageCode | The code of language. Currently accepted: { 'en-US', 'es-ES', 'fr-FR' }. It's also possible to add another code by adding custom language to languageSettings.languages | languageSettings.languages | Partial<Record<TLanguageCode, ILanguage>> | Languages by codes | components | TComponentType[] | Components that will be on the form, as well as their order, which depends on the position of the components in the array. Currently accepted: {'available-card-brands', 'card-list', 'requisites', 'phone-number', 'charge-terms', 'pay-button', 'apm-list', 'user-preferences', 'merchant-info', 'protected-by', 'no-methods'} | resultComponentsStructure | IResultComponentsStructure | Components that will be on the result page of the form, as well as their order, which depends on the position of the components in the array | resultComponentsStructure.aboveResult | TComponentType[] | Components that will be on the result page of the form above the result, as well as their order, which depends on the position of the components in the array | resultComponentsStructure.belowResult | TComponentType[] | Components that will be on the result page of the form below the result, as well as their order, which depends on the position of the components in the array | merchantInfo | IMerchantInfoSettings | The information displays in merchant info section | | merchantInfo.text | Partial<Record<TLanguageCode, string>> | The information about a merchant multilingual support and linkify. Read more about linkify in the Linkify section | | merchantInfo.links | ILink[] | The array of merchant links. For example, Policy and Terms | | chargeTerms | IChargeTermsSettings | The charge terms section | | chargeTerms.checkboxes | Partial<Record<TLanguageCode, string>>[] | The user must select all the checkboxes to proceed with the payment. Without selecting these checkboxes, the payment cannot be made. Multilingual support and linkify. Read more about linkify in the Linkify section | | chargeTerms.text | Partial<Record<TLanguageCode, string>> | The text of charge terms. For example, description of terms and policy. Multilingual support and linkify. Read more about linkify in the Linkify section | | userPreferences | IUserPreferencesSettings | The user preferences section | | userPreferences.text | Partial<Record<TLanguageCode, string>> | The text of user preferences. For example, description of autorefill plan. Multilingual support and linkify. Read more about linkify in the Linkify section | | userPreferences.checkboxes | IUserPreferenceCheckbox[] | The text of user preferences. For example, description of autorefill plan. Multilingual support and linkify. Read more about linkify in the Linkify section | | availableCardBrands | string[] or CreditCardTypeCardBrandId[] | The сard brands for which icons will be displayed on the form as card payment methods. Currently accepted: {american-express, discover, jcb, maestro, mastercard, unionpay, visa} | | requiredFieldsBehavior | IRequiredFieldsBehavior | Configuration of the display of required fields for the user to manage the user's focus on them | | requiredFieldsBehavior.showStars | boolean | Show the stars on labels of required fields | | requiredFieldsBehavior.buttonStateUntilFilled | TButtonStateUntilFilled | If 'disabled', the payment buttons remain disabled until the user fills in all the required fields. If 'enabled', then when one of the buttons is pressed (the buttons are enabled), the required fields will be marked as invalid (red color) for the user. If 'hidden-enabled', then when one of the buttons is pressed (the buttons are enabled but have styles as disabled), the required fields will be marked as invalid (red color) for the user. Current accepted: { enabled, disabled, hidden-enabled } | | requiredFieldsBehavior.markAsInvalidUntilCorrect | boolean | If true, then all unfilled or incorrectly filled required fields will initially appear as invalid (red color) to the user until they are filled and correct. If the value is false, the required fields will only be shown as invalid in the event that the user presses the payment button when the buttons are enabled | | events | IEvents | The events configuration | | events.analytics | { optional: boolean; required: boolean; } | The analytics events configuration. Optional is responsible for 'focus' events. Required is responsible for 'click' events. Read more about analytics events in the Analytics section | | events.error | boolean | The error events configuration. If true, the error events will be emitted; otherwise, they will not | | apmsCollapse | boolean | The apms collapse configuration. If true, the 'or use alternative methods' text will be clickable and will control whether the apms are shown or not. By default, if true, the apms will be collapsed. |
Example
Payment
const payment: IPaymentSettings = {
amount: 10,
currency: 'usd',
email: '[email protected]',
merchantId: '22504',
paymentId: 'IS949832NF29',
userId: 'OIDSF0202JFS',
amountType: 'net',
tax: 3,
orderType: 'gems',
userAuthToken: 'testestest.testestest.testestest',
billingAddress: {
addressLine: "main street 1",
city: "Paris",
country: "US",
state: "NY",
zip: "220125"
},
description: 'my description',
initiator: 'customer',
details: {
myField: 21
},
ipAddress: '12.12.12.12',
phoneNumber: '+995234234234',
returnUrl: 'https://my.return.com',
signature: '0saidmc0smac',
webhookUrl: 'https://my.webhook.com'
}
Settings
const settings: ISettings = {
urls: {
processing: 'https://my.processing.com',
billingProfiles: 'https://my.billingprofiles.com',
},
apmsCollapse: false,
availableCardBrands: ['visa', 'american-express', 'mastercard', 'diners-club', 'jcb'],
chargeTerms: {
text: {
'en-US': 'My charge terms { href: "https://google.com", text: "Google" } the ending of text',
'es-ES': 'My charge terms { href: "https://google.com", text: "Google es-ES" } the ending of text es-ES',
'custom': 'custom charge terms text'
},
checkboxes: [
{
'en-US': 'Charge terms { href: "https://google.com", text: "Google" } checkbox',
'es-ES': 'Charge terms { href: "https://google.com", text: "Google es-ES" } checkbox es-ES',
'custom': 'custom'
}
]
},
closePaymentRedirect: true,
components: [
'available-card-brands',
'no-methods',
'card-list',
'requisites',
//'phone-number',
'charge-terms',
'pay-button',
'apm-list',
'user-preferences',
'merchant-info',
'protected-by'
],
events: {
analytics: {
optional: true,
required: true,
},
error: true
},
isCloseButtonVisible: true,
isHeaderVisible: true,
isProblemTipsListVisible: true,
isSignatureRequired: false,
languageSettings: {
code: 'custom',
languages: {
'custom': {
apms: {
or: 'custom',
orUseAlternative: 'custom'
},
availableCardBrandsHeader: 'custom',
close: 'custom',
errors: {
invalidCardExpiration: 'custom',
invalidCardHolder: 'custom',
invalidCardNumber: 'custom',
invalidCvv: 'custom',
invalidPhoneNumber: 'custom',
notCheckedCheckboxes: 'custom'
},
existingCards: {
addNewCard: 'custom',
manageCardsHeader: 'custom',
removeCard: 'custom'
},
header: 'custom',
noAvailablePaymentMethods: 'custom',
notifications: {
cardDeleted: {
text: 'custom',
title: 'custom'
},
error: {
default: {
text: 'custom',
title: 'custom'
}
}
},
pay: 'custom',
paymentProcessing: 'custom',
paymentResult: {
success: 'custom',
failure: {
main: 'custom',
paymentFailed: 'custom'
}
},
phone: {
header: 'custom',
placeholder: 'custom'
},
requisites: {
cardExpirationPlaceholder: 'custom',
cardHolderHeader: 'custom',
cardHolderPlaceholder: 'custom',
cardNumberPlaceholder: 'custom',
cvvHint: 'custom',
paymentDetailsHeader: 'custom',
cvvPlaceholder: {
CID: 'custom',
CVC: 'custom',
CVE: 'custom',
CVN: 'custom',
CVP2: 'custom',
CVV: 'custom'
}
},
totalHeader: 'custom',
transactionProtectedBy: 'custom'
}
}
},
merchantInfo: {
text: {
'en-US': 'Merchant info { href: "https://google.com", text: "Google" } merchant info { href: "https://google.com", text: "Google" } Merchant info',
'es-ES': 'Merchant info { href: "https://google.com", text: "Google es-ES" } merchant info { href: "https://google.com", text: "Google es-ES" } Merchant info es-ES',
'custom': 'custom merchant info'
},
links: [
{
href: 'https://google.com',
text: {
'en-US': 'google',
'es-ES': 'google (es-ES)',
'custom': 'custom link'
}
}
]
},
requiredFieldsBehavior: {
buttonStateUntilFilled: 'enabled',
markAsInvalidUntilCorrect: false,
showStars: true
},
resultComponentsStructure: {
aboveResult: [],
belowResult: ['merchant-info', 'protected-by']
},
userPreferences: {
text: {
'en-US': 'User preferences text { href: "https://google.com", text: "Google" } User preferences text',
'es-ES': 'User preferences text { href: "https://google.com", text: "Google es-ES" } User preferences text es-ES',
'custom': 'custom user preferences text'
},
checkboxes: [
{
name: 'user_preference_name',
text: {
'en-US': 'User preference { href: "https://google.com", text: "Google" } checkbox',
'es-ES': 'User preference { href: "https://google.com", text: "Google es-ES" } checkbox es-ES',
'custom': 'custom user preferences checkbox'
},
defaultValue: false
}
]
}
};
Output events
| Event | Type | Description | | ----- | ---------------- | -------------------------------------------- | | close | void | triggered by clicking on the closing icon | | paid | IPaymentResponse | triggered after receiving a payment response | | error | any | triggered by error events | | analyticsTracked | TAnalyticsServiceAction | triggered by analytics events |
Payment Result Structure
IPaymentResponse {
action?: {
name: string;
data: IThreeDsData | IRedirectData | any;
}
ip: string;
merchant: string;
order: {
invoiceId: string;
details: string;
}
payment: {
paymentId: string;
state: TPaymentState; //on of strings: completed, rejected, needAction
rebillAnchor: string;
info: {
completeProcessingTime?: Date;
currency: string;
paymentType: string;
paymentSystem: string;
paymentRequisites?: string;
price: number
}
rejectionDetails?: {
hardDecline: boolean;
message?: string;
problemSolvingTips: string[];
rejectionCode: number;
}
}
taxInfo: {
abbreviation: string;
price: number;
priceNet: number;
tax: number;
}
user: {
id: string;
}
signature: string;
}
IThreeDsData {
paReq: string;
acsurl: string;
termUrl: string;
md: string;
}
IRedirectData {
location: string;
}
Analytics
For receiving analytics listen analyticsTracked events.
'init-start' - the form has started initialization. Payload: void. 'loading-finish' - the form has finished initialization and loading. Payload: void. 'card-number-field-focus' - the card number field has been focused. Payload: void. 'expiration-date-field-focused' - the expiration date field has been focused. Payload: void. 'cvv-field-focused' - the cvv field has been focused. Payload: void. 'card-holder-field-focused' - the card holder field has been focused. Payload: void. 'charge-term-checkbox--focus' - the charge term checkbox has been focused. Payload: IChargeTermsAnalytics. 'charge-term-checkbox--check' - the charge term checkbox has been checked. Payload: IChargeTermsAnalytics. 'charge-term-checkbox--uncheck' - the charge term checkbox has been unchecked. Payload: IChargeTermsAnalytics. 'user-preference-checkbox--focus' - the user preference checkbox has been focused. Payload: IUserPreferencesAnalytics. 'user-preference-checkbox--check' - the user preference checkbox has been checked. Payload: IUserPreferencesAnalytics. 'user-preference-checkbox--uncheck' - the user preference checkbox has been unchecked. Payload: IUserPreferencesAnalytics. 'pay-button-focus' - the 'Pay' button has been focused (payment by card). Payload: void. 'pay-button-click' - the 'Pay' button has been clicked (payment by card). Payload: void. '-button-focus' - the apm payment button has been focused. Payload: void. '-button-click' - the apm payment button has been clicked. Payload: void. 'add-card-button-focus' - the "Add New Card" button has been focused. Payload: void. 'add-card-button-click' - the "Add New Card" button has been clicked. Payload: void. 'card-options-button-click' - the card options button has been clicked (existing card menu visualized as 3 vertical dots). Payload: ICardAnalytics. 'cards-toggle-button-click' - the cards toggle button has been clicked (show or collapse card list). Payload: ICardListAnalytics. 'remove-card-button-click' - the card removing button has been clicked. Payload: ICardAnalytics. 'select-card-button-click' - the card selecting button has been clicked. Payload: ICardAnalytics. 'apms-show-button-click' - the apm collapse text has been clicked. Payload: ICardAnalytics.
Examples of dynamic event types (number inserting): 1. Example of the first checkbox: 'charge-term-checkbox-0-check' 2. Example of the second checkbox: 'charge-term-checkbox-1-check'
Examples of dynamic event types (apm inserting): 1. 'coinbase-button-click' 2. 'moto-button-click'
type TAnalyticsServiceAction =
| ServiceAction<'init-start'>
| ServiceAction<'loading-finish'>
| ServiceAction<'card-number-field-focus'>
| ServiceAction<'expiration-date-field-focus'>
| ServiceAction<'cvv-field-focus'>
| ServiceAction<'card-holder-field-focus'>
| ServiceAction<'number-field-focus'> //
| ServiceAction<
`charge-term-checkbox${`-${number}`}-focus`,
IChargeTermsAnalytics
>
| ServiceAction<
`charge-term-checkbox${`-${number}`}-check`,
IChargeTermsAnalytics
>
| ServiceAction<
`charge-term-checkbox${`-${number}`}-uncheck`,
IChargeTermsAnalytics
>
| ServiceAction<
`user-preference-checkbox${`-${number}`}-focus`,
IUserPreferencesAnalytics
>
| ServiceAction<
`user-preference-checkbox${`-${number}`}-check`,
IUserPreferencesAnalytics
>
| ServiceAction<
`user-preference-checkbox${`-${number}`}-uncheck`,
IUserPreferencesAnalytics
>
| ServiceAction<'pay-button-focus'>
| ServiceAction<'pay-button-click'>
| ServiceAction<`${string}-button-focus`>
| ServiceAction<`${string}-button-click`>
| ServiceAction<'add-card-button-focus'>
| ServiceAction<'add-card-button-click'>
| ServiceAction<'card-options-button-click', ICardAnalytics>
| ServiceAction<'cards-toggle-button-click', ICardListAnalytics>
| ServiceAction<'remove-card-button-click', ICardAnalytics>
| ServiceAction<'select-card-button-click', ICardAnalytics>
| ServiceAction<'apms-show-button-click', ICardAnalytics>;
//payload types
interface ICardListAnalytics {
toggle: boolean;
}
interface ICardAnalytics {
last4Digits: string;
}
interface IChargeTermsAnalytics {
text: string;
}
interface IUserPreferencesAnalytics {
name: string;
text: string;
}
Linkify
Text elements, which are described in the documentation as linkify, can be transformed into text with links. The link object starts with the '{' character and ends with the '}' character. The href field specifies the URL to navigate to when clicked, and the text field specifies the text that will be displayed to the user as the link. Example: 'My text { href: "https://google.com", text: "Link1" } the ending of text. The continuation of the text { href: "https://google.com", text: "Link1" }.'
Webpack 5 Issues
During the integration process, you might face multiple issues with webpack 5. This issue is caused due to the fact that some packages have certain dependencies, which are not present within the browser environment by webpack 5. Hence, you require certain node polyfills to be added to your project, while overriding the configurations to enable their usage. When that is done, your project should run without any issues.