@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/CHANGELOG.md +100 -0
- package/LICENSE +21 -0
- package/README.md +612 -0
- package/dist/gateways/payment-gateway.interface.d.ts +7 -1
- package/dist/gateways/stripe/stripe-webhook.controller.js +4 -0
- package/dist/gateways/stripe/stripe-webhook.controller.js.map +1 -1
- package/dist/gateways/stripe/stripe.gateway.d.ts +2 -2
- package/dist/gateways/stripe/stripe.gateway.js +6 -2
- package/dist/gateways/stripe/stripe.gateway.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/payment.service.js +6 -0
- package/dist/services/payment.service.js.map +1 -1
- package/package.json +41 -2
- package/prisma/payment-models.prisma +504 -0
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<
|
|
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":";;;;;;;;;;;;;;;;AAAA,2CAUwB;AACxB,uCAAyC;AAIzC,0EAAqE;AACrE,oEAAgE;AAChE,kEAA8D;AAC9D,+CAAyD;
|
|
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 {
|
|
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<
|
|
46
|
+
}): Promise<NormalizedWebhookEvent>;
|
|
47
47
|
createOrRetrieveCustomer(params: {
|
|
48
48
|
email: string;
|
|
49
49
|
name?: string;
|