Skip to content

Configuration

You register PaymentsModule.forRoot() in your AppModule and pass it the gateways you want to enable. The library does not read .env: you build each gateway's config yourself (usually from your own environment variables — see Environment variables).

Registering the module

ts
import { Module } from '@nestjs/common'
import { EventEmitterModule } from '@nestjs/event-emitter'
import { TypeOrmModule } from '@nestjs/typeorm'
import { PaymentsModule, StripeGateway, RedsysGateway, PayPalGateway } from '@kaizen/payments-gateway'

@Module({
  imports: [
    EventEmitterModule.forRoot(),

    TypeOrmModule.forRoot({
      type: 'postgres',
      host: process.env.DB_HOST,
      port: Number(process.env.DB_PORT),
      username: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      autoLoadEntities: true, 
      synchronize: false,     
    }),

    PaymentsModule.forRoot({
      public_base_url: process.env.PUBLIC_BASE_URL!, 
      gateways: [
        StripeGateway.register({
          secret_key: process.env.STRIPE_SECRET_KEY!,
          webhook_secret: process.env.STRIPE_WEBHOOK_SECRET!,
        }),
        RedsysGateway.register({
          merchant_code: process.env.REDSYS_MERCHANT_CODE!,
          terminal: process.env.REDSYS_TERMINAL!,
          secret_key: process.env.REDSYS_SECRET_KEY!,
          environment: process.env.REDSYS_ENVIRONMENT as 'test' | 'production',
          merchant_url: process.env.REDSYS_MERCHANT_URL!,
          merchant_name: process.env.REDSYS_MERCHANT_NAME,
        }),
        PayPalGateway.register({
          client_id: process.env.PAYPAL_CLIENT_ID!,
          client_secret: process.env.PAYPAL_CLIENT_SECRET!,
          webhook_id: process.env.PAYPAL_WEBHOOK_ID!,
          environment: process.env.PAYPAL_ENVIRONMENT as 'sandbox' | 'production',
          public_base_url: process.env.PUBLIC_BASE_URL!,
        }),
      ],
    }),
  ],
})
export class AppModule {}

The specific fields of each register() (keys, currency, terminal, endpoints…) are documented on each gateway's page: Stripe, Redsys and PayPal.

public_base_url

It's the public URL of your API (e.g. https://your-domain.com). The library uses it for the gateways that need a browser round-trip: it serves Redsys's redirect page and builds PayPal's return_url. Locally it's usually http://localhost:3000.

Two mandatory requirements

Both are standard NestJS, but they're essential for the library to work:

1. autoLoadEntities: true

The library registers its entities (Payment, Refund, WebhookEvent) with an internal forFeature. With this flag the DataSource picks them up without you listing them.

2. rawBody: true at bootstrap

The webhooks controller verifies the signature over the raw body. Enable it in main.ts:

ts
// main.ts
const app = await NestFactory.create(AppModule, { rawBody: true }) 

WARNING

Without rawBody: true, Stripe signature verification always fails with 400.

What happens to your database

You don't have to create tables or run migrations. The first time your app boots, the library, over your same connection:

  1. Creates the payments schema if it doesn't exist.
  2. Creates its control table payments.migrations (separate from yours).
  3. Runs its pending migrations inside a transaction.

It's idempotent (booting N times is safe) and touches exclusively the payments schema: it never uses a global synchronize nor alters your tables.

Listening to events

ts
import { Injectable } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { PaymentEventPayload } from '@kaizen/payments-gateway'

@Injectable()
export class MyPaymentsListener {
  @OnEvent('payment.captured')
  onCaptured(payload: PaymentEventPayload) {
    // mark the order as paid, send an email, etc.
  }

  @OnEvent('payment.failed')
  onFailed(payload: PaymentEventPayload) {
    // …
  }
}

Continue with Environment variables.