A payment-provider interface so MonCash, NatCash and cards are interchangeable
poze 4 months ago624 gadeen
I'm starting with MonCash, adding NatCash when Natcom gives us access, and maybe cards later for the diaspora. What interface should the rest of my code depend on?
Dyagnostik IA
Se IA ki fè l. Se pa yon repons — kominote a anba a konfime oswa korije l. Toujou verifye anvan ou fye l.
1 Repons
Keep the interface small and model what your business needs, not any provider's API:
export type PaymentStatus = "PENDING" | "SUCCEEDED" | "FAILED";
export interface PaymentProvider {
readonly id: "MONCASH" | "NATCASH" | "CARD";
/** Start a payment for one attempt. Returns where to send the customer (or instructions). */
start(input: { attemptId: string; amountHtgCents: number; customerPhone?: string }):
Promise<{ kind: "redirect"; url: string } | { kind: "instructions"; text: string }>;
/** Ask the provider, server to server, what really happened. Never trust the browser. */
status(attemptId: string): Promise<{
status: PaymentStatus;
providerReference?: string;
amountHtgCents?: number;
}>;
}
export const providers: Record<PaymentProvider["id"], PaymentProvider> = {
MONCASH: new MonCashProvider(),
NATCASH: new NatCashProvider(),
CARD: new CardProvider(),
};
One confirmation routine serves every provider:
export async function confirmAttempt(attemptId: string) {
const attempt = await db.paymentAttempt.findUniqueOrThrow({ where: { id: attemptId } });
const result = await providers[attempt.provider].status(attempt.id);
if (result.status !== "SUCCEEDED") return result.status;
if (result.amountHtgCents !== attempt.amountHtgCents) {
await flagForReview(attempt.id, "amount mismatch");
return "FAILED";
}
await markPaidOnce(attempt.orderId, attempt.provider, result.providerReference!);
return "SUCCEEDED";
}
Why kind: "instructions": some providers, or a manual fallback, don't redirect. They tell the customer to dial a USSD menu or send to a merchant number. The UI handles both without knowing which provider it is.
Write a fake provider too. Your tests and local development use it, and it doubles as living documentation of the contract.
Konekte pou di lòt moun si li te mache.
Sign in and verify your email to post an answer.