// The server's refusals of a staff payment, in the words staff see. Any other answer is not a refusal: // the payment may have landed, so its key is kept. type Refusal = { outcome?: unknown; message?: unknown; openUntil?: unknown }; export const paymentRefusal = (body: unknown): string | undefined => { const refusal = body as Refusal | undefined; if (refusal?.outcome !== 'refused') return undefined; switch (refusal.message) { case 'amount_exceeds_remaining': case 'partial_payment_not_allowed': return 'The balance changed before the payment was recorded. Check what is owed and try again.'; case 'card_payment_open': { const until = typeof refusal.openUntil === 'string' ? new Date(refusal.openUntil) : undefined; const when = until && !Number.isNaN(until.getTime()) ? ` until ${until.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}` : ''; return `A card payment is open on this invoice${when}. Nothing was recorded; record this payment once it completes or expires.`; } default: return 'This invoice is no longer open for payment, so nothing was recorded.'; } };