import { logger } from '../logger'; export type PaymentProvider = 'ORANGE_MONEY' | 'WAVE'; export interface PaymentInitRequest { amount: number; currency: string; reference: string; notifyUrl: string; returnUrl: string; description: string; customerPhone?: string; } export interface PaymentInitResult { paymentUrl: string; sessionId: string; provider: PaymentProvider; } export interface WebhookPayload { status: 'SUCCESS' | 'FAILED' | 'PENDING'; reference: string; transactionId?: string; amount?: number; provider: PaymentProvider; } // Orange Money Merchant API (West Africa) async function initOrangeMoney(req: PaymentInitRequest): Promise { const apiKey = process.env.ORANGE_MONEY_API_KEY; const merchantId = process.env.ORANGE_MONEY_MERCHANT_ID; const apiUrl = process.env.ORANGE_MONEY_API_URL || 'https://api.orange.com/orange-money-webpay/dev/v1'; if (!apiKey || !merchantId) throw new Error('Orange Money credentials not configured'); const res = await fetch(`${apiUrl}/webpayment`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ merchant_key: merchantId, currency: req.currency, order_id: req.reference, amount: req.amount, return_url: req.returnUrl, cancel_url: req.returnUrl, notif_url: req.notifyUrl, lang: 'fr', reference: req.reference, }), }); if (!res.ok) { const err = await res.json().catch(() => ({}) ) as { message?: string }; throw new Error(`Orange Money init failed: ${err.message || res.status}`); } const data = await res.json() as { payment_url: string; pay_token: string }; return { paymentUrl: data.payment_url, sessionId: data.pay_token, provider: 'ORANGE_MONEY', }; } // Wave API (Senegal) async function initWave(req: PaymentInitRequest): Promise { const apiKey = process.env.WAVE_API_KEY; const apiUrl = 'https://api.wave.com/v1/checkout/sessions'; if (!apiKey) throw new Error('Wave API key not configured'); const res = await fetch(apiUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ amount: String(req.amount), currency: req.currency, error_url: req.returnUrl, success_url: `${req.returnUrl}?success=1&ref=${req.reference}`, client_reference: req.reference, }), }); if (!res.ok) { const err = await res.json().catch(() => ({}) ) as { message?: string }; throw new Error(`Wave init failed: ${err.message || res.status}`); } const data = await res.json() as { wave_launch_url: string; id: string }; return { paymentUrl: data.wave_launch_url, sessionId: data.id, provider: 'WAVE', }; } export async function initiatePayment(provider: PaymentProvider, req: PaymentInitRequest): Promise { logger.info({ provider, reference: req.reference, amount: req.amount }, '[PAYMENT] Initiating payment'); if (provider === 'ORANGE_MONEY') return initOrangeMoney(req); if (provider === 'WAVE') return initWave(req); throw new Error(`Unsupported provider: ${provider}`); } // Parse and normalize webhooks from Orange Money and Wave into a unified format export function parseWebhook(provider: PaymentProvider, body: Record): WebhookPayload { if (provider === 'ORANGE_MONEY') { const statusMap: Record = { SUCCESS: 'SUCCESS', FAILED: 'FAILED', INITIATED: 'PENDING', }; return { provider, reference: String(body.order_id || body.reference || ''), transactionId: String(body.txnid || ''), amount: Number(body.amount || 0), status: statusMap[String(body.status)] ?? 'FAILED', }; } if (provider === 'WAVE') { return { provider, reference: String(body.client_reference || ''), transactionId: String(body.id || ''), amount: Number(body.amount || 0), status: body.payment_status === 'succeeded' ? 'SUCCESS' : 'FAILED', }; } throw new Error(`Unknown provider: ${provider}`); }