From 6ea520e0eddcc3689e16c5b3c9b056e312ca7fa9 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Thu, 26 Mar 2026 21:24:27 +0100 Subject: [PATCH] feat: add mollie.js helper and embedded checkout API call --- lib/api/billing.ts | 16 +++++++++++ lib/mollie.ts | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 lib/mollie.ts diff --git a/lib/api/billing.ts b/lib/api/billing.ts index 666ac47..22c1391 100644 --- a/lib/api/billing.ts +++ b/lib/api/billing.ts @@ -101,3 +101,19 @@ export interface Order { export async function getOrders(): Promise { return apiRequest('/api/billing/invoices') } + +export interface CreateEmbeddedCheckoutParams { + plan_id: string + interval: string + limit: number + country: string + vat_id?: string + card_token: string +} + +export async function createEmbeddedCheckout(params: CreateEmbeddedCheckoutParams): Promise<{ status: 'success' | 'pending'; redirect_url?: string }> { + return apiRequest<{ status: 'success' | 'pending'; redirect_url?: string }>('/api/billing/checkout-embedded', { + method: 'POST', + body: JSON.stringify(params), + }) +} diff --git a/lib/mollie.ts b/lib/mollie.ts new file mode 100644 index 0000000..e731c8c --- /dev/null +++ b/lib/mollie.ts @@ -0,0 +1,69 @@ +'use client' + +// Mollie.js types (no official @types package) +export interface MollieInstance { + createComponent: (type: string, options?: { styles?: Record }) => MollieComponent + createToken: () => Promise<{ token: string | null; error: MollieError | null }> +} + +export interface MollieComponent { + mount: (selector: string | HTMLElement) => void + unmount: () => void + addEventListener: (event: string, callback: (event: unknown) => void) => void +} + +export interface MollieError { + field: string + message: string +} + +declare global { + interface Window { + Mollie: (profileId: string, options?: { locale?: string; testmode?: boolean }) => MollieInstance + } +} + +const MOLLIE_PROFILE_ID = process.env.NEXT_PUBLIC_MOLLIE_PROFILE_ID || '' + +// Mollie Components card field styles — matches Pulse dark theme +export const MOLLIE_FIELD_STYLES = { + base: { + color: '#ffffff', + fontSize: '14px', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + fontWeight: '400', + letterSpacing: '0.025em', + '::placeholder': { + color: '#737373', + }, + }, + valid: { + color: '#ffffff', + }, + invalid: { + color: '#ef4444', + }, +} + +let mollieInstance: MollieInstance | null = null + +/** + * Initialize Mollie.js. Must be called after the Mollie script has loaded. + */ +export function initMollie(): MollieInstance | null { + if (mollieInstance) return mollieInstance + if (typeof window === 'undefined' || !window.Mollie || !MOLLIE_PROFILE_ID) return null + + // Mollie auto-detects test/live mode based on the API key configured server-side. + mollieInstance = window.Mollie(MOLLIE_PROFILE_ID, { + locale: 'en_US', + }) + return mollieInstance +} + +/** + * Get the current Mollie instance (must call initMollie first). + */ +export function getMollie(): MollieInstance | null { + return mollieInstance +}