Embedded components framework integrations

Learn how to use Payabli’s embedded components with front-end frameworks like React and Vue

Applies to:Developers

You can use Payabli’s embedded components in a React or Vue application following the same configuration patterns as in a vanilla JavaScript application.

React

Visit the React Integration Example to see Payabli’s embedded components in a React application.

Step 1: Create the hook

Create a hook that allows you to use the embedded component and execute its methods. The hook needs to inject the Payabli library script and initialize the embedded component with the provided configuration. Make a new file for the usePayabli hook and add the following code:

// usePayabli.ts
import { useState, useEffect, useRef, useCallback } from "react";
const useScript = (src: string) => {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
const existingScript = document.querySelector(`script[src="${src}"]`);
const onLoad = () => {
setIsLoaded(true);
};
if (!existingScript) {
const script = document.createElement("script");
script.src = src;
script.async = true;
script.addEventListener("load", onLoad);
document.body.appendChild(script);
return () => {
script.removeEventListener("load", onLoad);
document.body.removeChild(script);
};
} else {
if (existingScript.getAttribute("data-loaded") === "true") {
setIsLoaded(true);
} else {
existingScript.addEventListener("load", onLoad);
}
}
}, [src]);
return isLoaded;
};
declare var PayabliComponent: any;
export const usePayabli = (
options: any,
method: string,
parameters: any = null,
production: boolean = false
) => {
const [payOptions, setPayOptions] = useState(options);
const [isInitialized, setIsInitialized] = useState(false);
const payComponentRef = useRef<any>(null);
const initCallbacks = useRef<(() => void)[]>([]); // Queue for functions waiting on initialization
const scriptSrc = production ? "https://embedded-component.payabli.com/component.js" : "https://embedded-component-sandbox.payabli.com/component.js";
const isScriptLoaded = useScript(scriptSrc);
useEffect(() => {
if (isScriptLoaded) {
payComponentRef.current = new PayabliComponent(payOptions);
setIsInitialized(true);
// payabliExecute queued callbacks
initCallbacks.current.forEach((cb) => cb());
initCallbacks.current = []; // Clear the queue
}
}, [isScriptLoaded, payOptions]);
useEffect(() => {
if (isInitialized && payComponentRef.current) {
payComponentRef.current.updateConfig(payOptions);
}
}, [isInitialized, payOptions]);
const payabliReinit = useCallback(() => {
if (isInitialized && payComponentRef.current) {
payComponentRef.current.payabliExec("reinit");
}
}, [isInitialized]);
const payabliExec = useCallback(() => {
const payabliExecuteMethod = () => {
if (parameters != null) {
payComponentRef.current.payabliExec(method, parameters);
} else {
payComponentRef.current.payabliExec(method);
}
};
if (isInitialized && payComponentRef.current) {
payabliExecuteMethod();
} else {
initCallbacks.current.push(payabliExecuteMethod); // Queue the payabliExecution
}
}, [isInitialized, method, parameters]);
return [payOptions, setPayOptions, payabliExec, payabliReinit];
};

Step 2: Create the component

Create a new PayabliCheckout component that passes in the configuration object for the embedded component to the usePayabli hook. The PayabliCheckout component uses the payabliExec function to execute the embedded component’s method. Create a new file in the same directory and add the following code:

There are multiple types of embedded components with different use cases. See the Embedded Components Overview to decide which component type is best for you.

// PayabliCheckout.tsx
import { usePayabli } from './usePayabli';
export const PayabliCheckout = () => {
const token = "o.z8j8aaztW9tUtUg4dlVeYAx+L2MazOFGr0DY8yuK3u79MCYlGK4/q0t5AD1UgLAjXOohnxN8VTZfPswyZcwtChGNn1a8jFMmYWHmLN2cPDW9IrBt1RtrSuu+85HJI+4kML5sIk9SYvULDAU2k0X0E1KFYcPwjmmkUjktrEGtz48XCUM70aKUupkrTh8nL7CXpAXATzVUZ2gEld9jGINwECPPLWmu+cZ4CJb7QMJxnzKFD073+nq/eL+pMth7+u/SkmAWC0+jn8y+Lf6T5Q5PqB6wN7Mvosp8g7U7lbEW2wC0DA92pjblfDHVJOQUkjgT7B1GvryMokLvBjoiaLhKa55iKZE1YDlyqruILkoNF+zGSPS9r17qU6w4ziKhoMdSPzPBJBlLhQhz3MVANXbjfEfJwmtr/JJ1uStUfBFJ710cS1x7goxMJO/cl+q+LVtPy788EKFkgMc5OjfBNCsNL+dBDVbK5CiIJUSbOFzdqdjY/VJ14MEodsHYOwMAjuF4.KRFMeEj0SOur8MLZ362c/UZ/U/Az3CSUkr3/8EVDE6Y="
const entryPoint = "bozeman-aikido"
const rootContainer = "pay-component-1"
const payabliButton = "btnx"
const [payabliConfig, setPayabliConfig, payabliExec] = usePayabli({
type: "methodEmbedded",
rootContainer: rootContainer,
defaultOpen: 'card', // offering only Card method - Embedded UI can only show a payment method
// customCssUrl: "your url to a custom css file",
token: token,
entryPoint: entryPoint,
card: {
enabled: true,
amex: true,
discover: true,
visa: true,
mastercard: true,
jcb: true,
diners: true,
inputs: { // here we are customizing the input fields
cardHolderName: {
label: "NAME ON CARD",
placeholder: "",
floating: false,
value: "John Doe",
size: 12,
row: 0,
order: 0
},
cardNumber: {
label: "CARD NUMBER",
placeholder: "1234 1234 1234 1234",
floating: false,
size: 6,
row: 1,
order: 0
},
cardExpirationDate: {
label: "EXPIRATION DATE",
placeholder: "MM/YY",
floating: false,
size: 6,
row: 1,
order: 1
},
cardCvv: {
label: "CVV/CVC",
placeholder: "CVV/CVC",
floating: false,
size: 6,
row: 2,
order: 0,
},
cardZipcode: {
label: "ZIP/POSTAL CODE",
placeholder: "ZIP/POSTAL CODE",
floating: false,
size: 6,
row: 2,
order: 1,
country: ["us", "ca"],
}
}
},
ach: {
enabled: false,
checking: true,
savings: true
},
customerData: {
customerNumber: "00001",
firstName: "John",
lastName: "Doe",
billingEmail: "johndoe@email.com"
},
functionCallBackSuccess: (response: any) => {
const containerEl = document.getElementById(rootContainer);
const responseText = JSON.stringify(response.responseText);
const responseData = JSON.stringify(response.responseData);
alert(responseText + " " + responseData);
containerEl!.innerHTML += `
<hr/>
<p><b>Embedded Component Response:</b></p>
<p>${responseText}</p>
<p>${responseData}</p>
<hr/>
`;
},
functionCallBackReady: (data: any) => {
var btn = document.getElementById(payabliButton);
if (data[1] === true) {
btn!.classList.remove("hidden");
} else {
if (!btn!.classList.contains("hidden")) {
btn!.classList.add("hidden");
}
}
},
functionCallBackError: (errors: any) => {
alert('Error!');
console.log(errors);
}
},
"pay", {
paymentDetails: {
totalAmount: 100,
serviceFee: 0,
categories: [
{
label: "payment",
amount: 100,
qty: 1,
},
],
},
})
return (
<div>
<div id="pay-component-1"></div>
<button id="btnx" className="hidden" onClick={payabliExec}>Pay</button>
<button onClick={() => setPayabliConfig({
...payabliConfig,
card: {
...payabliConfig.card,
inputs: {
...payabliConfig.card.inputs,
cardHolderName: {
...payabliConfig.card.inputs.cardHolderName,
value: "Johnny Dover"
}
}
}
})}>Switch to Johnny Dover</button>
</div>
)
}
// PayabliCheckout.tsx
import { usePayabli } from './usePayabli.ts';
export const PayabliCheckout = () => {
const token = "o.z8j8aaztW9tUtUg4dlVeYAx+L2MazOFGr0DY8yuK3u79MCYlGK4/q0t5AD1UgLAjXOohnxN8VTZfPswyZcwtChGNn1a8jFMmYWHmLN2cPDW9IrBt1RtrSuu+85HJI+4kML5sIk9SYvULDAU2k0X0E1KFYcPwjmmkUjktrEGtz48XCUM70aKUupkrTh8nL7CXpAXATzVUZ2gEld9jGINwECPPLWmu+cZ4CJb7QMJxnzKFD073+nq/eL+pMth7+u/SkmAWC0+jn8y+Lf6T5Q5PqB6wN7Mvosp8g7U7lbEW2wC0DA92pjblfDHVJOQUkjgT7B1GvryMokLvBjoiaLhKa55iKZE1YDlyqruILkoNF+zGSPS9r17qU6w4ziKhoMdSPzPBJBlLhQhz3MVANXbjfEfJwmtr/JJ1uStUfBFJ710cS1x7goxMJO/cl+q+LVtPy788EKFkgMc5OjfBNCsNL+dBDVbK5CiIJUSbOFzdqdjY/VJ14MEodsHYOwMAjuF4.KRFMeEj0SOur8MLZ362c/UZ/U/Az3CSUkr3/8EVDE6Y="
const entryPoint = "bozeman-aikido"
const [payabliConfig, setPayabliConfig, payabliExec, payabliReinit] = usePayabli({
type: "methodLightbox",
rootContainer: "pay-component-1",
buttonLabelInModal: 'Save Payment Method',
defaultOpen: 'ach',
hideComponent: true,
token: token,
entryPoint: entryPoint,
card: {
enabled: true,
amex: true,
discover: true,
visa: true,
mastercard: true,
jcb: true,
diners: true
},
ach: {
enabled: true,
checking: true,
savings: false
},
customerData: {
customerNumber: "00001",
firstName: "John",
lastName: "Doe",
billingEmail: "johndoe@email.com"
},
functionCallBackSuccess: (response: any) => {
// This callback covers both 2XX and 4XX responses
console.log(response);
switch (response.responseText) {
case "Success":
// Tokenization was successful
alert(`Success: ${response.responseData.resultText}`);
break;
case "Declined":
// Tokenization failed due to processor decline or validation errors
// Recommend reinitialization of the component so that the user can try again
// with different card data
alert(`Declined: ${response.responseData.resultText}`);
payabliReinit()
break;
default:
// Other response text. These are normally errors with Payabli internal validations
// before processor engagement
// We recommend reinitializing the component.
// If the problem persists, contact Payabli to help debug
alert(`Error: ${response.responseText}`);
payabliReinit()
break;
}
},
functionCallBackError: (errors: any) => {
// This callback covers 5XX response or parsing errors
// We recommend reinitializing the component.
// If the problem persists, contact Payabli to help debug
console.log(errors);
payabliReinit()
}
},
"pay", {
paymentDetails: {
totalAmount: 100,
serviceFee: 0,
categories: [
{
label: "payment",
amount: 100,
qty: 1,
},
],
},
})
return (
<div>
<div id="pay-component-1"></div>
<button onClick={payabliExec}>Pay</button>
<button onClick={() => setPayabliConfig({
...payabliConfig,
card: {
...payabliConfig.card,
enabled: !payabliConfig.card.enabled
}
})}>Toggle Card Payments</button>
</div>
)
}

Types

The hook receives the following arguments:

Arguments
options
PayabliEmbeddedMethodOptionsRequired

The configuration object for the embedded component.

method
stringRequired

The method to execute in payabliExec. See the field for more information.

parameters
PayabliEmbeddedComponentParameters

An optional object that contains objects to pass to the method. See the paymentMethod, paymentDetails, or customerData objects for more information.

production
booleanDefaults to false

A boolean value that determines whether to use the production or sandbox environment.

The hook returns an array with the following elements:

Return Values
payOptions
PayabliEmbeddedMethodOptions

The configuration object for the embedded component.

setPayOptions
(options: PayabliEmbeddedMethodOptions) => void

A function to update the configuration object for the embedded component. This allows you to dynamically change the options after initialization.

payabliExec
() => void

A function to execute the embedded component’s method. This will call the method specified in the method argument passed to the hook.

payabliReinit
() => void

A function to reinitialize the embedded component.

Vue

Visit the Vue integration example to see Payabli’s embedded components in a Vue application.

Step 1: Create the composable

Create a composable that allows you to use the embedded component and execute its methods. The composable needs to inject the Payabli library script and initialize the embedded component with the provided configuration. Make a new file for the usePayabli composable and add the following code:

TypeScript
// usePayabli.ts
import { ref, reactive, onMounted, watchEffect } from 'vue';
const loadedScripts = new Set<string>();
const useScript = (src: string) => {
const isLoaded = ref(false);
onMounted(() => {
if (loadedScripts.has(src)) {
isLoaded.value = true;
return;
}
const existingScript = document.querySelector(`script[src="${src}"]`);
const handleLoad = () => {
loadedScripts.add(src);
isLoaded.value = true;
script.setAttribute("data-loaded", "true");
};
let script: HTMLScriptElement;
if (!existingScript) {
script = document.createElement("script");
script.src = src;
script.async = true;
script.addEventListener("load", handleLoad);
document.body.appendChild(script);
} else {
if (existingScript.getAttribute("data-loaded") === "true") {
isLoaded.value = true;
} else {
existingScript.addEventListener("load", handleLoad);
}
}
});
return isLoaded;
};
declare var PayabliComponent: any;
export const usePayabli = (
options: any,
method: string,
parameters: any = null,
production = false
) => {
const payOptions = reactive({ ...options });
const isInitialized = ref(false);
const payComponentRef = ref<any>(null);
const initCallbacks: (() => void)[] = [];
const scriptSrc = production
? "https://embedded-component.payabli.com/component.js"
: "https://embedded-component-sandbox.payabli.com/component.js";
const isScriptLoaded = useScript(scriptSrc);
const initPayabli = () => {
if (!isScriptLoaded.value || isInitialized.value) return;
payComponentRef.value = new PayabliComponent(payOptions);
isInitialized.value = true;
initCallbacks.splice(0).forEach(cb => cb());
};
watchEffect(() => {
if (isScriptLoaded.value) {
initPayabli();
}
});
watchEffect(() => {
if (isInitialized.value && payComponentRef.value) {
payComponentRef.value.updateConfig(payOptions);
}
});
const payabliReinit = () => {
if (isInitialized.value && payComponentRef.value) {
payComponentRef.value.payabliExec("reinit");
}
};
const payabliExec = () => {
const exec = () => {
if (!payComponentRef.value) return;
if (payOptions.type === "methodEmbedded") {
if (parameters != null) {
payComponentRef.value.payabliExec(method, parameters);
} else {
payComponentRef.value.payabliExec(method);
}
} else if (
payOptions.type === "methodLightbox" ||
payOptions.type === "vterminal"
) {
payComponentRef.value.showModal();
}
};
if (isInitialized.value) {
exec();
} else {
initCallbacks.push(exec);
}
};
return [payOptions, payabliExec, payabliReinit] as const;
};

Step 2: Create the component

Create a new PayabliCheckout component that passes in the configuration object for the embedded component to the usePayabli composable. The PayabliCheckout component uses the payabliExec function to execute the embedded component’s method. Create a new file in the same directory and add the following code:

Vue
<template>
<div>
<div id="pay-component-1"></div>
<button id="btnx" class="hidden" @click="payabliExec">Pay</button>
<button @click.prevent="switchToJohnnyDover">Switch to Johnny Dover</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { usePayabli } from '../composables/usePayabli.ts';
const token = "o.z8j8aaztW9tUtUg4dlVeYAx+L2MazOFGr0DY8yuK3u79MCYlGK4/q0t5AD1UgLAjXOohnxN8VTZfPswyZcwtChGNn1a8jFMmYWHmLN2cPDW9IrBt1RtrSuu+85HJI+4kML5sIk9SYvULDAU2k0X0E1KFYcPwjmmkUjktrEGtz48XCUM70aKUupkrTh8nL7CXpAXATzVUZ2gEld9jGINwECPPLWmu+cZ4CJb7QMJxnzKFD073+nq/eL+pMth7+u/SkmAWC0+jn8y+Lf6T5Q5PqB6wN7Mvosp8g7U7lbEW2wC0DA92pjblfDHVJOQUkjgT7B1GvryMokLvBjoiaLhKa55iKZE1YDlyqruILkoNF+zGSPS9r17qU6w4ziKhoMdSPzPBJBlLhQhz3MVANXbjfEfJwmtr/JJ1uStUfBFJ710cS1x7goxMJO/cl+q+LVtPy788EKFkgMc5OjfBNCsNL+dBDVbK5CiIJUSbOFzdqdjY/VJ14MEodsHYOwMAjuF4.KRFMeEj0SOur8MLZ362c/UZ/U/Az3CSUkr3/8EVDE6Y=";
const entryPoint = "bozeman-aikido";
const rootContainer = "pay-component-1";
const payabliButton = "btnx";
const [payabliConfig, payabliExec] = usePayabli({
type: "methodEmbedded",
rootContainer: rootContainer,
defaultOpen: 'card',
token: token,
entryPoint: entryPoint,
card: {
enabled: true,
amex: true,
discover: true,
visa: true,
mastercard: true,
jcb: true,
diners: true,
inputs: {
cardHolderName: {
label: "NAME ON CARD",
placeholder: "",
floating: false,
value: "John Doe",
size: 12,
row: 0,
order: 0
},
cardNumber: {
label: "CARD NUMBER",
placeholder: "1234 1234 1234 1234",
floating: false,
size: 6,
row: 1,
order: 0
},
cardExpirationDate: {
label: "EXPIRATION DATE",
placeholder: "MM/YY",
floating: false,
size: 6,
row: 1,
order: 1
},
cardCvv: {
label: "CVV/CVC",
placeholder: "CVV/CVC",
floating: false,
size: 6,
row: 2,
order: 0,
},
cardZipcode: {
label: "ZIP/POSTAL CODE",
placeholder: "ZIP/POSTAL CODE",
floating: false,
size: 6,
row: 2,
order: 1,
country: ["us", "ca"],
}
}
},
ach: {
enabled: false,
checking: true,
savings: true
},
customerData: {
customerNumber: "00001",
firstName: "John",
lastName: "Doe",
billingEmail: "johndoe@email.com"
},
functionCallBackSuccess: (response) => {
const containerEl = document.getElementById(rootContainer);
const responseText = JSON.stringify(response.responseText);
const responseData = JSON.stringify(response.responseData);
alert(responseText + " " + responseData);
containerEl.innerHTML += `
<hr/>
<p><b>Embedded Component Response:</b></p>
<p>${responseText}</p>
<p>${responseData}</p>
<hr/>
`;
},
functionCallBackReady: (data) => {
const btn = document.getElementById(payabliButton);
if (data[1] === true && btn) {
btn.classList.remove("hidden");
} else if (btn) {
btn.classList.add("hidden");
}
},
functionCallBackError: (errors) => {
alert('Error!');
console.log(errors);
}
},
"pay", {
paymentDetails: {
totalAmount: 100,
serviceFee: 0,
categories: [
{
label: "payment",
amount: 100,
qty: 1,
},
],
},
});
const switchToJohnnyDover = () => {
payabliConfig.card.inputs.cardHolderName.value = "Johnny Dover";
};
</script>
<style scoped>
.hidden {
display: none;
}
</style>

Types

The composable receives the following arguments:

Arguments
options
PayabliEmbeddedMethodOptionsRequired

The configuration object for the embedded component.

method
stringRequired

The method to execute in payabliExec. See the field for more information.

parameters
PayabliEmbeddedComponentParameters

An optional object that contains objects to pass to the method. See the paymentMethod, paymentDetails, or customerData objects for more information.

production
booleanDefaults to false

A boolean value that determines whether to use the production or sandbox environment.

The composable returns an array with the following elements:

Return Values
payOptions
PayabliEmbeddedMethodOptions

The configuration object for the embedded component.

payabliExec
() => void

A function to execute the embedded component’s method. This will call the method specified in the method argument passed to the hook.

payabliReinit
() => void

A function to reinitialize the embedded component.