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
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:
// 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:
- Creates the
paymentsschema if it doesn't exist. - Creates its control table
payments.migrations(separate from yours). - 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
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.