@gj-kit/toss-payments
토스페이먼츠 연동에서 검증 단계를 빠뜨리면 런타임이 아니라 컴파일에서 막힙니다.
- npm
0.6.2 - 런타임 의존성 0
- unit 테스트 550개 이상
- @ts-expect-error 컴파일 차단 144건
- 토스 에러 코드 42종 분류
계속 학습하기
섹션 제목: “계속 학습하기”왜 필요한가
섹션 제목: “왜 필요한가”토스 연동 사고는 대부분 프로덕션에서야 드러납니다. 저장해 둔 금액과 대조하지 않고 confirm을 호출하거나, 멱등키 없이 cron이 billing approve를 두 번 돌려 이중 과금이 나거나, 서명 없는 PAYMENT_STATUS_CHANGED payload를 그대로 믿고 주문을 이행하거나, 가상계좌 secret 저장을 빠뜨려 입금 webhook이 전부 unknown-order로 거부되는 식입니다. transport 실패로 끝난 confirm이 결제 실패가 아니라는 점도 마찬가지입니다. 뭉뚱그려 실패로 처리하면 돈은 이미 빠져나간 상태에서 고객에게는 실패했다고 안내하게 됩니다.
무엇으로 막는가
섹션 제목: “무엇으로 막는가”- 배선한 flow만 타입에 존재 — createTossPayments가 돌려주는 타입에는 BillingKeyStore를 넘기지 않으면 billing 프로퍼티가, OrderStore를 넘기지 않으면 confirm 프로퍼티가 아예 없습니다.
- 브라우저에 닿지 못하는 secret key — 토스 key 4종이 서로 다른 brand입니다. parseApiSecretKey·parseWidgetSecretKey는 node 조건으로만 해석되는 /server entry에만 있고, loadWidgets는 WidgetClientKey만 받습니다.
- 검증을 통과해야 confirm 가능 — flow.confirm은 VerifiedCheckout만 받습니다. 이 brand는 flow.verify가 createOrder 시점에 저장한 금액과 callback의 amount를 대조하고 10분 승인 시한까지 확인해야 발급됩니다.
- 취소에는 지름길이 없음 — paymentKey로 바로 취소하는 API가 없습니다. getPayment → asCancelable → kind narrowing을 거쳐야 하고, 입금 완료 가상계좌는 refundAccount가 필수, 가상계좌가 아닌 결제는
refundAccount?: never로 아예 막힙니다. - webhook 신뢰도 3등급 분리 — verify()는 raw body만 받고, 이벤트는 signature / secret / unverified 3등급으로 나뉩니다. 토스가 빌링 승인 webhook을 보내지 않기 때문에 WebhookHandlers에는 onBillingApproved key 자체가 없습니다.
Golden path
섹션 제목: “Golden path”완료 상태: 전달한 store에 맞는 결제 흐름만 노출하는 서버 전용 payment kit을 만듭니다.
1. 설치
섹션 제목: “1. 설치”pnpm add @gj-kit/toss-payments2. 앱이 소유할 경계를 정합니다
섹션 제목: “2. 앱이 소유할 경계를 정합니다”부팅 시 API secret을 파싱하고, 승인을 활성화하기 전에 서버 소유 order store를 제공합니다.
3. 최소 연결부터 시작합니다
섹션 제목: “3. 최소 연결부터 시작합니다”먼저 아래 코드를 복사한 뒤, 위에서 언급한 앱 소유 값만 교체하세요.
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.실제로는 이렇게 걸립니다
섹션 제목: “실제로는 이렇게 걸립니다”코드 리뷰는 통과하고 프로덕션에서 터지는 두 실수, 반쪽만 배선된 kit과 멱등키 없는 billing approve. 여기서는 둘 다 컴파일 에러입니다.
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}`)),});사용할 때
섹션 제목: “사용할 때”결제 키 경계, 주문 금액 검증, 웹훅 신뢰도, 멱등 빌링 흐름을 타입으로 강제해야 할 때 사용합니다.
사용하지 않을 때
섹션 제목: “사용하지 않을 때”완전한 주문 시스템으로 취급하거나 raw secret, audit payload, 환불 정책을 범용 계층에 저장하지 마세요.
환경과 peer
섹션 제목: “환경과 peer”엔진: node >=20
| Peer | 지원 범위 | 필요한 위치 |
|---|---|---|
@tosspayments/tosspayments-sdk |
^2 |
문서화된 optional subpath에서만 |
공개 subpath
섹션 제목: “공개 subpath”| Entry | Exported symbols |
|---|---|
Root entry |
155 |
./browser |
14 |
./server |
257 |
./testing |
9 |
./webhook |
40 |
안전 경계
섹션 제목: “안전 경계”server 키 parser를 브라우저 코드에 import하거나 문서화된 검증 경로 없이 redirect/웹훅을 신뢰하지 마세요. secret과 정확한 audit body는 저장 시 암호화하세요.
API reference
섹션 제목: “API reference”전체 API reference에는 선언 파일에서 생성한 모든 공개 함수, 클래스, 타입, 상수, 오류 계약이 있습니다. 각 항목은 import path, release signature, package version을 표시합니다.
패키지 관계
섹션 제목: “패키지 관계”| 관계 | 패키지 | 함께 쓰는 이유 |
|---|---|---|
| 단독 사용 | — | 다른 GJ Kit 패키지 없이 도입할 수 있습니다. |
| 선택 조합 | @gj-kit/toss-payments-nestjs |
typed core kit을 Nest dependency injection으로 등록할 때 함께 사용합니다. |
| 선택 조합 | @gj-kit/toss-payments-postgresql |
앱 소유 payment store에 PostgreSQL 구현을 사용할 때 함께 사용합니다. |