← 모든 튜토리얼

튜토리얼 4 / 9

결제를 만들고 검증하세요.

백엔드에서 청구서를 만들고 검증된 결제 알림을 처리하세요.

완료 후 갖게 되는 것

재시도와 중복 방지가 있는 서버 측 결제 흐름.

1. ID와 접근 권한 준비하기

활성 스토어와 테스트한 결제 수단으로 시작하세요. 다음에서: 설정 → API 접근필요한 프로젝트로 제한된 읽기/쓰기 자격 증명을 만드세요. 토큰은 백엔드에 보관하고 브라우저 코드나 공개 저장소에 넣지 마세요.

복사하세요: 프로젝트 API ID 및 스토어 API ID 스토어의 다음 위치에서: 기본 → API ID 상자. 이 값은 UUID이며 읽기 쉬운 프로젝트 식별자나 주문 번호가 아니에요. 자체 API 호스트명을 사용하세요.

다음에서: 스토어 → IPN에서 서명 비밀값을 먼저 만든 뒤 다음을 제공하세요: ipn_url판매자 VPS에서 HTTPS 수신기에 접근할 수 있어야 해요.

2. 청구서 만들기

자리표시자를 바꾸고 백엔드에서 이 요청을 보내세요. 금액은 부동소수점 계산이 아닌 십진 문자열이에요.

curl --fail-with-body --request POST \
  'https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices' \
  --header 'Authorization: Bearer YOUR_MERCHANT_API_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: order-1042-attempt-1' \
  --data '{
  "amount": "10.00",
  "currency": "EUR",
  "order_id": "order-1042",
  "description": "Example order",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  }
}'

저장하세요: data.invoice_id 을 주문에 저장한 뒤 고객을 다음으로 리디렉션하세요: links.checkout새 결제 시도에는 고유 멱등 키를 쓰세요. 시간 초과 시 다음으로 재시도하세요: 같은 자격 증명, 키, 정확히 같은 본문 바이트.

생략하면: payment_methods 스토어의 허용 수단을 사용해요. 체인 슬러그와 티커로 청구서별 범위를 좁힐 수 있지만 허용하지 않은 자산을 켜지는 않아요. 모든 요청 필드와 응답 예시 →

3. IPN, 웹훅 또는 둘 다 선택하기

IPN 는 청구서 수명 주기를 따라요. 스토어 기본 IPN URL을 설정하거나 다음으로 재정의하세요: ipn_url 해당 청구서에 적용해요. 웹훅 은 엔드포인트가 선택한 이벤트를 구독하게 해요. 예: invoice.settled.

둘은 같은 서명 형식을 쓰지만 비밀값은 달라요IPN은 스토어 IPN 비밀값을, 각 웹훅 엔드포인트는 자체 비밀값을 사용해요. 어느 쪽도 API Bearer 토큰으로 서명하지 않아요.

둘 다 앱으로 전달하면 알림이 겹칠 수 있어요. 주문을 두 번 반영하지 마세요.

4. 알림 검증 및 저장하기

  1. JSON 파싱 전에 정확한 원시 요청 본문을 읽으세요. 다음을 검증하세요: Wholly-Signature 일치하는 비밀값과 타임스탬프·재전송 검사를 사용하세요. 공식 SDK가 검증기를 제공해요.
  2. 서명된 본문의 프로젝트, 스토어, 청구서, 이벤트 식별자를 검증하세요. 서명되지 않은 전달 헤더는 인증 근거가 아니에요.
  3. 고유한 다음 값으로 이벤트를 영속 저장하세요: event_id그런 다음 HTTP 2xx를 신속히 반환하세요. 주문은 백그라운드 워커에서 처리하세요.
  4. 요청에 담긴 임의 호스트가 아니라 설정된 API 호스트에서 현재 청구서를 조회하세요. 저장한 프로젝트, 스토어, 금액, 통화, 주문 참조를 비교하세요.

PHP · Python · JavaScript / TypeScript · 서명 규격과 수신기 예시

5. 정산 후 한 번만 이행하기

다음 방식에서는: 이벤트 기반 수신기가 처리할 항목: event_type = invoice.settled그리고 현재 다음을 검증하세요: status = settled 과 예외 정책을 확인하세요. 데이터베이스 트랜잭션·고유 주문 제약으로 한 번만 이행하세요.

이벤트 유형과 상태는 달라요

빠르게 확정되는 체인은 다음 둘을 모두 보낼 수 있어요: payment.received 및 invoice.settled 및 status = settled다른 체인에서는 payment.received가 아직 processing일 수 있어요. 둘 다 오류가 아니에요.

다음으로 이벤트 중복을 제거하세요: event_id순번만 쓰지 마세요. 다른 이벤트 유형이 같은 순번을 공유할 수 있어요. 상태 기반 SDK 처리기는 대신 청구서 리비전을 합치고 이벤트 유형과 무관하게 상태를 확인해요. 이 합치기와 이벤트 유형 필터를 함께 쓰지 마세요. 두 방식 모두 주문 수준 중복 방지가 필요해요.

확인하세요: requires_review 과 수동 해결 결과를 이행 전에 확인하세요. amount_status = paid 만으로 확인이 입증되지는 않아요. 최상위 결제 자산 필드는 정산을 요약하고 payment_info 에는 자세한 수신·견적 데이터가 담겨요. 모든 상태, 이벤트, 예외 규칙 →

6. 재시도와 복구 테스트하기

소액 결제, 중복 전달, 만료 청구서, 일시적으로 사용할 수 없는 수신기를 테스트하세요. 이벤트 재실행이 주문을 두 번 반영하면 안 돼요. 순서가 바뀐 이벤트가 더 최신 상태를 덮어쓰지 않게 처리하세요.

확인하세요: 스토어 → IPN / 웹훅 → 기록 → 상세또는 청구서 상세의 전달 영역을 확인하세요. 재전송은 기록된 이벤트를 재사용하며 새 정산이 아니에요.

처리 크레딧이 부족하면 IPN/웹훅은 멈추지만 결제는 계속돼요. API로 미완료 주문을 대사하고 복구 후 보존된 전달을 처리하세요. 브라우저 리디렉션이나 고객 스크린샷으로 주문을 이행하지 마세요.