Back to blog
NestJSPaymentsStripeArchitecture

Multiple Payment Gateways in NestJS with a Stripe Example

How to design a payment abstraction in NestJS so Stripe is only one implementation behind a stable application contract.

Henrique Weiand

Henrique Weiand

Multiple Payment Gateways in NestJS with a Stripe Example

Payment integrations tend to start simple and become strategic. Today you may only need Stripe. Later, you may need Paddle, PayPal, regional providers, or a fallback gateway. This post is based on my original Medium article, How to Integrate Multiple Payment Gateways in NestJS.

The architecture goal is simple: application code should not depend directly on Stripe everywhere.

Define a payment contract

Start with an abstraction that represents what the application needs from a payment provider:

export abstract class PaymentGateway {
  abstract createPayment(input: CreatePaymentInput): Promise<PaymentResult>;
}

The application layer can depend on PaymentGateway, while infrastructure provides a concrete implementation.

Stripe as one implementation

The Stripe service imports Stripe's SDK, handles provider-specific payloads, and maps the response back to the application's PaymentResult.

That keeps Stripe-specific concerns in one place:

The rest of the application only sees the payment contract.

Wiring with NestJS

NestJS dependency injection can bind the abstract provider to the concrete class:

{
  provide: PaymentGateway,
  useClass: StripePaymentGateway,
}

If a different gateway is needed later, the binding can change without rewriting every use case.

Why it matters

Payments are external infrastructure. They should sit near the edge of the system, not inside domain logic.

This design makes it easier to:

Takeaways

Start with Stripe if that is the current need, but do not let the entire application become Stripe-shaped. A small payment abstraction gives the system room to evolve.