Skip to content

@gj-kit/toss-payments core concepts

A confirm that skipped verification does not compile — flow.confirm takes only VerifiedCheckout.

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.

Confirmation, billing, and webhook capabilities do not appear until the server supplies their corresponding stores, making an unwired payment path impossible to call.

Redirects and webhook payloads are inputs, not payment truth. The server verifies order amount, raw-body event, and trust grade before settlement.

  • Unwired flows have no property — createTossPayments returns a type where billing does not exist unless you pass a BillingKeyStore, and confirm does 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.

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.