@gj-kit/toss-payments
A confirm that skipped verification does not compile — flow.confirm takes only VerifiedCheckout.
- npm
0.6.2 - 0 runtime dependencies
- 550+ unit tests
- 144 @ts-expect-error compile guards
- 42 Toss error codes classified
Keep learning
Section titled “Keep learning”Why this exists
Section titled “Why this exists”Toss integrations break in ways that only surface in production: confirming without comparing the amount you stored, a cron re-running billing approve without an idempotency key and charging twice, trusting an unsigned PAYMENT_STATUS_CHANGED payload, or forgetting to save the virtual-account secret so every deposit webhook is rejected as unknown-order. And a confirm that fails on transport is not a failed payment — batch-failing it tells the customer their payment failed after the money already moved.
What it does about it
Section titled “What it does about it”- Unwired flows have no property — createTossPayments returns a type where
billingdoes not exist unless you pass a BillingKeyStore, andconfirmdoes not exist unless you pass an OrderStore. - Secret keys cannot reach the browser — Toss’s four key kinds are four separate brands; the secret parsers live only on the node-resolved /server entry, and loadWidgets accepts WidgetClientKey alone.
- confirm() rejects unverified callbacks — flow.confirm takes only VerifiedCheckout — the brand flow.verify mints after the callback amount matches the amount createOrder stored and the 10-minute approval window still holds.
- Cancel has no shortcut path — Nothing cancels by paymentKey: you go getPayment to asCancelable to kind narrowing, where a deposited virtual account requires refundAccount and every non-virtual-account payment declares it
refundAccount?: never. - Webhook trust is graded, not assumed — verify() takes raw body only, events split into signature / secret / unverified, and there is no onBillingApproved key because Toss sends no such webhook.
Golden path
Section titled “Golden path”Outcome: A server-only payment kit whose available flows match the stores you pass in.
1. Install
Section titled “1. Install”pnpm add @gj-kit/toss-payments2. Keep the app-owned boundary explicit
Section titled “2. Keep the app-owned boundary explicit”Parse the API secret at boot and provide your server-owned order store before enabling confirmation.
3. Start with the smallest integration
Section titled “3. Start with the smallest integration”Copy this first, then replace only the app-owned values named above.
import { orThrow } from '@gj-kit/toss-payments';import { createTossPayments, parseApiSecretKey } from '@gj-kit/toss-payments/server';
declare const apiSecretFromEnv: string; // Read this once from your server environment.
export const toss = createTossPayments({ secretKey: orThrow(parseApiSecretKey(apiSecretFromEnv)),});
// Add your OrderStore to enable toss.confirm; the type exposes only wired flows.What that looks like
Section titled “What that looks like”Two mistakes that normally pass review and detonate in production — a half-wired kit and a billing approve with no idempotency key — are both compile errors here.
import { idempotencyKey, orThrow } from '@gj-kit/toss-payments';import type { BillingKeyStore, BillingOrder, BillingProfile, OrderStore } from '@gj-kit/toss-payments/server';import { createTossPayments, parseApiSecretKey } from '@gj-kit/toss-payments/server';
declare const apiSecret: string; // app ownsdeclare const orderStore: OrderStore; // app ownsdeclare const billingKeyStore: BillingKeyStore; // app ownsdeclare const profile: BillingProfile; // app ownsdeclare const order: BillingOrder; // app owns
export const toss = createTossPayments({ secretKey: orThrow(parseApiSecretKey(apiSecret)), orders: orderStore, // omit and `toss.confirm` is not on the type billingKeys: billingKeyStore, // omit and `toss.billing` is not on the type});
// toss.billing.approve(profile, order); -> error TS2554: Expected 3 arguments, but got 2.export const charged = toss.billing.approve(profile, order, { idempotencyKey: orThrow(idempotencyKey(`sub:2026-09:${profile.customerKey}`)),});Use it when
Section titled “Use it when”Use it when payment key boundaries, order-amount verification, webhook trust, and idempotent billing flows must be encoded in types.
Do not use it when
Section titled “Do not use it when”Do not treat it as a complete order system or store raw secrets, audit payloads, and refund policy in its generic layer.
Runtime and peers
Section titled “Runtime and peers”Engines: node >=20
| Peer | Supported range | Required for |
|---|---|---|
@tosspayments/tosspayments-sdk |
^2 |
documented optional subpaths only |
Public subpaths
Section titled “Public subpaths”| Entry | Exported symbols |
|---|---|
Root entry |
155 |
./browser |
14 |
./server |
257 |
./testing |
9 |
./webhook |
40 |
Safety boundary
Section titled “Safety boundary”Never import server key parsers into browser code or trust a redirect/webhook without the documented verification path. Keep secrets and exact audit bodies encrypted at rest.
API reference
Section titled “API reference”complete API reference contains every public function, class, type, constant, and error contract generated from release declarations. Each item shows its import path, release signature, and package version.
Package relationships
Section titled “Package relationships”| Relationship | Package | Why combine them |
|---|---|---|
| Standalone | — | This package can be adopted without another GJ Kit package. |
| Optional companion | @gj-kit/toss-payments-nestjs |
Register the typed core kit through Nest dependency injection. |
| Optional companion | @gj-kit/toss-payments-postgresql |
Use PostgreSQL implementations for application-owned payment stores. |