A payment-provider interface so MonCash, NatCash and cards are interchangeable

posée 4 months ago631 vuesen

7

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?

Diagnostic IA

Généré par IA. Ce n'est pas une réponse — la communauté ci-dessous le confirme ou le corrige. Vérifiez toujours avant de vous y fier.

1 réponse

7
✓
✓ Réponse acceptée

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.

Connectez-vous pour dire si ça a marché.

answered 4 months ago

Sign in and verify your email to post an answer.