# Payment API

> 2328.io Payment API로 암호화폐 결제 세션을 생성하고 관리합니다.

Payment API를 사용하면 결제 세션을 생성하고, 고객을 호스팅된 결제 페이지로 리디렉션하며, 결제 상태를 추적할 수 있습니다.

## 결제 생성

결제 세션을 생성하고 고객이 결제할 수 있는 URL을 반환합니다.

### 요청 매개변수

| 필드 | 타입 | 필수 | 설명 | 값 |
|-------|------|----------|-------------|--------|
| `amount` | decimal | yes | 통화 단위의 결제 금액, 예: `100.00` |  |
| `currency` | string | yes | 법정화폐(USD, EUR, RUB 등) 또는 암호화폐(USDT, TRX, BTC 등) | `USD`, `EUR`, `RUB`, `KZT`, `UAH`, `UZS`, `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `order_id` | string | yes | 가맹점 측 주문 ID, 예: `ORDER-12345` (최대 128자) |  |
| `to_currency` | string | no | 미리 선택된 암호화폐 | `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `network` | string | no\* | 네트워크 코드 (`to_currency`가 설정된 경우 또는 `currency`가 암호화폐인 경우 필수) | `TRX-TRC20`, `ETH-ERC20`, `BASE`, `BSC-BEP20`, `AVAX-C`, `POL-MATIC`, `TON`, `SOL`, `BTC`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `url_return` | string | no | 결제 후 리디렉션할 URL, 예: `https://your-site.com/return` |  |
| `url_success` | string | no | `url_return`의 대안 |  |
| `url_callback` | string | yes | webhook 알림용 URL, 예: `https://your-site.com/webhook` |  |
| `invite_code` | string | no | 추천인 코드 |  |
| `fee_split` | decimal | no | 결제자에게 전가할 가맹점 수수료의 비율, 0–100 (%). 0 = 가맹점이 전액 부담, 100 = 결제자가 전액 부담. 프로젝트 수준 설정을 덮어씁니다. **예: `30`** (결제자가 수수료의 30%를 부담). |  |
| `price_markup` | decimal | no | 청구 금액에 적용할 마크업 또는 할인, −99 ~ 100 (%). 프로젝트 수준 설정을 덮어씁니다. **예: `5`** (+5%) 또는 `-10` (10% 할인). |  |
| `description` | string | no | 선택적 청구 설명(최대 200자). 결제 페이지에서 결제자에게 표시됩니다. **예: `Premium plan — Order #12345`**. |  |
| `ttl_seconds` | int | no | 청구 유효 기간(초). `300`(5분) ~ `86400`(24시간). 이 시간이 지나면 청구가 만료되어 결제할 수 없습니다. 기본값: `3600`(1시간). **예: `3600`**. |  |

### 응답

```json
{
  "state": 0,
  "result": {
    "uuid": "abc123-def456-...",
    "order_id": "ORDER-12345",
    "amount": "100.00",
    "currency": "USD",
    "amount_usd": "100.00",
    "exchange_rate": null,
    "url": "https://2328.io/pay/abc123-def456-...",
    "tg_deeplink": "https://t.me/my2328bot?start=pay_abc123-def456-...",
    "expires_at": "2026-01-11T21:00:00Z",
    "created_at": "2026-01-11T20:00:00Z",
    "payer_currency": "USDT",
    "payer_amount": "100.50",
    "network": "TRX-TRC20",
    "address": "TXYZabc123...",
    "payment_status": "check",
    "txid": null,
    "payment_amount": null,
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

- 결제를 완료하려면 고객을 `result.url`로 리디렉션하세요.
- `tg_deeplink` — Telegram MiniApp을 통한 결제용 Telegram bot deeplink.
- `qr` — 입금 주소의 base64 인코딩된 QR 코드(data URI). 주소가 이미 할당된 경우(즉, `network`가 `to_currency`와 함께 지정되거나 `currency`가 암호화폐일 때) 제공됩니다. 그 외에는 `null`입니다.
- `txid`, `payment_amount` — 고객이 결제하기 전까지 `null`입니다. 온체인에서 트랜잭션이 감지되면 채워집니다. 시점은 `payment_status: paid` webhook을 수신하여 확인하세요.
- `exchange_rate` — 변환이 아직 적용되지 않은 경우(예: 법정화폐 → 암호화폐 환율이 잠기지 않은 경우) `null`입니다. 결제자 통화가 선택되면 채워집니다.

> Use your project UUID and the endpoint-appropriate API key from the merchant dashboard.

#### Interactive request: `POST /v1/payment`
  - `amount` (decimal, required)
  - `currency` (enum, required): USD,EUR,RUB,KZT,UAH,UZS,USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `order_id` (string, required)
  - `to_currency` (enum): USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `network` (enum): TRX-TRC20,ETH-ERC20,BASE,BSC-BEP20,AVAX-C,POL-MATIC,TON,SOL,BTC,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `url_return` (string)
  - `url_success` (string)
  - `url_callback` (string, required)
  - `invite_code` (string)
  - `fee_split` (decimal)
  - `price_markup` (decimal)
  - `description` (string)
  - `ttl_seconds` (integer)

## 호스티드 체크아웃, H2H, 그리고 정확한 암호화폐 금액

동일한 엔드포인트가 세 가지 별개의 인보이스 형식을 지원합니다. 하나를 신중하게 선택하고 금액 의미를 섞지 마십시오.

### 결제자 선택이 가능한 호스티드 체크아웃

`amount`, `currency`, `order_id`, 그리고 `url_callback`를 보내고 `to_currency`와 `network`는 생략하십시오. 응답에는 `result.url`가 포함되며, `address`, `qr`, 그리고 때로는 결제자 필드는 결제자가 호스티드 페이지에서 방향을 선택할 때까지 `null`로 남아 있습니다.

```json
{
  "amount": "125.00",
  "currency": "EUR",
  "order_id": "ORDER-2026-1042",
  "url_callback": "https://merchant.example/webhooks/2328",
  "url_return": "https://merchant.example/orders/ORDER-2026-1042"
}
```

### 직접 주소 H2H 인보이스

`to_currency`와 `network`를 모두 전송하세요. 2328.io는 API 호출 중에 블록체인 인보이스를 생성하므로, 고객을 리디렉션하지 않고도 체크아웃 내에서 성공적인 응답을 렌더링할 수 있습니다.

```json
{
  "amount": "100.00",
  "currency": "USD",
  "to_currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1043",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

반환된 값 그대로 렌더링하세요:

- `payer_amount`와 `payer_currency` — 결제 지침;
- `network`와 `address` — 이 인보이스의 유일한 목적지;
- `qr` — 동일한 주소에 대한 데이터 URI;
- `expires_at` — 인보이스 마감 기한;
- `url` — 커스텀 체크아웃이 완료되지 못할 때 유용한 호스팅된 대체 방법.

> **DANGER:** 주소를 생성하거나 대체하지 말고, 다른 송장의 주소를 재사용하지 말며, 공개 현물 가격에서 `payer_amount`를 계산하지 마십시오. API 응답이 권위적입니다.

### 정확한 암호화폐 금액에 대한 송장

송장 자체가 암호화폐로 표시될 때 암호화폐를 `currency`에 넣으십시오:

```json
{
  "amount": "25.000000",
  "currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1044",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

요청한 암호화폐 값은 `payer_currency` / `payer_amount`에 보존됩니다. 서비스는 회계 및 환율 필드를 위해 내부적으로 USD 평가액도 유지할 수 있습니다; 정확한 암호화폐 지침을 해당 평가액으로 대체하지 마십시오. 반환된 소수 문자열을 후행 소수점 자리까지 포함하여 보존하십시오.

네트워크가 하나만 지원되는 암호화폐의 경우, 네트워크는 자동으로 선택될 수 있습니다. 결정적 통합을 위해 `network`를 명시적으로 제공하는 것이 여전히 권장됩니다. 스테이블코인과 같은 다중 네트워크 자산의 경우, 항상 이를 전송하세요.

## 멱등성과 재시도

`order_id`는 인증된 상인 프로젝트에 범위가 지정되며 생성 멱등성 키로 작용합니다. 결제가 이미 존재하면 API는 `state: 0`와 함께 해당 세션을 반환합니다.

> **WARNING:** 같은 `order_id`로 재시도하는 경우, **not**는 “이 인보이스를 업데이트합니다”를 의미합니다. 금액, 통화, 콜백, 마크업, TTL 또는 방향 필드가 변경되더라도 기존 세션이 반환되므로 무시될 수 있습니다. 첫 요청을 지속적으로 저장하고 충돌하는 재시도는 자체 애플리케이션에서 거부하세요.

추천 생성 알고리즘:

1. 로컬 결제 시도와 고유 `order_id`를 하나의 데이터베이스 트랜잭션에 삽입합니다.
2. 서명된 API 요청을 보냅니다.
3. 반환된 `uuid` 및 전체 응답을 저장합니다.
4. HTTP 결과가 손실된 경우, 동일한 요청을 재시도하거나 `order_id`로 `/v1/payment/info`를 조회합니다.
5. 상류 요청이 시간 초과되었다고 해서 두 번째 로컬 주문을 생성하지 마십시오.

## 결제 엣지 케이스

| 상황 | 올바른 처리 |
|-----------|------------------|
| `address` / `qr`는 `null`입니다. | 지불자 방향이 초기화되지 않았습니다. `url`로 리디렉션하거나 새 `order_id`로 올바르게 지정된 새로운 H2H 송장을 생성하십시오. |
| HTTP `400` 검증 오류 | 필드 수준 `errors`를 읽으십시오; 변경되지 않은 입력으로 다시 시도하지 마십시오. |
| HTTP `429` | 지터링된 지수 백오프로 다시 시도하고 동일한 `order_id`를 유지하십시오. |
| HTTP `503` / `direction_disabled` | `/v1/directions`를 새로 고치십시오; 방향을 일시적으로 숨기거나 나중에 다시 시도하십시오. |
| 클라이언트 요청 시간 초과 | 결과를 알 수 없는 것으로 처리하십시오. 다른 것을 생성하기 전에 `order_id`로 조회하십시오. |
| `underpaid_check` | 부분 이벤트를 저장하고 추가 충전이나 나중 상태를 기다리세요. 더 많은 txid가 도착해도 두 번 크레딧하지 마십시오. |
| `underpaid` | 최종 부족 지급 상태. 실제 크레딧된 금액에 대해 구성된 이행/수동 검토 정책을 적용하세요. |
| `overpaid` | 초과 자금으로 성공적인 결제. 멱등적으로 이행하고 실제 금액을 정산/환불 정책을 위해 보관하세요. |
| `aml_lock` | 자동으로 이행하거나 자금 해제하지 마세요; 컴플라이언스/지원 워크플로로 전달하세요. |
| `cancel` | 청구서가 만료되었거나 취소되었습니다. 지연된 온체인 전송이 불가능하다고 추정하지 마세요; 나중 이벤트는 지원과 함께 정산하세요. |

브라우저 반환 URL은 탐색 전용입니다. 고객은 결제하지 않고 열거나, 결제 후 닫거나, 나중에 다시 재생할 수 있습니다. 상인 주문을 완료할 수 있는 것은 검증된 API/웹훅 상태뿐입니다.

## 결제 정보

`uuid` 또는 `order_id`로 현재 결제 상태를 조회합니다.

### 요청 매개변수

| 필드 | 타입 | 필수 | 설명 | 값 |
|-------|------|----------|-------------|--------|
| `uuid` | string | yes\* | 결제 UUID (생성 시 `result.uuid`) |  |
| `order_id` | string | yes\* | 가맹점 측 주문 ID |  |

> **INFO:** `uuid` 또는 `order_id` 중 최소 하나는 반드시 지정해야 합니다.

#### Interactive request: `POST /v1/payment/info`
  - `uuid` (string)
  - `order_id` (string)

## 결제 목록

필터링과 페이지네이션을 지원하는 모든 결제 목록을 조회합니다.

### 요청 매개변수

| 필드 | 타입 | 필수 | 설명 | 값 |
|-------|------|----------|-------------|--------|
| `status` | string | no | 결제 상태로 필터링([References](/docs/references) 참고) | `pending`, `check`, `paid`, `underpaid_check`, `underpaid`, `overpaid`, `cancel` |
| `date_from` | date | no | 시작 일자 (YYYY-MM-DD), 예: `2026-01-01` |  |
| `date_to` | date | no | 종료 일자 (YYYY-MM-DD), 예: `2026-01-31` |  |
| `page` | int | no | 페이지 번호, 기본값 `1` |  |
| `per_page` | int | no | 페이지당 항목 수, 기본값 `15`, 최댓값 `5000` |  |

#### Interactive request: `POST /v1/payment/list`
  - `status` (enum): pending,check,paid,underpaid_check,underpaid,overpaid,cancel
  - `date_from` (string)
  - `date_to` (string)
  - `page` (integer)
  - `per_page` (integer)