Checkout
Checkout Session은 구매자를 결제창으로 넘기기 위한 일회성 세션이에요.
내 서버에서 세션을 만들면 결제창 url을 받고, 그 주소로 구매자를 리다이렉트해요.
세션에 담은 metadata는 결제가 끝나면 order.paid 웹훅으로 그대로 돌아와요.
세션은 발급 후 30분 동안만 유효해요.
이 시간이 지나면 상태가 expired로 바뀌고 결제창을 열 수 없어요.
세션 생성
checkout:writecurl -X POST https://api.payri.kr/v1/checkout/sessions \
-H "X-Api-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"linkId": "lnk_a1b2c3",
"customer": { "name": "김구매", "email": "buyer@example.com" },
"metadata": { "userUuid": "u_9f2c" },
"successUrl": "https://myapp.com/done",
"cancelUrl": "https://myapp.com/cart"
}'요청 본문
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
linkId | string | ✅ | 결제 링크 ID (상품 객체의 linkId) |
customer | object | — | 결제창에 미리 채울 구매자 정보 (name·email·phone) |
metadata | object | — | 값이 문자열인 키-값 쌍. 결제 완료 시 회신 |
successUrl | string | — | 결제 완료 후 돌려보낼 URL (http/https) |
cancelUrl | string | — | 구매자가 결제를 취소했을 때 돌려보낼 URL (http/https) |
세션 ID는 cs_로 시작해요.
url로 구매자를 리다이렉트하면 결제창이 열려요.
{
"id": "cs_7h2k9d",
"linkId": "lnk_a1b2c3",
"productId": "prod_88x",
"status": "open",
"orderId": null,
"successUrl": "https://myapp.com/done",
"cancelUrl": "https://myapp.com/cart",
"expiresAt": "2026-06-16T04:00:00.000Z",
"createdAt": "2026-06-16T03:30:00.000Z",
"url": "https://pay.payri.kr/checkout/lnk_a1b2c3?session=cs_7h2k9d"
}브라우저에서 세션 생성
세션 생성은 서버 없이 프런트엔드에서 직접 호출할 수도 있어요.
이때는 secret 키 대신 browser 키를 쓰세요.
/v1 경로는 CORS가 열려 있어 브라우저의 fetch로 바로 호출돼요.
const res = await fetch('https://api.payri.kr/v1/checkout/sessions', {
method: 'POST',
headers: { 'X-Api-Key': 'pk_live_xxxxxxxxxxxxxxxxxxxxxxxx', 'Content-Type': 'application/json' },
body: JSON.stringify({ linkId: 'lnk_a1b2c3', metadata: { userUuid: 'u_9f2c' } }),
})
const session = await res.json()
location.href = session.url // 결제창으로 이동browser 키는 세션 생성(checkout:write)만 할 수 있어요.
세션 조회를 포함한 나머지 API는 secret 키로 서버에서 호출해야 해요.
세션 조회
checkout:readcurl https://api.payri.kr/v1/checkout/sessions/cs_7h2k9d \
-H "X-Api-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"결제가 끝나면 status가 completed로 바뀌고 orderId가 채워져요.
조회 응답에는 생성 때 담은 metadata가 포함돼요.
{
"id": "cs_7h2k9d",
"linkId": "lnk_a1b2c3",
"productId": "prod_88x",
"status": "completed",
"orderId": "ord_3kf9a2",
"successUrl": "https://myapp.com/done",
"cancelUrl": "https://myapp.com/cart",
"expiresAt": "2026-06-16T04:00:00.000Z",
"createdAt": "2026-06-16T03:30:00.000Z",
"url": "https://pay.payri.kr/checkout/lnk_a1b2c3?session=cs_7h2k9d",
"metadata": { "userUuid": "u_9f2c" }
}주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
id | string | 세션 ID (cs_로 시작) |
linkId | string | 세션을 만든 결제 링크 ID |
productId | string | 링크가 가리키는 상품 ID |
status | enum | open · completed · expired |
orderId | string | 결제가 완료되면 연결된 주문 ID (그 전엔 null) |
expiresAt | string | 세션 만료 시각 (생성 30분 뒤, ISO 8601) |
metadata | object | 생성 때 담은 메타데이터 (조회 응답에만 포함) |
metadata 회신
metadata는 세션을 거쳐 주문으로 복사돼요.
결제가 완료되면 order.paid 웹훅 페이로드의 metadata 필드로 그대로 돌아와요.
내 시스템의 사용자·장바구니 식별자를 결제와 이어 붙일 때 쓰세요.
{
"id": "ord_3kf9a2",
"productId": "prod_88x",
"status": "paid",
"metadata": { "userUuid": "u_9f2c" }
}리다이렉트
결제가 끝나면 구매자는 successUrl로 돌아가요.
이때 페이리가 order_id와 session_id 쿼리를 붙여요.
서버에서 이 값으로 GET /v1/orders/:id를 호출해 최종 상태를 확인하세요.
https://myapp.com/done?order_id=ord_3kf9a2&session_id=cs_7h2k9d리다이렉트 쿼리만으로 지급을 확정하지 마세요.
결제 확정의 신뢰 가능한 신호는 order.paid 웹훅과
GET /v1/orders/:id 조회예요.
관련 에러
| 코드 | HTTP | 상황 |
|---|---|---|
PAYMENT_LINK_NOT_FOUND | 404 | linkId에 해당하는 결제 링크가 없음 |
FORBIDDEN | 403 | 다른 스토어의 링크이거나 키의 상품 범위 밖 |
CHECKOUT_URL_INVALID | 400 | successUrl/cancelUrl이 올바른 http/https 주소가 아님 |
CHECKOUT_SESSION_NOT_FOUND | 404 | 세션이 없거나 키 범위 밖 |
CHECKOUT_SESSION_NOT_OPEN | 409 | 이미 완료됐거나 만료된 세션 |
공통 에러 형태와 코드는 에러 코드에서 다뤄요.