@gj-kit/toss-payments core concepts
A confirm that skipped verification does not compile — flow.confirm takes only VerifiedCheckout.
Why this boundary exists
Section titled “Why this boundary 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.
Available flows reflect supplied stores
Section titled “Available flows reflect supplied stores”Confirmation, billing, and webhook capabilities do not appear until the server supplies their corresponding stores, making an unwired payment path impossible to call.
Verification precedes trust
Section titled “Verification precedes trust”Redirects and webhook payloads are inputs, not payment truth. The server verifies order amount, raw-body event, and trust grade before settlement.
What the package enforces
Section titled “What the package enforces”- 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.
What remains application-owned
Section titled “What remains application-owned”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.
Continue with the two verified recipes.