@ambushsoftworks/nestjs-payments-graphql 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,612 @@
1
+ # @ambushsoftworks/nestjs-payments-graphql
2
+
3
+ Production-grade payments module for NestJS with GraphQL support. Invoicing, Stripe payment processing, refunds, payment plans, recurring invoicing with auto-charge, e-transfer, and composable email notifications — with zero database coupling.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Design Principles](#design-principles)
8
+ - [Installation](#installation)
9
+ - [Quick Start](#quick-start)
10
+ - [Architecture](#architecture)
11
+ - [Features](#features)
12
+ - [Invoicing](#invoicing)
13
+ - [Stripe Gateway & Webhooks](#stripe-gateway--webhooks)
14
+ - [Refunds](#refunds)
15
+ - [Payment Plans](#payment-plans)
16
+ - [Recurring Invoices](#recurring-invoices)
17
+ - [E-Transfer](#e-transfer)
18
+ - [Email Notifications](#email-notifications)
19
+ - [Event Listeners](#event-listeners)
20
+ - [Transactions](#transactions)
21
+ - [Configuration Reference](#configuration-reference)
22
+ - [Required Options](#required-options)
23
+ - [Optional Repositories](#optional-repositories)
24
+ - [Stripe Options](#stripe-options)
25
+ - [Email Options](#email-options)
26
+ - [Defaults & Feature Flags](#defaults--feature-flags)
27
+ - [DI Tokens](#di-tokens)
28
+ - [Reserved GraphQL Type Names](#reserved-graphql-type-names)
29
+ - [Exceptions](#exceptions)
30
+ - [Critical Integration Requirements](#critical-integration-requirements)
31
+ - [Local Development](#local-development)
32
+ - [License](#license)
33
+
34
+ ---
35
+
36
+ ## Design Principles
37
+
38
+ - **Interface-driven persistence** — All storage is behind interfaces (`IInvoiceRepository`, `IPaymentRepository`, `IRefundRepository`, etc.). Bring your own ORM; Prisma is the expected default, but any ORM works.
39
+ - **Instance-based DI** — Repositories and adapters are injected as instances via `useFactory`, not as classes.
40
+ - **Framework-agnostic core** — Services are pure business logic. Resolvers, schedulers, and Prisma adapters live in your application.
41
+ - **Consumer owns GraphQL decoration** — The package ships `@ObjectType`/`@InputType` classes. You build resolvers that call services and return these types.
42
+ - **Composable email** — A sender (transport) plus a renderer (HTML) plus a branding resolver plus a client resolver. All but the first two are optional with sane defaults.
43
+ - **Stripe is bundled, not required** — The Stripe gateway and webhook controller ship with the package, but are only activated when `stripe` config is provided.
44
+
45
+ ---
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ npm install @ambushsoftworks/nestjs-payments-graphql
51
+ # or
52
+ pnpm add @ambushsoftworks/nestjs-payments-graphql
53
+ ```
54
+
55
+ ### Peer Dependencies
56
+
57
+ ```bash
58
+ npm install @nestjs/common @nestjs/core @nestjs/graphql \
59
+ class-transformer class-validator graphql graphql-type-json \
60
+ reflect-metadata rxjs
61
+ ```
62
+
63
+ `stripe` is bundled as a hard dependency of this package — you do not need to install it separately.
64
+
65
+ ---
66
+
67
+ ## Quick Start
68
+
69
+ A minimal setup: invoices, manual payments, and a Stripe gateway.
70
+
71
+ ### 1. Implement the required repositories
72
+
73
+ Each repository is a pure TypeScript interface backed by your ORM. Here is the sketch for Prisma. A ready-to-copy reference schema lives at [`prisma/payment-models.prisma`](./prisma/payment-models.prisma) and ships inside the npm tarball — it contains every model and enum you need, with consumer-owned relations marked `// CONSUMER:`.
74
+
75
+ ```typescript
76
+ // prisma-invoice.repository.ts
77
+ import { Injectable } from '@nestjs/common';
78
+ import { PrismaService } from './prisma.service';
79
+ import {
80
+ IInvoiceRepository,
81
+ UniqueConstraintViolationException,
82
+ } from '@ambushsoftworks/nestjs-payments-graphql';
83
+
84
+ @Injectable()
85
+ export class PrismaInvoiceRepository implements IInvoiceRepository {
86
+ constructor(private readonly prisma: PrismaService) {}
87
+
88
+ async create(data) {
89
+ try {
90
+ return await this.prisma.invoice.create({ data });
91
+ } catch (e: any) {
92
+ if (e.code === 'P2002') throw new UniqueConstraintViolationException();
93
+ throw e;
94
+ }
95
+ }
96
+
97
+ async findById(id) { return this.prisma.invoice.findUnique({ where: { id } }); }
98
+ // ... implement the rest of IInvoiceRepository
99
+ async withTransaction(fn) {
100
+ return this.prisma.$transaction(async (tx) => {
101
+ const scoped = new PrismaInvoiceRepository({ ...this.prisma, invoice: tx.invoice } as any);
102
+ return fn(scoped);
103
+ });
104
+ }
105
+ }
106
+ ```
107
+
108
+ Repositories you must implement:
109
+
110
+ | Interface | Purpose |
111
+ |-----------|---------|
112
+ | `IInvoiceRepository` | Invoice + line item persistence |
113
+ | `IPaymentRepository` | Payment records |
114
+ | `IRefundRepository` | Refund records |
115
+ | `IPaymentConfigRepository` | Per-division payment config + providers |
116
+ | `IWebhookIdempotencyRepository` | Dedupe Stripe webhook deliveries |
117
+ | `ITransactionManager` | Multi-repo transaction coordinator |
118
+
119
+ Opt-in interfaces:
120
+
121
+ | Interface | Required when |
122
+ |-----------|---------------|
123
+ | `IPaymentPlanRepository` | `features.paymentPlans = true` |
124
+ | `IRecurringInvoiceRepository` | `features.recurringInvoices = true` |
125
+ | `IPaymentCustomerRepository` | `features.recurringInvoices = true` |
126
+
127
+ ### 2. Register the module
128
+
129
+ ```typescript
130
+ import { Module } from '@nestjs/common';
131
+ import { ConfigModule, ConfigService } from '@nestjs/config';
132
+ import { PaymentsModule } from '@ambushsoftworks/nestjs-payments-graphql';
133
+
134
+ @Module({
135
+ imports: [
136
+ ConfigModule.forRoot(),
137
+ PaymentsModule.forRootAsync({
138
+ imports: [ConfigModule, MyRepositoriesModule, MyAdaptersModule],
139
+ inject: [
140
+ PrismaInvoiceRepository,
141
+ PrismaPaymentRepository,
142
+ PrismaRefundRepository,
143
+ PrismaPaymentConfigRepository,
144
+ PrismaWebhookIdempotencyRepository,
145
+ PrismaTransactionManager,
146
+ ConfigService,
147
+ ],
148
+ useFactory: (invoices, payments, refunds, config, webhooks, tx, cfg) => ({
149
+ invoiceRepositoryInstance: invoices,
150
+ paymentRepositoryInstance: payments,
151
+ refundRepositoryInstance: refunds,
152
+ paymentConfigRepositoryInstance: config,
153
+ webhookIdempotencyRepositoryInstance: webhooks,
154
+ transactionManagerInstance: tx,
155
+
156
+ stripe: {
157
+ secretKey: cfg.get('STRIPE_SECRET_KEY'),
158
+ webhookSecret: cfg.get('STRIPE_WEBHOOK_SECRET'),
159
+ },
160
+
161
+ defaultCurrency: 'USD',
162
+ }),
163
+ }),
164
+ ],
165
+ })
166
+ export class AppModule {}
167
+ ```
168
+
169
+ ### 3. Enable `rawBody` for Stripe signature verification
170
+
171
+ Stripe's webhook signature check requires the unparsed request body.
172
+
173
+ ```typescript
174
+ // main.ts
175
+ const app = await NestFactory.create(AppModule, { rawBody: true });
176
+ ```
177
+
178
+ ### 4. Exempt the webhook path from auth
179
+
180
+ The controller is registered at `POST /webhooks/stripe` and marked with `@PaymentsWebhook()`. Your auth/tenant guards must respect this metadata. Either:
181
+
182
+ - Have guards check `Reflector.get(PAYMENTS_WEBHOOK, handler)` and bail out early, **or**
183
+ - Have guards check the route path against the exported `PAYMENTS_WEBHOOK` string constant.
184
+
185
+ See [Critical Integration Requirements](#critical-integration-requirements).
186
+
187
+ ### 5. Build a resolver
188
+
189
+ ```typescript
190
+ import { Resolver, Mutation, Args } from '@nestjs/graphql';
191
+ import {
192
+ InvoiceService,
193
+ InvoiceModel,
194
+ CreateInvoiceInput,
195
+ } from '@ambushsoftworks/nestjs-payments-graphql';
196
+
197
+ @Resolver()
198
+ export class InvoiceResolver {
199
+ constructor(private readonly invoices: InvoiceService) {}
200
+
201
+ @Mutation(() => InvoiceModel)
202
+ async createInvoice(
203
+ @Args('input') input: CreateInvoiceInput,
204
+ // resolve divisionId + userId from your auth/tenant context
205
+ ) {
206
+ return this.invoices.createInvoice(divisionId, input, userId);
207
+ }
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Architecture
214
+
215
+ ```
216
+ ┌──────────────────────────────────────────────────────────────┐
217
+ │ Consumer App (e.g. Ariadne API) │
218
+ │ ────────────────────────────── │
219
+ │ • Prisma repositories (implements IInvoiceRepository, ...) │
220
+ │ • Resolvers (GraphQL queries/mutations) │
221
+ │ • Schedulers (@nestjs/schedule) │
222
+ │ • Auth guards (respect @PaymentsWebhook metadata) │
223
+ │ • Event listener (implements IPaymentEventListener) │
224
+ │ • Email adapters (IPaymentEmailSender, IPaymentClient...) │
225
+ │ │
226
+ │ Wires together via PaymentsModule.forRootAsync({ ... }) │
227
+ └──────────────────────────────────────────────────────────────┘
228
+ ↓ imports
229
+ ┌──────────────────────────────────────────────────────────────┐
230
+ │ @ambushsoftworks/nestjs-payments-graphql │
231
+ │ ───────────────────────────────────────── │
232
+ │ • Services (pure business logic) │
233
+ │ • Interfaces (what consumer must implement) │
234
+ │ • DI tokens (Symbols) │
235
+ │ • GraphQL models + input types │
236
+ │ • StripeGateway + StripeWebhookController (opt-in) │
237
+ │ • Exceptions │
238
+ └──────────────────────────────────────────────────────────────┘
239
+ ```
240
+
241
+ The dynamic module is registered as `global: true` — any module in your app can inject services and tokens without re-importing.
242
+
243
+ ---
244
+
245
+ ## Features
246
+
247
+ ### Invoicing
248
+
249
+ - Create DRAFT invoices with optional line items.
250
+ - Line item edits allowed only in DRAFT; totals auto-recalculate on mutation.
251
+ - `InvoiceNumberService` generates globally-unique numbers against a per-division prefix, retrying on unique-constraint collisions up to 5 times.
252
+ - Tax rate resolution: use the value passed on `createInvoice` if provided, otherwise `PaymentConfig.defaultTaxRate` for the division, otherwise `0`.
253
+ - Status transitions: `DRAFT → SENT → {PARTIALLY_PAID, PAID, OVERDUE, VOID, REFUNDED}`.
254
+ - Invoice `metadata` is a Stripe-style `Record<string, string>` for consumer-specific context.
255
+
256
+ ### Stripe Gateway & Webhooks
257
+
258
+ When `stripe` config is provided, the module registers `StripeGateway` and mounts `POST /webhooks/stripe`. Webhook security relies entirely on Stripe signature verification. Behaviour:
259
+
260
+ - **Idempotency** — every event ID is recorded via `IWebhookIdempotencyRepository` before the handler runs; replays no-op.
261
+ - **Retries** — `payment_intent.succeeded` events retry up to 3 times with a 2-second delay if the corresponding `Payment` row has not yet been created.
262
+ - **Unrecognized event types** — Stripe fires many events per checkout (`payment_intent.created`, `charge.updated`, `charge.succeeded`, …) that the package does not route. These return 200 with a `debug`-level log and Stripe does not retry. The gateway surfaces them as an `IgnoredWebhookEvent` (`{ type: 'ignored', eventId, eventType }`) — if you implement a custom `PaymentGateway`, your `handleWebhook` must return this sentinel for unrouted events instead of throwing.
263
+ - **Error reporting** — configure `webhook.errorReporter` to pipe exceptions into Sentry/GlitchTip/etc.
264
+
265
+ > **Payable-status requirement.** `PaymentService.handlePaymentSucceeded` only applies an incoming payment if the invoice is in `SENT`, `PARTIALLY_PAID`, or `OVERDUE`. If a consumer creates a DRAFT invoice and starts checkout without first calling `InvoiceService.sendInvoice` to transition it to SENT, the SUCCEEDED payment lands in the `Payment` table but the invoice stays in DRAFT with `amountPaid=0`. As of v0.1.3 this fires a WARN log identifying the payment, provider, invoice, and status; call `sendInvoice` before `createPaymentSession` to avoid the case.
266
+
267
+ `GatewayRegistryService` keeps a runtime lookup of registered gateways so the package can work without `@Optional() @Inject()`.
268
+
269
+ ### Refunds
270
+
271
+ Full and partial refunds through Stripe (when the originating payment went through Stripe) or manual refund records. All amounts in cents.
272
+
273
+ ### Payment Plans
274
+
275
+ Split an invoice into installments with per-installment due dates.
276
+
277
+ Enable:
278
+
279
+ ```typescript
280
+ features: { paymentPlans: true },
281
+ paymentPlanRepositoryInstance: paymentPlanRepo,
282
+ ```
283
+
284
+ Each installment can optionally reference a generated invoice; the service tracks `PENDING → INVOICED → PAID → OVERDUE → WAIVED → CANCELLED`.
285
+
286
+ ### Recurring Invoices
287
+
288
+ Auto-generates invoices on a recurring schedule. Supports `DAYS` and `MONTHS` interval units anchored to an original date (handles month-end correctly via `computeNextInvoiceDate`).
289
+
290
+ Enable:
291
+
292
+ ```typescript
293
+ features: { recurringInvoices: true },
294
+ recurringInvoiceRepositoryInstance: recurringRepo,
295
+ paymentCustomerRepositoryInstance: paymentCustomerRepo,
296
+ ```
297
+
298
+ Phase 2 auto-charge: when `autoCharge: true`, the scheduler pulls the stored `PaymentCustomer` for the client, issues a charge via the gateway, and fires either `onRecurringInvoiceGenerated` or `onRecurringPaymentFailed`. Consecutive failures can auto-pause the schedule (`onRecurringInvoicePaused`).
299
+
300
+ Utility helpers exported:
301
+
302
+ ```typescript
303
+ import {
304
+ computeNextInvoiceDate,
305
+ computeCycleCount,
306
+ } from '@ambushsoftworks/nestjs-payments-graphql';
307
+ ```
308
+
309
+ The consumer provides the scheduler (typically `@nestjs/schedule`) that calls `RecurringInvoiceService.generateDue()` and friends on a cron.
310
+
311
+ ### E-Transfer
312
+
313
+ Canadian Interac e-transfer flow: `ETransferService` generates human-readable codes (`<prefix>-<invoice>-<random>`) and security answers, then exposes GraphQL instructions for the client.
314
+
315
+ Enable:
316
+
317
+ ```typescript
318
+ features: { eTransfer: true },
319
+ eTransferCodePrefix: 'ARD-', // whatever short prefix makes sense for your brand
320
+ ```
321
+
322
+ ### Email Notifications
323
+
324
+ Composable: transport + rendering + branding + client lookup.
325
+
326
+ ```typescript
327
+ import {
328
+ PaymentsModule,
329
+ StaticBrandingResolver,
330
+ DefaultPaymentEmailTemplateRenderer,
331
+ } from '@ambushsoftworks/nestjs-payments-graphql';
332
+
333
+ PaymentsModule.forRootAsync({
334
+ useFactory: (..., emailSender, clientResolver) => ({
335
+ // ... required deps
336
+ features: { emailNotifications: true },
337
+ email: {
338
+ senderInstance: emailSender,
339
+ clientResolverInstance: clientResolver,
340
+ brandingResolverInstance: new StaticBrandingResolver({
341
+ appName: 'My App',
342
+ primaryColor: '#1976D2',
343
+ fromEmail: 'billing@example.com',
344
+ fromName: 'My App Billing',
345
+ companyName: 'My App Inc.',
346
+ supportEmail: 'support@example.com',
347
+ }),
348
+ templateRendererInstance: new DefaultPaymentEmailTemplateRenderer(),
349
+ },
350
+ }),
351
+ });
352
+ ```
353
+
354
+ You implement:
355
+
356
+ - **`IPaymentEmailSender`** — a `send()` method that posts to your transport (Resend, SendGrid, SES, etc.).
357
+ - **`IPaymentClientResolver`** — returns `{ email, name } | null` for a `clientDetailsId`. Returning `null` skips that email silently.
358
+
359
+ You can override:
360
+
361
+ - **`IPaymentEmailBrandingResolver`** — per-division branding. Use `StaticBrandingResolver` for single-tenant apps.
362
+ - **`IPaymentEmailTemplateRenderer`** — full HTML control (e.g. React Email, MJML, Handlebars). `DefaultPaymentEmailTemplateRenderer` ships inline-styled responsive templates.
363
+
364
+ Covered emails: invoice sent, e-transfer instructions, payment confirmation, overdue reminder, refund confirmation.
365
+
366
+ ### Event Listeners
367
+
368
+ Register a single `IPaymentEventListener` to react to domain events without touching services. All handlers except `onInvoicePaid` are optional.
369
+
370
+ ```typescript
371
+ paymentEventListenerInstance: {
372
+ async onInvoicePaid(event) { /* fulfill the order, confirm the booking, ... */ },
373
+ async onInvoiceRefunded(event) { /* undo fulfillment */ },
374
+ async onRecurringInvoiceGenerated(event) { /* notify, audit */ },
375
+ async onRecurringPaymentFailed(event) { /* alert, retry */ },
376
+ async onRecurringInvoicePaused(event) { /* notify billing team */ },
377
+ }
378
+ ```
379
+
380
+ Event payloads include `metadata` from the invoice plus the full `lineItems` snapshot so consumers don't need to re-query.
381
+
382
+ ### Transactions
383
+
384
+ `ITransactionManager.runInTransaction(fn)` gives the callback a `TransactionRepositories` bag with all repositories bound to the same ORM transaction. The package uses this internally when recording payments and mutating totals.
385
+
386
+ ```typescript
387
+ await transactionManager.runInTransaction(async (repos) => {
388
+ const invoice = await repos.invoices.findByIdForUpdate(id);
389
+ await repos.payments.create({ ... });
390
+ await repos.invoices.update(id, { amountPaid: invoice.amountPaid + amount });
391
+ });
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Configuration Reference
397
+
398
+ ### Required Options
399
+
400
+ | Option | Type | Description |
401
+ |--------|------|-------------|
402
+ | `invoiceRepositoryInstance` | `IInvoiceRepository` | Invoice + line item persistence |
403
+ | `paymentRepositoryInstance` | `IPaymentRepository` | Payment records |
404
+ | `refundRepositoryInstance` | `IRefundRepository` | Refund records |
405
+ | `paymentConfigRepositoryInstance` | `IPaymentConfigRepository` | Per-division payment config |
406
+ | `webhookIdempotencyRepositoryInstance` | `IWebhookIdempotencyRepository` | Stripe webhook dedupe |
407
+ | `transactionManagerInstance` | `ITransactionManager` | Multi-repo transactions |
408
+
409
+ ### Optional Repositories
410
+
411
+ | Option | Required when |
412
+ |--------|---------------|
413
+ | `paymentPlanRepositoryInstance` | `features.paymentPlans` is enabled |
414
+ | `recurringInvoiceRepositoryInstance` | `features.recurringInvoices` is enabled |
415
+ | `paymentCustomerRepositoryInstance` | `features.recurringInvoices` is enabled |
416
+
417
+ ### Stripe Options
418
+
419
+ ```typescript
420
+ stripe: {
421
+ secretKey: string;
422
+ webhookSecret: string;
423
+ }
424
+ ```
425
+
426
+ Omit the entire `stripe` key for non-Stripe deployments. A warning logs at startup if no gateway is configured.
427
+
428
+ ### Email Options
429
+
430
+ ```typescript
431
+ email: {
432
+ senderInstance: IPaymentEmailSender; // required
433
+ clientResolverInstance: IPaymentClientResolver; // required
434
+ brandingResolverInstance?: IPaymentEmailBrandingResolver; // optional
435
+ templateRendererInstance?: IPaymentEmailTemplateRenderer; // optional
436
+ }
437
+ ```
438
+
439
+ Required when `features.emailNotifications` is enabled.
440
+
441
+ ### Defaults & Feature Flags
442
+
443
+ | Option | Type | Default | Description |
444
+ |--------|------|---------|-------------|
445
+ | `defaultCurrency` | `string` | `'USD'` | ISO 4217 currency used when an invoice omits one |
446
+ | `eTransferCodePrefix` | `string` | `''` | Short prefix prepended to generated e-transfer codes |
447
+ | `webhook.errorReporter` | `(err, ctx) => void` | — | Forward webhook errors to Sentry/GlitchTip/etc. |
448
+
449
+ | Flag | Default | Description |
450
+ |------|---------|-------------|
451
+ | `features.paymentPlans` | `false` | Enable payment-plan services + GraphQL types |
452
+ | `features.recurringInvoices` | `false` | Enable recurring invoice services + auto-charge |
453
+ | `features.eTransfer` | `false` | Enable e-transfer code generation and instruction emails |
454
+ | `features.emailNotifications` | `false` | Enable outbound email; requires `email` config |
455
+
456
+ Validation runs when the module boots. Enabling a feature without providing the backing repository or config throws at startup.
457
+
458
+ ---
459
+
460
+ ## DI Tokens
461
+
462
+ All DI tokens are exported from the package as `Symbol`s. Use them with `ModuleRef.get()` (see [Critical Integration Requirements](#critical-integration-requirements)).
463
+
464
+ ```typescript
465
+ import {
466
+ INVOICE_REPOSITORY,
467
+ PAYMENT_REPOSITORY,
468
+ REFUND_REPOSITORY,
469
+ PAYMENT_PLAN_REPOSITORY,
470
+ PAYMENT_CONFIG_REPOSITORY,
471
+ WEBHOOK_IDEMPOTENCY_REPOSITORY,
472
+ RECURRING_INVOICE_REPOSITORY,
473
+ PAYMENT_CUSTOMER_REPOSITORY,
474
+ TRANSACTION_MANAGER,
475
+ PAYMENT_EVENT_LISTENER,
476
+ PAYMENT_EMAIL_SENDER,
477
+ PAYMENT_CLIENT_RESOLVER,
478
+ PAYMENT_EMAIL_BRANDING_RESOLVER,
479
+ PAYMENT_EMAIL_TEMPLATE_RENDERER,
480
+ WEBHOOK_ERROR_REPORTER,
481
+ DEFAULT_CURRENCY,
482
+ E_TRANSFER_CODE_PREFIX,
483
+ } from '@ambushsoftworks/nestjs-payments-graphql';
484
+ ```
485
+
486
+ ---
487
+
488
+ ## Reserved GraphQL Type Names
489
+
490
+ The package registers these names on module import. Consumers **must not** redefine or re-register them, or GraphQL will throw `"type already registered"`.
491
+
492
+ **Object types:** `Invoice`, `InvoiceLineItem`, `InvoiceClient`, `Payment`, `Refund`, `PaymentPlan`, `PaymentPlanInstallment`, `PaymentConfig`, `PaymentProvider`, `PaymentMethodSetup`, `ETransferInstructions`, `OnlinePaymentSession`, `RecurringInvoice`, `RecurringInvoiceLineItem`, `PaginatedInvoices`, `PaginatedPaymentPlans`, `PaginatedRecurringInvoices`
493
+
494
+ **Enums:** `InvoiceStatus`, `PaymentMethod`, `PaymentStatus`, `RefundStatus`, `PaymentPlanStatus`, `InstallmentStatus`, `RecurringIntervalUnit`, `RecurringInvoiceStatus`
495
+
496
+ **Input types:** `CreateInvoiceInput`, `CreateLineItemInput`, `CreateRefundInput`, `CreateInstallmentInput`, `CreatePaymentPlanInput`, `CreateRecurringInvoiceInput`, `CreateRecurringLineItemInput`, `RecordManualPaymentInput`, `UpdatePaymentConfigInput`, `UpdateInvoiceMetadataInput`, `UpdateRecurringTemplateInput`, `UpsertPaymentProviderInput`, `InvoiceFilterInput`, `PaymentPlanFilterInput`, `RecurringInvoiceFilterInput`
497
+
498
+ ---
499
+
500
+ ## Exceptions
501
+
502
+ All exceptions extend `PaymentException` (a plain `Error` subclass) with a stable `.code` string so consumers can map them to GraphQL/HTTP status codes.
503
+
504
+ | Exception | `.code` |
505
+ |-----------|---------|
506
+ | `InvalidInvoiceStateException` | `INVALID_INVOICE_STATE` |
507
+ | `PaymentAmountExceededException` | `PAYMENT_AMOUNT_EXCEEDED` |
508
+ | `PaymentGatewayException` | `PAYMENT_GATEWAY_ERROR` |
509
+ | `DuplicatePaymentException` | `DUPLICATE_PAYMENT` |
510
+ | `InvoiceNotPayableException` | `INVOICE_NOT_PAYABLE` |
511
+ | `InvoiceNumberExhaustedException` | `INVOICE_NUMBER_EXHAUSTED` |
512
+ | `RefundNotAllowedException` | `REFUND_NOT_ALLOWED` |
513
+ | `InvalidPaymentStateException` | `INVALID_PAYMENT_STATE` |
514
+ | `InvalidRecurringInvoiceStateException` | `INVALID_RECURRING_INVOICE_STATE` |
515
+ | `UniqueConstraintViolationException` | `UNIQUE_CONSTRAINT_VIOLATION` |
516
+
517
+ Repository adapters are responsible for translating ORM-specific unique-constraint errors (e.g. Prisma `P2002`) into `UniqueConstraintViolationException` so core services stay ORM-agnostic.
518
+
519
+ ---
520
+
521
+ ## Critical Integration Requirements
522
+
523
+ ### 1. Use `ModuleRef.get()` for package tokens
524
+
525
+ There is a known NestJS bug where `@Optional() @Inject(TOKEN)` combined with `forwardRef()` module imports delivers `null` even when the provider exists. The package itself works around this internally. **Consumer code that needs a package token must do the same:**
526
+
527
+ ```typescript
528
+ // BAD — will fail with "Nest can't resolve dependencies"
529
+ constructor(@Inject(WEBHOOK_IDEMPOTENCY_REPOSITORY) private readonly repo: IWebhookIdempotencyRepository) {}
530
+
531
+ // GOOD — resolve lazily in onModuleInit
532
+ constructor(private readonly moduleRef: ModuleRef) {}
533
+ onModuleInit() {
534
+ this.repo = this.moduleRef.get(WEBHOOK_IDEMPOTENCY_REPOSITORY, { strict: false });
535
+ }
536
+ ```
537
+
538
+ ### 2. Do not re-register GraphQL enums
539
+
540
+ `PaymentsModule` imports `register-payment-enums.ts` as a side effect, which calls `registerEnumType()` for every enum listed under [Reserved GraphQL Type Names](#reserved-graphql-type-names). Remove any duplicate `registerEnumType()` calls from your app.
541
+
542
+ ### 3. Exempt `/webhooks/stripe` from auth
543
+
544
+ The webhook controller mounts at a fixed path and is decorated with `@PaymentsWebhook()`. Your auth/tenant guards must either:
545
+
546
+ - Check for the `PAYMENTS_WEBHOOK` metadata key via `Reflector`, **or**
547
+ - Skip the route by path (use the exported `PAYMENTS_WEBHOOK` constant if you match on string keys).
548
+
549
+ `@PaymentsWebhook()` also sets `isPublic` and `skipTenant` metadata, so guards that honour those keys work out of the box.
550
+
551
+ ### 4. Enable `rawBody` in `NestFactory.create()`
552
+
553
+ Stripe signature verification requires the unparsed request body:
554
+
555
+ ```typescript
556
+ const app = await NestFactory.create(AppModule, { rawBody: true });
557
+ ```
558
+
559
+ ### 5. Translate ORM errors to `UniqueConstraintViolationException`
560
+
561
+ Repository adapters must catch ORM-specific unique constraint errors and rethrow as `UniqueConstraintViolationException` so core services (especially `InvoiceNumberService`) can apply their retry logic.
562
+
563
+ ### 6. Use `"moduleResolution": "nodenext"` friendly imports
564
+
565
+ The `package.json` has an explicit `exports` field, so `nodenext` resolution is supported. If you use `node16`/`nodenext`, you do not need to opt into any special import syntax.
566
+
567
+ ---
568
+
569
+ ## Local Development
570
+
571
+ ### Developing the package alongside a consumer
572
+
573
+ Terminal 1 — package watch mode:
574
+
575
+ ```bash
576
+ cd nestjs-payments-graphql
577
+ pnpm link --global
578
+ pnpm run build:watch
579
+ ```
580
+
581
+ Terminal 2 — consumer:
582
+
583
+ ```bash
584
+ cd my-app
585
+ pnpm link --global @ambushsoftworks/nestjs-payments-graphql
586
+ pnpm start:dev
587
+ ```
588
+
589
+ Before committing or deploying the consumer:
590
+
591
+ ```bash
592
+ cd my-app
593
+ pnpm uninstall @ambushsoftworks/nestjs-payments-graphql
594
+ pnpm install
595
+ ```
596
+
597
+ **Do not** commit `package.json` or `pnpm-lock.yaml` with a `link:` or `workspace:` reference to this package. BuildKit-based deploys (e.g. Railway) cannot resolve those references.
598
+
599
+ ### Publishing
600
+
601
+ ```bash
602
+ npm version patch # or minor/major
603
+ git push --follow-tags
604
+ ```
605
+
606
+ GitLab CI publishes to npm via OIDC provenance on tag push. See `.gitlab-ci.yml` for the pipeline.
607
+
608
+ ---
609
+
610
+ ## License
611
+
612
+ MIT — see [LICENSE](./LICENSE).
@@ -35,7 +35,7 @@ export interface PaymentGateway {
35
35
  handleWebhook(params: {
36
36
  headers: Record<string, string>;
37
37
  body: Buffer;
38
- }): Promise<PaymentWebhookEvent>;
38
+ }): Promise<NormalizedWebhookEvent>;
39
39
  }
40
40
  export type PaymentWebhookEventType = 'payment.processing' | 'payment.succeeded' | 'payment.failed' | 'payment.cancelled' | 'refund.succeeded' | 'refund.failed' | 'setup.succeeded';
41
41
  export interface PaymentWebhookEvent {
@@ -48,3 +48,9 @@ export interface PaymentWebhookEvent {
48
48
  receiptUrl?: string;
49
49
  providerData: Record<string, unknown>;
50
50
  }
51
+ export interface IgnoredWebhookEvent {
52
+ type: 'ignored';
53
+ eventId: string;
54
+ eventType: string;
55
+ }
56
+ export type NormalizedWebhookEvent = PaymentWebhookEvent | IgnoredWebhookEvent;
@@ -73,6 +73,10 @@ let StripeWebhookController = StripeWebhookController_1 = class StripeWebhookCon
73
73
  this.logger.error(`Stripe webhook signature verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
74
74
  throw new common_1.BadRequestException('Webhook signature verification failed');
75
75
  }
76
+ if (event.type === 'ignored') {
77
+ this.logger.debug(`Ignoring Stripe webhook event ${event.eventId} (type=${event.eventType})`);
78
+ return { received: true };
79
+ }
76
80
  try {
77
81
  await this.routeEvent(event);
78
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stripe-webhook.controller.js","sourceRoot":"","sources":["../../../src/gateways/stripe/stripe-webhook.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stripe-webhook.controller.js","sourceRoot":"","sources":["../../../src/gateways/stripe/stripe-webhook.controller.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { PaymentGateway, PaymentWebhookEvent } from '../payment-gateway.interface';
1
+ import type { NormalizedWebhookEvent, PaymentGateway } from '../payment-gateway.interface';
2
2
  import type { RecurringPaymentGateway } from '../recurring-payment-gateway.interface';
3
3
  export declare class StripeGateway implements PaymentGateway, RecurringPaymentGateway {
4
4
  readonly providerType = "stripe";
@@ -43,7 +43,7 @@ export declare class StripeGateway implements PaymentGateway, RecurringPaymentGa
43
43
  handleWebhook(params: {
44
44
  headers: Record<string, string>;
45
45
  body: Buffer;
46
- }): Promise<PaymentWebhookEvent>;
46
+ }): Promise<NormalizedWebhookEvent>;
47
47
  createOrRetrieveCustomer(params: {
48
48
  email: string;
49
49
  name?: string;