<!-- Source: https://docs.infraio.xyz/ko/concepts/orders -->
<!-- Last updated: 2026-10-04 -->

# 주문

[CheckoutSession](https://docs.infraio.xyz/ko/concepts/sessions)이 구매자가 보는 것이라면, **Order**는
가맹점이 신경 쓰는 대상입니다. 다음의 영구 기록입니다.

- 구매되는 항목(라인 아이템)
- 청구된 금액과 실제로 결제된 금액
- 미해결 및 적용된 환불
- 외부 참조(`external_ref`) — 일반적으로 가맹점의 자체 주문 ID로,
  Order에 저장되며 `GET /b2b/v1/orders/{id}`에서 반환됩니다(웹훅
  페이로드에는 노출되지 않음 — 아래 참조)

주문에 라인 아이템이 꼭 필요하지는 않습니다. `items` 대신 `amount`를 보내면 금액만 청구할 수 있습니다(인보이스, 보증금, 임의 금액 결제 링크 등). 둘 중 하나만 보내세요.

CheckoutSession은 자체 PaymentIntent 중 하나가 정산되거나 TTL이 만료되면
사라집니다. Order는 영구적으로 존재합니다.

## Order가 생성되는 시점

`POST /b2b/v1/checkout-sessions/quick`을 호출하면 InfraIO Pay가 단일
트랜잭션에서 새 Order와 새 CheckoutSession을 **함께** 생성합니다. 이미
Order가 있고 체크아웃을 재시도하려는 경우(예: 구매자가 포기한 후),
`POST /b2b/v1/checkout-sessions`를 대신 사용하여 기존 주문에 새 세션을
연결하세요 — 감사 추적이 유지됩니다.

## 라이프사이클

```mermaid
stateDiagram-v2
    [*] --> PENDING: order created
    PENDING --> PAID:              full payment settled
    PENDING --> PARTIAL_PAID:      underpayment settled
    PENDING --> CANCELED:          session expired or canceled
    PARTIAL_PAID --> PAID:         merchant resolves OR remainder paid
    PAID --> PARTIALLY_REFUNDED:   partial refund executed
    PAID --> REFUNDED:             full refund executed
    PARTIALLY_REFUNDED --> REFUNDED: subsequent refund covers the rest
    PAID --> [*]
    REFUNDED --> [*]
    CANCELED --> [*]
```

| 상태 | 의미 |
| --- | --- |
| `PENDING` | CheckoutSession이 활성 상태이며 미해결입니다. |
| `PAID` | 전체 금액이 정산됨. **여기서 `payment.settled` 웹훅이 발생합니다.** 이행 처리에 안전한 상태입니다. |
| `PARTIAL_PAID` | 자금이 도착했지만 총액보다 적음. 아래 "과소 결제" 참조. |
| `CANCELED` | 세션이 만료되었거나 가맹점이 취소함. `metadata.canceled_reason`가 이유를 설명합니다(`payment_timeout`, `merchant_canceled` 등). |
| `REFUNDED` | 결제된 전체 금액이 환불됨. |
| `PARTIALLY_REFUNDED` | 일부 환불이 실행되었지만 잔액은 결제 상태로 유지됨. |

> **Note:**
>
> **이행 로직을 트리거할 상태는 CheckoutSession의 `COMPLETED`가 아닌 `PAID`입니다.**
> `payment.settled` 웹훅이 정식 신호입니다.

## `external_ref` 필드

세션 생성 시 `external_ref`(최대 255자의 임의 문자열 — 일반적으로 가맹점의
자체 주문 ID)를 포함할 수 있습니다. 전체 파이프라인을 통해 전달됩니다.

- Order에 저장됨
- 지원 조회를 위해 가맹점 대시보드에 표시됨
- `GET /b2b/v1/orders/{id}`에서 반환되므로 웹훅 핸들러가 `payment.settled`를
  수신한 후 가져올 수 있음

> **Warning:**
>
> 웹훅 페이로드에는 `external_ref`가 포함되지 **않습니다**. `payment.*`
> 웹훅을 자체 레코드와 매핑하려면 페이로드의 `order_id`를 사용하여 주문을
> 가져오세요.

## 과소 결제

구매자의 온체인 송금이 주문 총액보다 적은 금액으로 완료되면 Order는
`PARTIAL_PAID`로 이동합니다. 세 가지 옵션이 있습니다.

1. **수용 및 해결.** 주문을 `PAID`로 전환하고 `order.resolved`를
   발생시킵니다. 이는 REST API로 제공되지 않으므로, 셀프 서비스
   흐름에서는 옵션 2(부족분 수집)를 사용하세요.
2. **부족분 대기.** 동일한 Order에 대해 `amount_due` = 잔여 금액으로
   새 CheckoutSession을 생성합니다. 구매자가 차액을 결제하면 정산
   시 Order가 `PAID`로 이동합니다.
3. **취소 및 환불.** 부분 금액을 환불하고 Order를 취소합니다. 구매자가
   체인 수수료를 부담해야 합니다.

## 환불

환불은 별도의 API 표면과 별도의 개념 페이지가 있습니다.
[개념 → 환불](https://docs.infraio.xyz/ko/concepts/refunds)을 참조하세요.

## API 엔드포인트

| 메서드 | 경로 | 비고 |
| --- | --- | --- |
| `POST` | `/b2b/v1/orders` | 세션 없이 Order 생성(드뭄) |
| `GET` | `/b2b/v1/orders/:order_id` | 라인 아이템 + 결제 이력을 포함한 전체 주문 읽기 |
| `PATCH` | `/b2b/v1/orders/:order_id/cancel` | 미결제 주문 취소 |
| `PATCH` | `/b2b/v1/orders/:order_id/reopen` | 자동 취소된(`payment_timeout`) 주문 재오픈 |

## 다음 단계

- [개념 → 세션](https://docs.infraio.xyz/ko/concepts/sessions) — Order를 감싸는 구매자 영역 셸을
  다룹니다.
- [개념 → 환불](https://docs.infraio.xyz/ko/concepts/refunds) — 환불 상태와 수동 온체인 제출
  단계를 다룹니다.
- [웹훅 → 개요](https://docs.infraio.xyz/ko/webhooks/overview) — Order 라이프사이클에서 발생하는
  모든 이벤트를 다룹니다.
