@gj-kit/toss-payments-nestjs core concepts
A DI token carries no type — TossPaymentsFor<typeof config> gives it back, so unwired flows stay compile errors.
Why this boundary exists
Section titled “Why this boundary exists”A DI token carries no type: forRoot() returns a DynamicModule bound to a unique symbol, so the constructor annotation you wrote is the only remaining truth — toss.billing type-checks on a kit whose config never wired a BillingKeyStore, and the property is undefined at runtime. Then you add a webhook controller and Nest has already parsed the body: signature verification cannot be recovered from re-serialized JSON.
Nest DI must preserve the core kit type
Section titled “Nest DI must preserve the core kit type”A generic DI token loses configuration-specific capabilities. The package rebuilds the typed core kit from registered configuration at the injection point.
Webhook verification needs raw bytes
Section titled “Webhook verification needs raw bytes”Once Nest JSON middleware parses a request, re-serializing cannot recover its signature input. Raw-body setup is a prerequisite.
What the package enforces
Section titled “What the package enforces”- Unwired flows have no property —
TossPaymentsFor<typeof config>rebuilds the conditional kit type after DI, sotoss.billingis a compile error without a wiredBillingKeyStore. - Missing rawBody fails loudly — Without
req.rawBody,toNestWebhookHandlernever calls a handler: it answers 500 and logs the three settings to check —rawBody: trueonNestFactory.create, Fastify raw-body support, and a JSON middleware applied ahead of the webhook route. - Source IP survives the wrapper — The handler forwards the original Node
socket, keeping the core’s fail-closed IP check; trusting a proxy header requires passing an explicitsourceIpextractor. - One token across ESM and CJS —
TOSS_PAYMENTSandgetTossPaymentsToken(name)areSymbol.forlookups, so a dual-loaded package still resolves to one provider binding. - No emitDecoratorMetadata needed —
InjectTossPayments()is a thin@Inject(token)delegate and no code undersrc/readsdesign:paramtypes, so the package shipsemitDecoratorMetadata: false— and the Nest DI tests still resolve under vitest’s esbuild transform, which cannot emit that metadata at all.
What remains application-owned
Section titled “What remains application-owned”Preserve raw request bytes for verified webhooks and make every store dependency explicit in the host Nest module.
Continue with the two verified recipes.