---
title: 주문 생성
description: POST /v1/invoices 로 결제받을 주문을 만들어요.
sidebar:
  order: 1
  badge: POST
---

```http
POST /v1/invoices
```

결제받을 주문을 만들어요. 등록한 계좌에 입금자명과 금액이 모두 같은 입금이 들어오면 페이싱크가 주문을 결제 완료로 바꾸고 [`invoice.paid`](/api-reference/webhooks/invoice-paid) 웹훅을 보내요. 주문을 만들면 [`invoice.created`](/api-reference/webhooks/invoice-created) 웹훅도 가요.

:::warning
`customer.name`은 고객이 이체할 때 쓸 입금자명이에요. 은행 알림의 입금자명과 한 글자라도 다르면 매칭되지 않으니, 고객에게 이 이름으로 이체하도록 안내하세요.
:::

## 요청 본문

| 필드 | 타입 | 필수 | 설명 |
| --- | --- | --- | --- |
| `bankAccountIds` | string[] | 예 | 입금받을 계좌 ID 목록이에요. 빈 배열이면 등록한 모든 계좌로 받아요. 1원 인증을 마친 내 계좌만 넣을 수 있어요. [다중 계좌 라우팅](/multi-bank-account-routing) 참고 |
| `customer.name` | string | 예 | 입금자명이에요. 1~16자, 공백 없이 입력해요. 은행이 긴 입금자명을 자르는 경우는 [이름 앞자리 매칭](/matching-assist#이름-앞자리-매칭)을 참고하세요. |
| `customer.email` | string | | 구매자 이메일이에요. 5~255자예요. [결제 완료 이메일](/matching-assist#결제-완료-이메일)을 켜면 이 주소로 메일을 보내요. |
| `customer.phoneNumber` | string | | 구매자 전화번호예요. 하이픈 없이 숫자 10~11자리로 입력해요. 예: `01012345678` |
| `amount` | integer | 예 | 금액(원)이에요. 0보다 커야 해요. 입금 금액과 같아야 매칭돼요. |
| `cashReceipt.type` | string | | 현금영수증 종류예요. `PERSONAL`(소득공제용) 또는 `CORPORATE`(지출증빙용) |
| `cashReceipt.identifier` | string | | `PERSONAL`이면 휴대폰 번호, `CORPORATE`이면 사업자등록번호 10자리예요. |
| `expireAfter` | string | | 만료까지 남은 기간이에요. 최대 365일이고, 빼면 만료되지 않아요. |
| `metadata` | object | | 주문에 함께 저장할 문자열 키와 값이에요. 최대 5쌍, 키 64자, 값 1024자까지 넣을 수 있어요. |

### `expireAfter` 형식

ISO 8601 기간 형식과 짧은 형식을 모두 받아요.

| 형식 | 예 |
| --- | --- |
| ISO 8601 | `PT30M`, `PT1H`, `P1D`, `P1DT6H20M` |
| 짧은 형식 | `30m`, `1h`, `1d`, `1d 6h 20m`, `7d` |

### 현금영수증 자동 발급

`cashReceipt`를 넣으면 주문이 결제 완료될 때 페이싱크가 현금영수증을 발급해요. 대시보드 **현금영수증** 메뉴에 사업자 정보가 등록돼 있어야 해요. 주문을 만들 때는 `identifier` 형식을 검사하지 않고 발급할 때 검사하니, 형식이 틀리면 현금영수증이 발급되지 않아요. 발급 결과는 [현금영수증 목록 조회](/api-reference/cash-receipts/list)나 대시보드에서 확인하세요.

### `metadata` 활용

쇼핑몰 주문 ID 같은 내 시스템의 값을 넣어 두면 웹훅을 받았을 때 바로 주문을 찾을 수 있어요. `CAFE24_`나 `IMWEB_`로 시작하는 키는 쇼핑몰 연동에서 쓰니 피하세요.

## 요청 예시

```bash cURL
curl -X POST https://api.paysync.kr/v1/invoices \
  -H "Authorization: Bearer $PAYSYNC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bankAccountIds": [],
    "customer": {
      "name": "홍길동",
      "email": "hong@example.com",
      "phoneNumber": "01012345678"
    },
    "amount": 50000,
    "cashReceipt": { "type": "PERSONAL", "identifier": "01012345678" },
    "expireAfter": "1d",
    "metadata": { "orderId": "ORDER-2026-0001" }
  }'
```

```js Node.js
const res = await fetch("https://api.paysync.kr/v1/invoices", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAYSYNC_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    bankAccountIds: [],
    customer: { name: "홍길동", email: "hong@example.com", phoneNumber: "01012345678" },
    amount: 50000,
    cashReceipt: { type: "PERSONAL", identifier: "01012345678" },
    expireAfter: "1d",
    metadata: { orderId: "ORDER-2026-0001" },
  }),
});
const { code, data } = await res.json();
```

```python Python
import os
import requests

res = requests.post(
    "https://api.paysync.kr/v1/invoices",
    headers={"Authorization": f"Bearer {os.environ['PAYSYNC_API_KEY']}"},
    json={
        "bankAccountIds": [],
        "customer": {"name": "홍길동", "email": "hong@example.com", "phoneNumber": "01012345678"},
        "amount": 50000,
        "cashReceipt": {"type": "PERSONAL", "identifier": "01012345678"},
        "expireAfter": "1d",
        "metadata": {"orderId": "ORDER-2026-0001"},
    },
)
print(res.json())
```

## 응답

`201 CREATED`와 함께 만든 [주문 객체](/api-reference/invoices/get#주문-객체)가 와요.

```json 201 CREATED
{
  "code": "CREATED",
  "data": {
    "id": "ivc_a1b2c3d4e5f6g7h8i9j0k1l2",
    "issuerId": "acc_x9y8z7w6v5u4t3s2r1q0p9o8",
    "bankAccountIds": [],
    "customer": {
      "name": "홍길동",
      "email": "hong@example.com",
      "phoneNumber": "01012345678"
    },
    "cashReceipt": {
      "type": "PERSONAL",
      "identifier": "01012345678"
    },
    "amount": 50000,
    "paid": false,
    "metadata": { "orderId": "ORDER-2026-0001" },
    "issuedAt": "2026-04-28T03:14:15.926Z",
    "expiresAt": "2026-04-29T03:14:15.926Z",
    "deletedAt": null
  }
}
```

## 오류

| HTTP | `code` | 발생 조건 |
| --- | --- | --- |
| 401 | `NOT_AUTHORIZED` | API 키가 없거나 올바르지 않아요. |
| 403 | `INSUFFICIENT_PERMISSIONS` | `bankAccountIds`에 다른 계정의 계좌가 있거나, [API 키의 접근 가능한 계좌](/api-reference#api-키-계좌-제한) 밖의 계좌가 있어요. 계좌를 제한한 키로 빈 배열을 보내도 이 코드가 와요. |
| 403 | `BANK_ACCOUNT_NOT_VERIFIED` | `bankAccountIds`에 1원 인증을 마치지 않은 계좌가 있어요. |
| 404 | `BANK_ACCOUNT_NOT_FOUND` | `bankAccountIds`에 없는 계좌 ID가 있어요. |
| 409 | `INVOICE_ALREADY_EXISTS` | 입금자명과 금액이 같고 받을 계좌가 겹치는 결제 대기 주문이 이미 있어요. [주문 충돌 규칙](/multi-bank-account-routing#주문-충돌-규칙) 참고 |
| 422 | `INVALID_CUSTOMER_NAME` | `customer.name`이 1~16자가 아니거나 공백이 있어요. |
| 422 | `INVALID_EMAIL` | `customer.email` 형식이나 길이가 맞지 않아요. |
| 422 | `INVALID_PHONE_NUMBER` | `customer.phoneNumber`가 숫자 10~11자리가 아니에요. |
| 422 | `INVALID_AMOUNT` | `amount`가 0 이하예요. |
| 422 | `INVALID_DURATION` | `expireAfter`를 기간으로 읽을 수 없어요. |
| 422 | `EXPIRE_AFTER_EXCEEDS_MAXIMUM` | `expireAfter`가 365일을 넘어요. |
| 422 | `METADATA_KEY_VALUE_PAIR_LIMIT_EXCEEDED` | `metadata`가 5쌍을 넘어요. |
| 422 | `METADATA_KEY_LENGTH_LIMIT_EXCEEDED` | `metadata` 키가 64자를 넘어요. |
| 422 | `METADATA_VALUE_LENGTH_LIMIT_EXCEEDED` | `metadata` 값이 1024자를 넘어요. |
