302 lines
9.6 KiB
TypeScript
302 lines
9.6 KiB
TypeScript
import type { ICustomer, IRedirect } from '@/features/cashier/types';
|
|
|
|
// Store parameters received via postMessage from parent window
|
|
const postMessageParams: Record<string, string> = {};
|
|
let postMessageCustomer: Partial<ICustomer> | null = null;
|
|
|
|
// Initialize postMessage listener to receive params from parent window
|
|
if (typeof window !== 'undefined' && window.parent !== window) {
|
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
// Accept messages from any origin (parent window)
|
|
// In production, you might want to validate event.origin
|
|
if (event.data && typeof event.data === 'object') {
|
|
// If the message contains config params, store them
|
|
if (event.data.type === 'CASHIER_CONFIG' || !event.data.type) {
|
|
// Convert object to URLSearchParams-like format
|
|
Object.entries(event.data).forEach(([key, value]) => {
|
|
if (key === 'customer' && typeof value === 'object' && value !== null) {
|
|
// Handle customer object separately
|
|
postMessageCustomer = value as Partial<ICustomer>;
|
|
} else if (key !== 'type' && typeof value === 'string') {
|
|
postMessageParams[key] = value;
|
|
} else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
postMessageParams[key] = String(value);
|
|
}
|
|
});
|
|
console.log('Received params via postMessage:', postMessageParams);
|
|
console.log('Received customer via postMessage:', postMessageCustomer);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
export interface ICashierConfig {
|
|
// Core payment configuration
|
|
paymentType?: 'deposit' | 'withdrawal';
|
|
currency?: string;
|
|
amount?: number;
|
|
account?: string; // For withdrawal
|
|
|
|
// Customer information
|
|
customer?: Partial<ICustomer>;
|
|
|
|
// Redirect URLs
|
|
redirect?: Partial<IRedirect>;
|
|
|
|
// Merchant and session info
|
|
merchantId?: string;
|
|
userId?: string; // Maps to customer.id
|
|
sessionId?: string;
|
|
environment?: 'production' | 'staging' | 'development';
|
|
|
|
// UI/UX configuration
|
|
locale?: string;
|
|
fetchConfig?: boolean;
|
|
predefinedValues?: Record<string, unknown>;
|
|
prefillCreditcardHolder?: boolean;
|
|
showAccounts?: string;
|
|
autoOpenFirstPaymentMethod?: boolean;
|
|
showTransactionOverview?: boolean;
|
|
showAmountLimits?: boolean;
|
|
receiptExcludeKeys?: string[];
|
|
|
|
// Attributes
|
|
attributes?: {
|
|
hostUri?: string;
|
|
bootstrapVersion?: string;
|
|
[key: string]: unknown;
|
|
};
|
|
}
|
|
|
|
export function getCashierConfig(): ICashierConfig {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
// Merge URL params with postMessage params (postMessage params take precedence)
|
|
const params = new URLSearchParams();
|
|
|
|
// First add URL params
|
|
urlParams.forEach((value, key) => {
|
|
params.set(key, value);
|
|
});
|
|
|
|
// Then override/add postMessage params
|
|
Object.entries(postMessageParams).forEach(([key, value]) => {
|
|
params.set(key, value);
|
|
});
|
|
|
|
// Debug: Log all parameters (URL + postMessage)
|
|
console.log('All URL params:', Object.fromEntries(urlParams.entries()));
|
|
console.log('All postMessage params:', postMessageParams);
|
|
console.log('Merged params:', Object.fromEntries(params.entries()));
|
|
|
|
const config: ICashierConfig = {
|
|
};
|
|
|
|
// Payment type from URL (support both 'method' and 'payment_type' for backward compatibility)
|
|
const method = params.get('method');
|
|
|
|
const paymentType = params.get('payment_type') || method;
|
|
if (paymentType === 'deposit' || paymentType === 'withdrawal') {
|
|
config.paymentType = paymentType;
|
|
}
|
|
|
|
// Currency from URL
|
|
const currency = params.get('currency');
|
|
if (currency) {
|
|
config.currency = currency;
|
|
}
|
|
|
|
// Amount from URL
|
|
const amount = params.get('amount');
|
|
if (amount && amount !== 'undefined') {
|
|
const parsedAmount = parseFloat(amount);
|
|
if (!isNaN(parsedAmount)) {
|
|
config.amount = parsedAmount;
|
|
}
|
|
}
|
|
|
|
// Account from URL (for withdrawal)
|
|
const account = params.get('account');
|
|
if (account) {
|
|
config.account = account;
|
|
}
|
|
|
|
// Merchant and session info
|
|
const merchantId = params.get('merchantId');
|
|
if (merchantId) {
|
|
config.merchantId = merchantId;
|
|
}
|
|
|
|
const userId = params.get('userId');
|
|
if (userId) {
|
|
// Use userId from URL remove for production
|
|
config.userId = '12345';
|
|
} else if (import.meta.env.DEV) {
|
|
// Development fallback: use a default numeric userId for testing
|
|
// The API expects customer ID to be a numeric value
|
|
config.userId = '123';
|
|
}
|
|
|
|
|
|
const sessionId = params.get('sessionId');
|
|
if (sessionId) {
|
|
config.sessionId = sessionId;
|
|
}
|
|
|
|
const environment = params.get('environment');
|
|
if (environment === 'production' || environment === 'staging' || environment === 'development') {
|
|
config.environment = environment;
|
|
}
|
|
|
|
// Customer data from URL (support both new 'userId' and old 'customer_id' format)
|
|
// Start with postMessage customer if available, otherwise create new object
|
|
const customer: Partial<ICustomer> = postMessageCustomer ? { ...postMessageCustomer } : {};
|
|
|
|
const customerId = params.get('customer_id') || userId;
|
|
if (customerId) {
|
|
customer.id = customerId;
|
|
} else if (userId && !customer.id) {
|
|
// Ensure userId is set on customer if available
|
|
customer.id = userId;
|
|
}
|
|
|
|
// Parse additional customer fields from URL params (only if not already set from postMessage)
|
|
if (!customer.first_name) {
|
|
const firstName = params.get('customer_first_name') || params.get('first_name');
|
|
if (firstName) customer.first_name = firstName;
|
|
}
|
|
|
|
if (!customer.last_name) {
|
|
const lastName = params.get('customer_last_name') || params.get('last_name');
|
|
if (lastName) customer.last_name = lastName;
|
|
}
|
|
|
|
if (!customer.email) {
|
|
const email = params.get('customer_email') || params.get('email');
|
|
if (email) customer.email = email;
|
|
}
|
|
|
|
if (Object.keys(customer).length > 0) {
|
|
config.customer = customer;
|
|
}
|
|
|
|
// Redirect URLs from URL
|
|
const redirect: Partial<IRedirect> = {};
|
|
const successUrl = params.get('redirect_success');
|
|
const cancelUrl = params.get('redirect_cancel');
|
|
const errorUrl = params.get('redirect_error');
|
|
|
|
if (successUrl) redirect.success = successUrl;
|
|
if (cancelUrl) redirect.cancel = cancelUrl;
|
|
if (errorUrl) redirect.error = errorUrl;
|
|
|
|
if (Object.keys(redirect).length > 0) {
|
|
config.redirect = redirect;
|
|
}
|
|
|
|
// UI/UX configuration
|
|
const locale = params.get('locale');
|
|
if (locale) {
|
|
config.locale = locale;
|
|
}
|
|
|
|
const fetchConfig = params.get('fetchConfig');
|
|
if (fetchConfig !== null) {
|
|
config.fetchConfig = fetchConfig === 'true';
|
|
}
|
|
|
|
// Parse predefinedValues (JSON object)
|
|
const predefinedValuesStr = params.get('predefinedValues');
|
|
if (predefinedValuesStr && predefinedValuesStr !== '[object Object]') {
|
|
try {
|
|
const decoded = decodeURIComponent(predefinedValuesStr);
|
|
config.predefinedValues = JSON.parse(decoded);
|
|
} catch {
|
|
// If parsing fails, ignore it
|
|
}
|
|
}
|
|
|
|
const prefillCreditcardHolder = params.get('prefillCreditcardHolder');
|
|
if (prefillCreditcardHolder !== null) {
|
|
config.prefillCreditcardHolder = prefillCreditcardHolder === 'true';
|
|
}
|
|
|
|
const showAccounts = params.get('showAccounts');
|
|
if (showAccounts) {
|
|
config.showAccounts = showAccounts;
|
|
}
|
|
|
|
const autoOpenFirstPaymentMethod = params.get('autoOpenFirstPaymentMethod');
|
|
if (autoOpenFirstPaymentMethod !== null) {
|
|
config.autoOpenFirstPaymentMethod = autoOpenFirstPaymentMethod === 'true';
|
|
}
|
|
|
|
const showTransactionOverview = params.get('showTransactionOverview');
|
|
if (showTransactionOverview !== null) {
|
|
config.showTransactionOverview = showTransactionOverview === 'true';
|
|
}
|
|
|
|
const showAmountLimits = params.get('showAmountLimits');
|
|
if (showAmountLimits !== null) {
|
|
config.showAmountLimits = showAmountLimits === 'true';
|
|
}
|
|
|
|
// Receipt exclude keys (comma-separated string)
|
|
const receiptExcludeKeys = params.get('receiptExcludeKeys');
|
|
if (receiptExcludeKeys) {
|
|
config.receiptExcludeKeys = receiptExcludeKeys.split(',').map(key => key.trim());
|
|
}
|
|
|
|
// Attributes (handle attributes.* parameters)
|
|
const attributes: Record<string, unknown> = {};
|
|
params.forEach((value, key) => {
|
|
if (key.startsWith('attributes.')) {
|
|
const attrKey = key.replace('attributes.', '');
|
|
attributes[attrKey] = value;
|
|
}
|
|
});
|
|
|
|
if (Object.keys(attributes).length > 0) {
|
|
config.attributes = attributes;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
// Default redirect URLs (fallback if not provided)
|
|
// Convert relative paths to absolute URLs for payment provider redirects
|
|
// Uses hash-based routing for iframe compatibility
|
|
export function getDefaultRedirectUrls(): IRedirect {
|
|
const currentOrigin = window.location.origin;
|
|
const currentPath = window.location.pathname.replace(/\/[^/]*$/, ''); // Remove last path segment if any
|
|
|
|
return {
|
|
success: `${currentOrigin}${currentPath}#/result?status=success`,
|
|
cancel: `${currentOrigin}${currentPath}#/result?status=cancel`,
|
|
error: `${currentOrigin}${currentPath}#/result?status=error`,
|
|
};
|
|
}
|
|
|
|
// Convert relative URLs to absolute URLs with hash-based routing
|
|
export function normalizeRedirectUrl(url: string): string {
|
|
// If it's already an absolute URL, return as-is
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
return url;
|
|
}
|
|
|
|
// If it's a relative path, convert to absolute with hash routing
|
|
const currentOrigin = window.location.origin;
|
|
const currentPath = window.location.pathname.replace(/\/[^/]*$/, '');
|
|
|
|
// Handle hash-based routing - if URL starts with /, convert to #/
|
|
// Preserve query parameters
|
|
let hashPath = url;
|
|
if (hashPath.startsWith('/')) {
|
|
hashPath = `#${hashPath}`;
|
|
} else if (!hashPath.startsWith('#')) {
|
|
hashPath = `#/${hashPath}`;
|
|
}
|
|
|
|
return `${currentOrigin}${currentPath}${hashPath}`;
|
|
}
|