react-tap-payment
Version:
ReactJS library for implementing TAP payment gateway
439 lines (425 loc) • 16.6 kB
JavaScript
'use strict';
var React = require('react');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
const c = {
// checkoutURL: "https://tap-checkout.netlify.app/",
checkoutURL: "https://checkout.touchandpay.me/"
// checkoutURL: "http://localhost:3050/",
};
class u {
/**
* Constructor for the InlinePayment class.
* @param {object} transactionDefaults - Default transaction parameters.
*/
constructor(t) {
this.iframe = null, this.background = null, this.isIframeOpen = false, this.transactionDefaults = t, this.checkoutRemoved = false, this.fallback = h(), this.messageHandler = null, this.initializeCheckout(), this.listenForEvents();
}
/**
* Sets new transaction parameters and updates the iframe.
* If a transaction already exists, it resets the checkout before setting the new one.
* @param {object} transactionDefaults - The new transaction parameters.
*/
setTransaction(t) {
this.transactionDefaults && this.resetCheckout(), this.transactionDefaults = t, this.updateIframe();
}
/**
* Initializes the checkout by creating the popup background.
*/
initializeCheckout() {
this.createPopupBackground();
}
/**
* Prepares and returns the transaction parameters to be sent to the iframe.
* It excludes internal fields and formats metadata and split fields.
* @returns {object|null} The transaction parameters or null if not set.
*/
getTransactionParameters() {
if (!this.transactionDefaults)
return null;
const t = ["onClose", "callback"];
return f(this.transactionDefaults, t);
}
/**
* Sends a message to the iframe to update its content with new transaction details.
*/
updateIframe() {
const t = this.getTransactionParameters();
setTimeout(() => {
this.iframe.contentWindow.postMessage(
{
params: t
},
"*"
);
}, 3e3);
}
/**
* Sets up a window event listener to handle messages from the checkout iframe.
*/
listenForEvents() {
this.messageHandler = (t) => {
this.checkoutRemoved || this.handleCheckoutEvents(t);
}, window.addEventListener("message", this.messageHandler, false);
}
/**
* Opens the iframe to start a new checkout process.
*/
openIframe() {
this.showCheckout();
}
/**
* Creates the background and checkout iframes and appends them to the document body.
* The background iframe contains a loading spinner.
*/
createPopupBackground() {
const t = document.createElement("iframe");
t.setAttribute("frameBorder", "0"), t.setAttribute("allowtransparency", "true"), t.id = d(), t.name = "checkout-background-" + t.id, t.style.cssText = `
z-index: 999999999999999;
background: rgba(0, 0, 0, 0.75);
border: none;
overflow-x: hidden;
overflow-y: hidden;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
visibility: hidden;
display: none;
transition: opacity 0.3s;
`, this.background = t, document.body.appendChild(t);
const n = this.background.contentWindow.document;
n.open(), n.write(v()), n.close();
const i = document.createElement("iframe");
i.setAttribute("frameBorder", "0"), i.setAttribute("allowtransparency", "true"), i.setAttribute("allowpaymentrequest", "true"), i.id = d(), i.name = "checkout-" + i.id, i.style.cssText = `
z-index: 999999999999999;
background: transparent;
border: none;
overflow-x: hidden;
overflow-y: hidden;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
visibility: hidden;
display: none;
`, i.src = c.checkoutURL + "popup", this.iframe = i, document.body.appendChild(i);
}
/**
* Displays the checkout iframe and the background overlay.
*/
showCheckout() {
!this.iframe || this.isIframeOpen || (this.background.style.display = "", this.background.style.visibility = "visible", this.iframe.style.display = "", this.iframe.contentWindow.postMessage("render", "*"), this.isIframeOpen = true);
}
/**
* Hides the loading spinner in the background iframe once the transaction is loaded.
*/
removeLoader() {
this.iframe.style.visibility = "visible";
const t = this.background.contentWindow.document.getElementById("app-loader");
t && (t.style.display = "none");
}
/**
* Handles events received from the checkout iframe, such as success or close events.
* @param {MessageEvent} event - The event object from the message listener.
*/
handleCheckoutEvents(t) {
if (t.origin + "/" === c.checkoutURL && this.iframe.contentWindow === t.source) {
const n = t.data || t.message;
try {
n && n.response.success.status === 1 && (this.closeCheckout(!0), this.handleSuccess(n));
} catch {
}
switch (n) {
case "loaded:transaction":
this.removeLoader();
break;
case "close":
this.closeCheckout();
break;
}
}
}
/**
* Closes the checkout iframe and hides the background.
* @param {boolean} [success=false] - Indicates if the checkout was closed due to a successful transaction.
*/
closeCheckout(t = false) {
this.background.style.opacity = 0, this.iframe.style.display = "none", this.iframe.contentWindow.postMessage("close", "*"), this.isIframeOpen = false, !t && this.transactionDefaults.onClose && this.transactionDefaults.onClose.call(this), setTimeout(() => {
this.resetBackground();
}, 300);
}
/**
* Resets the iframe and background to their initial states.
*/
resetCheckout() {
this.resetIframe(), this.resetBackground();
}
/**
* Resets the main checkout iframe's visibility and clears transaction data.
*/
resetIframe() {
this.iframe.style.visibility = "hidden", this.transactionDefaults = null, this.updateIframe();
}
/**
* Resets the background iframe to its initial state, showing the loader.
*/
resetBackground() {
this.background.style.display = "none", this.background.style.opacity = 1, this.background.contentWindow.document.getElementById(
"app-loader"
).style.display = "block";
}
/**
* Handles the callback for a successful transaction.
* @param {object} response - The success response from the checkout iframe.
*/
handleSuccess(t) {
this.transactionDefaults.callback && this.transactionDefaults.callback.call(this, t);
}
/**
* Cleans up all resources, removing event listeners and DOM elements.
*/
destroy() {
this.messageHandler && (window.removeEventListener("message", this.messageHandler), this.messageHandler = null), this.iframe && (this.iframe.remove(), this.iframe = null), this.background && (this.background.remove(), this.background = null), this.isIframeOpen = false, this.checkoutRemoved = true;
}
}
let a;
const r = {
isInitialized: false,
/**
* Initializes the inline payment system with default transaction parameters.
* @param {object} transactionDefaults - Default transaction parameters.
*/
initialize(e) {
a = new u(e), console.log("TAPPaymentPop initialized"), this.isInitialized = true;
},
/**
* Sets up a new transaction. If not initialized, it will initialize first.
* @param {object} transactionDetails - The details for the transaction.
* @returns {InlinePayment|undefined} The instance of InlinePayment or undefined.
*/
setup(e) {
const t = {
apiKey: e.apiKey || "",
// required
transID: e.transID || "",
// required
amount: e.amount || "",
// required
email: e.email || "",
// required
env: e.env || "",
phone: e.phone || "",
superAgentFee: e.superAgentFee || "",
savePaymentDetails: e.savePaymentDetails || false,
customerReference: e.customerReference || "",
onClose: e.onClose || "",
callback: e.callback || "",
customPayload: {
email: e.email,
...e.customPayload || {}
// Ensure customPayload is an object
}
};
if (m(t))
return this.isInitialized ? (console.log("Setting transaction parameters"), a.setTransaction(t)) : (console.log("Initializing inline payment with parameters"), this.initialize(t)), a;
},
/**
* Destroys the current inline payment instance and cleans up resources.
*/
destroy() {
a && (a.destroy(), a = null), this.isInitialized = false;
}
};
function l() {
return typeof window < "u" ? window : {};
}
if (typeof l() < "u") {
const e = l();
e.TAPPaymentPop = r, e.onload = () => {
r.isInitialized || r.initialize();
};
}
function d() {
let e = "";
const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let n = 0; n < 5; n++)
e += t.charAt(Math.floor(Math.random() * t.length));
return e;
}
function h() {
const e = "onload" in document.createElement("iframe");
return e || console.warn(
"This browser does not support iframes. Please redirect to standard"
), !e;
}
function f(e, t) {
const n = JSON.parse(JSON.stringify(e));
return t.forEach((i) => {
delete n[i];
}), Object.keys(n).forEach((i) => {
(n[i] === null || n[i] === void 0 || n[i].length === 0) && delete n[i];
}), n;
}
function m(e) {
if (p(e), !e.apiKey)
throw new Error("Please provide your public key via the key attribute");
if (!e.amount)
throw new Error("Please provide transaction amount via the amount");
if (!e.email)
throw new Error(
"Please provide customer email via the email or customerCode attribute"
);
if (e.savePaymentDetails) {
if (!e.customerReference)
throw new Error(
"Please provide customerReference when savePaymentDetails is true."
);
if (!e.phone)
throw new Error("Please provide phone when savePaymentDetails is true.");
}
return true;
}
function p(e) {
const t = {
email: "email",
amount: "number",
superAgentFee: "number",
onClose: "function",
callback: "function"
};
for (const s in e)
e.hasOwnProperty(s) && n(s, e[s]);
function n(s, o) {
if (t[s] && o)
switch (t[s]) {
case "email":
y(o) || i(s);
break;
case "number":
b(o) || i(s);
break;
case "function":
g(o) || i(s);
break;
case "object":
w(o) || i(s);
break;
case "array":
k(o) || i(s);
}
}
function i(s) {
const o = t[s];
throw alert(`Attribute ${s} must be a valid ${o}`), new Error(`Attribute ${s} must be a valid ${o}`);
}
}
function y(e) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);
}
function b(e) {
const t = typeof e == "string" ? parseFloat(e) : e;
return !isNaN(t) && isFinite(t) && t > 0;
}
function g(e) {
return typeof e == "function";
}
function w(e) {
return e !== null && typeof e == "object";
}
function k(e) {
return Array.isArray(e);
}
function v() {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Loading...</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { display: flex; justify-content: center; align-items: center; height: 100vh; background: rgba(0, 0, 0, 0.50); }
.loader { width: 50px; height: 50px; border: 5px solid white; border-top-color: transparent; border-radius: 50%; animation: spin 1s linear infinite; }
spin { to { transform: rotate(360deg); } }
</style>
</head>
<body>
<div class="loader" id="app-loader"></div>
</body>
</html>`;
}
var callTAPPaymentPop = function (tapPaymentArgs) {
var handler = r.setup(tapPaymentArgs);
handler.openIframe();
};
function useTAPPayment(hookConfig) {
function initializePayment(_a) {
var config = _a.config, onSuccess = _a.onSuccess, onClose = _a.onClose;
var args = __assign(__assign({}, hookConfig), config);
var apiKey = args.apiKey, amount = args.amount, transID = args.transID, firstname = args.firstname, lastname = args.lastname, phone = args.phone, email = args.email, env = args.env, superMerchantFee = args.superMerchantFee, savePaymentDetails = args.savePaymentDetails, customerReference = args.customerReference, billerID = args.billerID, productID = args.productID, metadata = args.metadata, label = args.label, quantity = args.quantity, rest = __rest(args, ["apiKey", "amount", "transID", "firstname", "lastname", "phone", "email", "env", "superMerchantFee", "savePaymentDetails", "customerReference", "billerID", "productID", "metadata", "label", "quantity"]);
var tapPaymentArgs = {
callback: onSuccess ? onSuccess : function () { return null; },
onClose: onClose ? onClose : function () { return null; },
apiKey: apiKey,
transID: transID,
email: email,
amount: amount,
env: env,
superMerchantFee: superMerchantFee !== null && superMerchantFee !== void 0 ? superMerchantFee : undefined,
phone: phone !== null && phone !== void 0 ? phone : undefined,
savePaymentDetails: savePaymentDetails !== null && savePaymentDetails !== void 0 ? savePaymentDetails : undefined,
customerReference: customerReference !== null && customerReference !== void 0 ? customerReference : undefined,
customPayload: __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({}, (firstname && { firstname: firstname })), (lastname && { lastname: lastname })), (email && { email: email })), (phone && { phone: phone })), (billerID && { billerID: billerID })), (productID && { productID: productID })), (metadata && { metadata: metadata })), (label && { label: label })), (quantity && { quantity: quantity })), (rest && __assign({}, rest)))
};
callTAPPaymentPop(tapPaymentArgs);
}
return initializePayment;
}
var TAPPaymentButton = function (_a) {
var text = _a.text, className = _a.className, children = _a.children, onSuccess = _a.onSuccess, onClose = _a.onClose, disabled = _a.disabled, config = __rest(_a, ["text", "className", "children", "onSuccess", "onClose", "disabled"]);
var initializePayment = useTAPPayment(config);
return (React.createElement("button", { className: className, onClick: function () { return initializePayment({ config: config, onSuccess: onSuccess, onClose: onClose }); }, disabled: disabled }, text || children));
};
exports.TAPPaymentButton = TAPPaymentButton;
exports.useTAPPayment = useTAPPayment;
//# sourceMappingURL=index.js.map