# JavaScript SDK 결제 연동

point3 JavaScript SDK의 결제창 연동 방법을 알아봅니다. 먼저 [JavaScript SDK 설정 및 초기화](/sdk-environment)을 완료하세요.

`widgets()`로 결제 객체를 만들고, 토스페이먼츠 SDK 호환을 위한 금액을 설정한 뒤 `requestPayment()`로 결제창을 엽니다.
실제 결제 금액은 가맹점 서버에서 생성한 point3 결제 세션을 기준으로 결정됩니다.

## 결제 연동

### point3.widgets()

`widgets()` 메서드로 결제 서비스를 초기화합니다. 저장된 point3 `payerId`가 없다면 `ANONYMOUS`를 사용하세요.

```js title="HTML"
const customerKey = savedPayerId || Point3.ANONYMOUS
const widgets = point3.widgets({ customerKey })
```

```js title="패키지 (JavaScript)"
import { ANONYMOUS } from '@tosspayments/tosspayments-sdk'

const customerKey = savedPayerId || ANONYMOUS
const widgets = point3.widgets({ customerKey })
```

```ts title="패키지 (TypeScript)"
import { ANONYMOUS } from '@tosspayments/tosspayments-sdk'

const customerKey: string = savedPayerId ?? ANONYMOUS
const widgets = point3.widgets({ customerKey })
```

`payerId`는 point3가 발급하는 구매자 식별값입니다. 임의로 만들거나 형식을 바꿀 수 없습니다. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요.

<Callout type="info" title="customerKey에는 point3 payerId를 사용하세요">
  `customerKey`는 토스페이먼츠 SDK 호환을 위해 유지한 파라미터 이름입니다. 가맹점이 임의로 만든 고객 키가 아니라 point3
  결제 화면에서 받은 `payerId`를 전달하고, 저장된 `payerId`가 없을 때만 `ANONYMOUS`를 사용하세요.
</Callout>

#### 파라미터

| 이름          | 필수 여부 | 타입     | 설명                                                                 |
| ------------- | --------- | -------- | -------------------------------------------------------------------- |
| `customerKey` | 필수      | `string` | 저장된 point3 `payerId`입니다. 값이 없으면 `ANONYMOUS`를 사용합니다. |

로그인한 회원의 결제에서 문자 인증을 생략하려면 `brandpay.redirectUrl` 옵션을 함께 전달합니다. 자세한 내용은 [JavaScript SDK 위임 인증](/sdk-delegated)을 참고하세요.

#### 응답

결제 금액을 설정하고 결제창을 열 수 있는 `widgets` 객체를 반환합니다.

### widgets.setAmount()

`setAmount()` 메서드로 결제 금액을 전달합니다. `requestPayment()`보다 먼저 호출하세요.

```js title="JavaScript"
await widgets.setAmount({
  currency: 'KRW',
  value: 10000
})
```

```ts title="TypeScript"
await widgets.setAmount({
  currency: 'KRW',
  value: 10000
})
```

<Callout type="info" title="실제 결제 금액은 결제 세션이 결정합니다">
  구매자에게 청구되는 금액은 가맹점 서버가 생성한 결제 세션의 금액입니다. `setAmount()`에는 결제 세션과 같은 금액을 전달하세요.
</Callout>

#### 파라미터

| 이름       | 필수 여부 | 타입     | 설명                                                    |
| ---------- | --------- | -------- | ------------------------------------------------------- |
| `currency` | 필수      | `string` | `KRW`만 지원합니다.                                     |
| `value`    | 필수      | `number` | 결제 금액입니다. 1원 이상의 정수입니다.                  |

할인 등으로 실제 결제 금액이 바뀌었다면 변경된 주문 금액으로 새 결제 세션을 생성하세요. 브라우저의 `setAmount()` 값으로
서버가 확정한 결제 금액을 변경할 수 없습니다.

#### 응답

설정이 완료되면 별도의 값을 반환하지 않습니다.

### widgets.requestPayment()

`requestPayment()` 메서드로 전체 화면 결제창을 엽니다.

```js title="JavaScript"
await widgets.requestPayment({
  orderId: sessionId,
  orderName,
  successUrl,
  failUrl
})
```

```ts title="TypeScript"
const sessionId: string = paymentSession.id
const orderName: string = '아메리카노 외 1건'
const successUrl: string = 'https://merchant.example.com/payments/success'
const failUrl: string = 'https://merchant.example.com/payments/fail'

await widgets.requestPayment({
  orderId: sessionId,
  orderName,
  successUrl,
  failUrl
})
```

<Callout type="info" title="결제 결과 URL은 가맹점이 정합니다">
`successUrl`과 `failUrl`에는 가맹점이 사용하는 결과 페이지의 절대 URL을 전달하세요. point3가 특정 경로를 정하지
  않으며, SDK 예제의 변수도 가맹점 환경에서 직접 구성해야 합니다.
</Callout>

#### 파라미터

| 이름         | 필수 여부 | 타입     | 설명                                                                                 |
| ------------ | --------- | -------- | ------------------------------------------------------------------------------------ |
| `orderId`    | 필수      | `string` | 결제 세션 생성 API 응답의 `id`인 point3 `sessionId`입니다.                           |
| `orderName`  | 필수      | `string` | 결제 세션의 상품명과 일치하는 공백이 아닌 100자 이하의 주문 상품명입니다.            |
| `successUrl` | 필수      | `string` | 결제 승인 준비가 끝나면 이동할 가맹점의 HTTPS 절대 URL입니다.                        |
| `failUrl`    | 필수      | `string` | 결제 요청을 완료하지 못하면 이동할 가맹점의 HTTPS 절대 URL입니다.                    |

`orderId`는 토스페이먼츠 SDK 호환을 위해 유지한 파라미터 이름입니다. `orderId`에는 point3 결제
세션 생성 API가 반환한 `id`를 입력하세요.

<Callout type="caution" title="EOB 점검 시간을 안내하세요">
매일 23:30부터 다음 날 00:30까지(KST)는 점검 시간입니다. 결제 세션 생성이 거절되면 구매자에게 `error.eob.endsAt`으로 종료 시각을 안내하고, 그 이후 다시 요청하세요. 응답 예시는 [EOB 점검 시간](/operations#eob-점검-시간)을 참고하세요.
</Callout>

로컬 개발에서는 `localhost`, `127.0.0.1`, `[::1]`에 한해 HTTP 결과 URL을 사용할 수 있습니다. 운영 환경에서는 HTTPS
주소를 사용하세요.

#### 응답

결제 승인 준비가 끝나면 SDK가 `successUrl`에 다음 쿼리 파라미터를 추가해 이동합니다.

| 이름        | 설명                                                 |
| ----------- | ---------------------------------------------------- |
| `orderId`   | point3 결제 세션 생성 API가 반환한 `id`            |
| `payerId`   | 결제를 진행한 구매자의 [구매자 아이디](/payer-id)      |
| `amount`    | `setAmount()`에 전달한 값                            |

토스페이먼츠 SDK와의 호환을 위해 성공 URL에서도 `orderId`라는 이름을 사용합니다. point3 연동에서 `orderId`의 값은
`requestPayment()`에 전달한 결제 세션 `id`, 즉 point3 `sessionId`입니다. 별도의 `sessionId` 쿼리 파라미터는 제공하지
않습니다.

가맹점 서버에 저장한 현재 주문의 `sessionId`와 결과 URL의 `orderId`가 같은지 확인한 뒤 [결제 승인 API](/payment-capture)를
호출하세요. `amount`는 실제 결제 금액의 검증 기준으로 사용하지 말고, 서버에 저장한 주문과 결제 세션의 금액을 기준으로 처리하세요.

`payerId`는 다음 결제의 `customerKey`로 다시 사용할 수 있도록 가맹점 회원 정보에 저장하세요. URL에서 받은 값은 조작될 수
있으므로 결제 세션 조회 응답의 `payerId`와 대조하세요. 자세한 내용은 [구매자 아이디](/payer-id)를 참고하세요.

<Callout type="caution" title="성공 URL 이동은 최종 결제 성공이 아닙니다">
  최종 결제 결과는 가맹점 서버가 결제 승인 API 응답을 확인한 뒤 결정하세요.
</Callout>

사용자가 결제창을 닫거나 결제 요청을 완료하지 못하면 SDK가 다음 쿼리 파라미터를 추가한 `failUrl`로 이동합니다.
앱의 WebView에서도 동일하게 동작합니다. 앱에서 결과를 받는 방법은 [연동 방식 선택](/integration-methods#react-native-flutter-네이티브-앱)을 참고하세요.

| 이름      | 설명                            |
| --------- | ------------------------------- |
| `code`    | 실패 결과를 구분하는 코드       |
| `message` | 실패 사유를 설명하는 메시지     |

결제창은 SDK가 자동으로 닫습니다. 실패 결과 화면에서는 `code`에 맞는
안내를 표시하고, 결과 처리는 `message`가 아닌 `code`를 기준으로 하세요.

| 코드                    | 안내 문구 예시                                                 |
| ----------------------- | -------------------------------------------------------------- |
| `PAYMENT_WINDOW_CLOSED` | 결제창이 닫혔습니다.                                          |
| `PAYER_DEACTIVATED`     | 탈퇴가 완료되어 결제가 종료되었습니다.                        |
| `SESSION_EXPIRED`       | 결제 세션이 만료되었습니다. 결제를 다시 시도해 주세요.       |
| `INVALID_SESSION`       | 결제 세션 정보를 확인할 수 없습니다. 결제를 다시 시작하세요. |
| `PAYMENT_FAILED`        | 결제 요청이 거절되었습니다. 예: 결제 금액은 적용 수수료보다 커야 합니다. |

SDK는 가맹점이 결과 URL에 미리 추가한 쿼리 파라미터를 유지합니다. 현재 주문이나 결제 시도 식별값이 필요하면 `failUrl`에 직접 추가하세요.

`PAYMENT_FAILED`는 결제 요청이 거절됐음을 나타냅니다. 수수료 조건을 충족하지 못한 경우 `message`에 `결제 금액은 적용 수수료보다 커야 합니다.`가 전달됩니다.

## point3 SDK 오류

아래 오류는 `failUrl`로 전달되지 않으며, 오류 객체의 `code`로 확인할 수 있습니다.

| 오류 코드                     | 설명                                                                                          |
| ----------------------------- | --------------------------------------------------------------------------------------------- |
| `INVALID_CLIENT_KEY`          | `Point3()`에 전달한 `clientId`가 `client-...` 형식이 아닙니다.                        |
| `INVALID_PAYMENT_REQUEST`     | 요청값 또는 SDK 호출 순서가 올바르지 않습니다.                                                |
| `PAYMENT_REQUEST_IN_PROGRESS` | 결제나 등록이 진행 중인 상태에서 다시 요청했습니다.                                            |
