278 lines
9.3 KiB
JavaScript
278 lines
9.3 KiB
JavaScript
// HelcimPay.js integration composable
|
|
export const useHelcimPay = () => {
|
|
let checkoutToken = null;
|
|
let secretToken = null;
|
|
|
|
// Initialize HelcimPay.js session
|
|
const initializeHelcimPay = async (customerId, customerCode, amount = 0) => {
|
|
try {
|
|
const response = await $fetch("/api/helcim/initialize-payment", {
|
|
method: "POST",
|
|
body: {
|
|
customerId,
|
|
customerCode,
|
|
amount,
|
|
},
|
|
});
|
|
|
|
if (response.success) {
|
|
checkoutToken = response.checkoutToken;
|
|
secretToken = response.secretToken;
|
|
return true;
|
|
}
|
|
|
|
throw new Error("Failed to initialize payment session");
|
|
} catch (error) {
|
|
console.error("Payment initialization error:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Initialize payment for event ticket purchase
|
|
const initializeTicketPayment = async (
|
|
eventId,
|
|
email,
|
|
amount,
|
|
eventTitle = null,
|
|
) => {
|
|
try {
|
|
const response = await $fetch("/api/helcim/initialize-payment", {
|
|
method: "POST",
|
|
body: {
|
|
customerId: null,
|
|
customerCode: email, // Use email as customer code for event tickets
|
|
amount,
|
|
metadata: {
|
|
type: "event_ticket",
|
|
eventId,
|
|
email,
|
|
eventTitle,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (response.success) {
|
|
checkoutToken = response.checkoutToken;
|
|
secretToken = response.secretToken;
|
|
return {
|
|
success: true,
|
|
checkoutToken: response.checkoutToken,
|
|
};
|
|
}
|
|
|
|
throw new Error("Failed to initialize ticket payment session");
|
|
} catch (error) {
|
|
console.error("Ticket payment initialization error:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Show payment modal
|
|
const showPaymentModal = () => {
|
|
return new Promise((resolve, reject) => {
|
|
if (!checkoutToken) {
|
|
reject(
|
|
new Error("Payment not initialized. Call initializeHelcimPay first."),
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Add CSS to style Helcim overlay - needs to target the actual elements Helcim creates
|
|
// Helcim injects a div with inline styles directly into body
|
|
if (!document.getElementById("helcim-overlay-fix")) {
|
|
const style = document.createElement("style");
|
|
style.id = "helcim-overlay-fix";
|
|
style.textContent = `
|
|
/* Style any fixed position div that Helcim creates */
|
|
body > div[style] {
|
|
background-color: rgba(0, 0, 0, 0.75) !important;
|
|
}
|
|
|
|
/* Target specifically iframes from Helcim */
|
|
body > div[style] > iframe[src*="helcim"],
|
|
body > div[style] iframe[src*="secure.helcim.app"] {
|
|
background: white !important;
|
|
border: none !important;
|
|
border-radius: 8px !important;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3) !important;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
// Load HelcimPay.js modal script
|
|
if (!window.appendHelcimPayIframe) {
|
|
console.log("HelcimPay script not loaded, loading now...");
|
|
const script = document.createElement("script");
|
|
script.src = "https://secure.helcim.app/helcim-pay/services/start.js";
|
|
script.async = true;
|
|
script.onload = () => {
|
|
console.log("HelcimPay script loaded successfully!");
|
|
console.log(
|
|
"Available functions:",
|
|
Object.keys(window).filter(
|
|
(key) => key.includes("Helcim") || key.includes("helcim"),
|
|
),
|
|
);
|
|
console.log(
|
|
"appendHelcimPayIframe available:",
|
|
typeof window.appendHelcimPayIframe,
|
|
);
|
|
openModal(resolve, reject);
|
|
};
|
|
script.onerror = () => {
|
|
reject(new Error("Failed to load HelcimPay.js"));
|
|
};
|
|
document.head.appendChild(script);
|
|
} else {
|
|
console.log("HelcimPay script already loaded, calling openModal");
|
|
openModal(resolve, reject);
|
|
}
|
|
});
|
|
};
|
|
|
|
// Open the payment modal
|
|
const openModal = (resolve, reject) => {
|
|
try {
|
|
console.log("Trying to open modal with checkoutToken:", checkoutToken);
|
|
|
|
if (typeof window.appendHelcimPayIframe === "function") {
|
|
// Set up event listener for HelcimPay.js responses
|
|
const helcimPayJsIdentifierKey = "helcim-pay-js-" + checkoutToken;
|
|
|
|
const handleHelcimPayEvent = (event) => {
|
|
console.log("Received window message:", event.data);
|
|
|
|
if (event.data.eventName === helcimPayJsIdentifierKey) {
|
|
console.log("HelcimPay event received:", event.data);
|
|
|
|
// Remove event listener to prevent multiple responses
|
|
window.removeEventListener("message", handleHelcimPayEvent);
|
|
|
|
// Close the Helcim modal
|
|
if (typeof window.removeHelcimPayIframe === "function") {
|
|
window.removeHelcimPayIframe();
|
|
}
|
|
|
|
if (event.data.eventStatus === "SUCCESS") {
|
|
console.log("Payment success:", event.data.eventMessage);
|
|
|
|
// Parse the JSON string eventMessage
|
|
let paymentData;
|
|
try {
|
|
paymentData = JSON.parse(event.data.eventMessage);
|
|
console.log("Parsed payment data:", paymentData);
|
|
} catch (parseError) {
|
|
console.error("Failed to parse eventMessage:", parseError);
|
|
reject(new Error("Invalid payment response format"));
|
|
return;
|
|
}
|
|
|
|
// Extract transaction details from nested data structure
|
|
const transactionData = paymentData.data?.data || {};
|
|
console.log("Transaction data:", transactionData);
|
|
|
|
resolve({
|
|
success: true,
|
|
transactionId: transactionData.transactionId,
|
|
cardToken: transactionData.cardToken,
|
|
cardLast4: transactionData.cardNumber
|
|
? transactionData.cardNumber.slice(-4)
|
|
: undefined,
|
|
cardType: transactionData.cardType || "unknown",
|
|
});
|
|
} else if (event.data.eventStatus === "ABORTED") {
|
|
console.log("Payment aborted:", event.data.eventMessage);
|
|
reject(new Error(event.data.eventMessage || "Payment failed"));
|
|
} else if (event.data.eventStatus === "HIDE") {
|
|
console.log("Modal closed without completion");
|
|
reject(new Error("Payment cancelled by user"));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Add event listener
|
|
window.addEventListener("message", handleHelcimPayEvent);
|
|
|
|
// Set up a MutationObserver to fix Helcim's overlay styling immediately
|
|
const observer = new MutationObserver((mutations) => {
|
|
mutations.forEach((mutation) => {
|
|
mutation.addedNodes.forEach((node) => {
|
|
if (node.nodeType === 1 && node.tagName === "DIV") {
|
|
const computedStyle = window.getComputedStyle(node);
|
|
// Check if this is Helcim's overlay (fixed position, full screen)
|
|
if (
|
|
computedStyle.position === "fixed" &&
|
|
computedStyle.inset === "0px"
|
|
) {
|
|
// Fix the background to show semi-transparent overlay
|
|
node.style.setProperty(
|
|
"background-color",
|
|
"rgba(0, 0, 0, 0.75)",
|
|
"important",
|
|
);
|
|
// Ensure proper centering
|
|
node.style.setProperty("display", "flex", "important");
|
|
node.style.setProperty("align-items", "center", "important");
|
|
node.style.setProperty(
|
|
"justify-content",
|
|
"center",
|
|
"important",
|
|
);
|
|
observer.disconnect(); // Stop observing once we've found and fixed it
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Start observing body for child additions
|
|
observer.observe(document.body, { childList: true });
|
|
|
|
// Open the HelcimPay iframe modal
|
|
console.log("Calling appendHelcimPayIframe with token:", checkoutToken);
|
|
window.appendHelcimPayIframe(checkoutToken, true);
|
|
console.log(
|
|
"appendHelcimPayIframe called, waiting for window messages...",
|
|
);
|
|
|
|
// Clean up observer after a timeout
|
|
setTimeout(() => observer.disconnect(), 5000);
|
|
|
|
// Add timeout to clean up if no response
|
|
setTimeout(() => {
|
|
console.log("60 seconds passed, cleaning up event listener...");
|
|
window.removeEventListener("message", handleHelcimPayEvent);
|
|
reject(new Error("Payment timeout - no response received"));
|
|
}, 60000);
|
|
} else {
|
|
reject(new Error("appendHelcimPayIframe function not available"));
|
|
}
|
|
} catch (error) {
|
|
console.error("Error opening modal:", error);
|
|
reject(error);
|
|
}
|
|
};
|
|
|
|
// Process payment verification
|
|
const verifyPayment = async () => {
|
|
try {
|
|
return await showPaymentModal();
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Cleanup tokens
|
|
const cleanup = () => {
|
|
checkoutToken = null;
|
|
secretToken = null;
|
|
};
|
|
|
|
return {
|
|
initializeHelcimPay,
|
|
initializeTicketPayment,
|
|
verifyPayment,
|
|
cleanup,
|
|
};
|
|
};
|