# 08. 정기 결제 적용하기

{% hint style="warning" %}
대한민국에서 2025년 8월 25일 이후 가입한 정기 결제의 경우, 사용자가 무료 체험 또는 신규 할인 기간이 종료되기 전에 동의하지 않으면 정기 결제가 해지됩니다.

* 원스토어는 프로모션 기간 종료 30일 전부터 사용자에게 이메일 및 원스토어 알림을 통해 동의를 요청합니다.
* 또한, 원스토어 정기 결제 관리 화면에서도 동의를 받으며, 개발자는 앱 내에서 [정기 결제 관리 화면 열기](https://onestore-dev.gitbook.io/dev/tools/billing/v21/subs#id-08-11)를 활용하여 고객에게 동의를 요청할 수 있습니다.
  {% endhint %}

## **개요** <a href="#id-08." id="id-08."></a>

정기 결제는 정해진 기간동안 사용자가 서비스를 이용할 수 있으며, 결제 주기에 따라 정기적으로 결제가 갱신됩니다.&#x20;

일반적으로 음악이나 영화, 혹은 게임들을 이용할 때 이와 같이 정기 결제 서비스를 사용하고 있습니다.  &#x20;

개발자는 다양한 서비스를 제공하기 위해 하나의 앱 안에서 여러가지 정기 결제 상품을 구성할 수 있고, 사용자는 여러가지 정기 결제를 이용할 수 있습니다. &#x20;

또한, 신규 사용자를 확보하기 위해 할인 가격으로 정기 결제를 제공하거나 체험을 위한 무료 기간도 제공할 수 있습니다. &#x20;

마찬가지로 기존 사용자가 이용중인 정기 결제 상품을 변경 할 수 있도록 상품을 전환 시킬 수도 있습니다.&#x20;

## **정기 결제 처리 하기**  <a href="#id-08." id="id-08."></a>

사용자가 정기 결제 상품을 구매하면, 상품을 이용하는 동안 다양한 상태 변경을 거칠 수 있습니다.&#x20;

앱은 정기 결제의 상태를 확인하여, 각 상태 변경에 대응해야 합니다.&#x20;

정기 결제의 상태는 원스토어 인앱 결제 라이브러리의 PurchaseClient.queryPurchasesAsync() 또는 서버 API getSubscriptionDetail을 사용하여 확인 할 수 있습니다.&#x20;

|      | PurchaseClient.queryPurchasesAsync() |                | getSubscriptionDetail |                                     |              |
| ---- | ------------------------------------ | -------------- | --------------------- | ----------------------------------- | ------------ |
| 상태   | 반환 여부                                | recurringState | 반환 여부                 | expiryTimeMillis                    | autoRenewing |
| 구독중  | 예                                    | 0              | 예                     | 향후 일정                               | true         |
| 해지   | 예                                    | 1              | 예                     | 향후 일정                               | false        |
| 유예   | 예                                    | 0              | 예                     | 향후 일정(유예 기간 종료)                     | true         |
| 보류   | 아니오                                  | 0              | 예                     | 종료됨(예상 만료 시간 종료 또는 유예 기간 종료(있는 경우)) | true         |
| 일시중지 | 아니오                                  | 0              | 예                     | 종료됨                                 | true         |
| 만료   | 아니오                                  | 1              | 예                     | 종료됨                                 | false        |

정기 결제 상태가 변경되면, SubscriptionNotification이 전송됩니다. 관련 내용은 PNS(Push Notification Service) 이용하기를 참고하세요.

### 정기 결제 구매  <a href="#id-08." id="id-08."></a>

사용자가 정기 결제를 구매하면 PurchaseClient.queryPurchasesAsync()에서 정기 결제 결과를 반환하고, SUBSCRIPTION\_PURCHASED인 SubscriptionNotification이 전송됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API를 쿼리하여 정기 결제 상태를 업데이트 해야 합니다. 정기 결제 리소스는 다음 예시를 참고하세요.&#x20;

```json
{
+    "acknowledgementState": 0, // 새로운 정기결제를 구매한 경우에는 acknowledge가 필요합니다.
+    "autoRenewing": true,
    "paymentState": 1,
    "lastPurchaseId": "22071114040010115614",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658106000000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657515841000,
+    "expiryTimeMillis": 1658156399000,
    "autoResumeTimeMillis": null,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

### 정기 결제 갱신 <a href="#id-08." id="id-08."></a>

정기 결제가 갱신되면 SUBSCRIPTION\_RENEWED 알림이 전송됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API를 쿼리하여 새로운 만료 일시로 정기 결제 상태를 업데이트 해야 합니다.  정기 결제 리소스는 다음 예시를 참고하세요.&#x20;

```json
{
+  "acknowledgementState": 1,
+  "autoRenewing": true,
    "paymentState": 1,
    "lastPurchaseId": "22071411443210116308",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658365200000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657766672000,
+  "expiryTimeMillis": 1658501999000,
    "autoResumeTimeMillis": null,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

월간 상품의 경우 익월 동일일에 결제가 됩니다. (3개월, 6개월 상품도 동일하게 동작합니다.)&#x20;

예를들어, 1월 15일에 정기 결제가 시작되었다면, 2월 15일에 다음 결제가 발생합니다.&#x20;

하지만 동일일이 없는 경우에는 그 달의 마지막 날이 결제 갱신일이 됩니다. &#x20;

예를들어, 1월 31일에 정기 결제가 시작되었다면, 2월 28일(혹은 29일)에 다음 결제가 발생하고, 3월 28일(혹은 29)에 그 다음 결제가 발생합니다.&#x20;

### 정기 결제 만료 <a href="#id-08." id="id-08."></a>

정기 결제가 만료되면 PurchaseClient.queryPurchasesAsync()에서 더 이상 반환되지 않으며 사용자는 정기 결제를 이용할 수 없게 됩니다.&#x20;

SUBSCRIPTION\_EXPIRED 인 SubscriptionNotification도 정기 결제가 만료될 때 전송됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API를 쿼리하여 정기 결제 상태를 업데이트 해야 합니다. 정기 결제 리소스는 다음 예시를 참고하세요.&#x20;

```json
{
    "acknowledgementState": 1,
+    "autoRenewing": false,
    "paymentState": 1,
    "lastPurchaseId": "22071213191410115875",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658192400000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657599554000,
+    "expiryTimeMillis": 1658242799000,
    "autoResumeTimeMillis": null,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

### 정기 결제 해지  <a href="#id-08." id="id-08."></a>

정기 결제는 사용자가 원스토어 앱에서 정기 결제를 직접 해지할 수 있고, 결제 수단에 문제가 생겼을 때 이를 해결하지 않는 경우 자동으로 해지 될 수 있습니다.   &#x20;

원스토어 인앱결제 API cancelRecurringPruchase 를 사용하여 정기 결제를 해지할 수도 있습니다.&#x20;

사용자가 정기 결제를 직접 해지 하더라도 현재 정기 결제 기간이 끝날 때까지는 서비스를 이용할 수 있어야 합니다. &#x20;

정기 결제가 해지 되었지만 아직 만료되지 않은 경우 PurchaseClient.queryPurchasesAsync() 에서 반환됩니다. 정기 결제가 해지되면 SUBSCRIPTION\_CANCELED 알림이 전송됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API를 쿼리하여 상태를 업데이트 해야 합니다. 쿼리 시 autoRenewing=false와 사용자가 서비스를 이용 할 수 있는 기한 expiryTimeMillis 이 반환 됩니다.&#x20;

expiryTimeMillis가 기한이 지난 과거라면 즉시 서비스가 중단되고, 기한이 미래라면 해당일시까지 서비스를 이용 할 수 있습니다. 정기 결제 리소스는 다음 예시를 참고하세요.    &#x20;

```reason
{
    "acknowledgementState": 1,
+    "autoRenewing": false,
    "paymentState": 1,
    "lastPurchaseId": "22071118111610115712",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658106000000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657515841000,
+    "expiryTimeMillis": 1658156399000,
    "autoResumeTimeMillis": null,
+    "cancelledTimeMillis": 1658156399000,
+    "cancelReason": 1,
    "promotionPrice": null,
    "priceChange": null
}

```

사용자에게 정기 결제가 해지 되었으며, 해당 정기 결제의 만료일시를 앱을 통해 알려야합니다. (예시 : 정기결제가 해지되었습니다. 2023년 6월 13일에 정기 결제가 만료됩니다. \[확인]) &#x20;

### 정기 결제 취소 <a href="#id-08." id="id-08."></a>

다양한 이유로 사용자가 정기 결제를 취소 할 수 있습니다. 정기 결제가 취소되면 즉시 서비스 이용이 차단되어야 합니다.&#x20;

이 경우 `PurchaseClient.queryPurchasesAsync()`에서 더이상 반환되지 않으며, SUBSCRIPTION\_REVOKED 알림도 전송 됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API 를 쿼리하여 정기 결제 상태를 업데이트 해야 합니다.&#x20;

정기 결제 리소스에는 autoRenewing와 정기 결제 이용 기한인 expiryTimeMillis가 포함됩니다. 정기 결제 리소스는 다음 예시를 참고하세요.&#x20;

```json
{
    "acknowledgementState": 1,
+    "autoRenewing": false,
    "paymentState": null,
    "lastPurchaseId": "22071216245010115987",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658192400000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657610690000,
+    "expiryTimeMillis": 1657610749000,
    "autoResumeTimeMillis": null,
+    "cancelledTimeMillis": 1657610749000,
+    "cancelReason": 1,
    "promotionPrice": null,
    "priceChange": null
}
```

### 정기 결제 유예  <a href="#id-08." id="id-08."></a>

사용자가 정기 결제 이용 중 결제 수단에 문제가 발생하고 문제가 해결되지 않는 경우 정기 결제가 바로 해지 되지는 않습니다.&#x20;

위와 같은 경우 (1) 결제 유예 - 유예 기간이 설정된 경우  (2) 계정 보류 순서대로 정기 결제 상태가 변경됩니다. &#x20;

결제 유예 기간 제공여부는 원스토어 개발자센터에서 설정 할 수 있습니다. &#x20;

유예 기간을 설정 했다면 사용자는 해당 기간 동안 정기 결제 콘텐츠를 이용할 수 있어야 합니다.&#x20;

앱에서 PurchaseClient.queryPurchasesAsync() 를 사용하여 정기 결제 상태를 확인한다면, PurchaseClient.queryPurchasesAsync()가 만료일 전에 정기 결제 상태를 계속 반환하므로 앱은 유예 기간을 자동으로 처리해야 합니다. &#x20;

사용자의 정기 결제 상태가 결제 유예가 되는 경우 SUBSCRIPTION\_IN\_GRACE\_PERIOD 인 알림이 전송됩니다.

이 알림을 받으면 원스토어 인앱결제 API를 쿼리하여 상태를 업데이트 해야합니다. 정기 결제 리소스에는 autoRenewing=true 와 유예가 적용되도록 미래 시점의 expiryTimeMillis 이 포함됩니다. <br>

유예 기간 내에 결제 문제를 해결하지 않으면 계정 보류 상태가 되어 사용자가 정기 결제 콘텐츠를 이용 할 수 없습니다.&#x20;

앱에서는 메시지를 표시하여 사용자가 결제 문제를 해결 할 수 있도록 해야 합니다. &#x20;

사용자가 결제 문제를 해결하는데 도움이 되도록 딥링크를 제공할 수 있습니다. &#x20;

사용자가 결제 문제를 해결하면 즉시 정기 결제가 갱신 됩니다.&#x20;

```json
{
    "acknowledgementState": 0,
+    "autoRenewing": true,
    "paymentState": 0,
    "lastPurchaseId": "22071209533410115807",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1658192400000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657587215000,
+    "expiryTimeMillis": 1658242799000,
    "autoResumeTimeMillis": null,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

### 정기 결제 계정 보류 <a href="#id-08." id="id-08."></a>

사용자가 정기 결제 이용 중 결제 수단에 문제가 발생하고 문제가 해결 되지 않는 경우 유예 기간이 종료되면 계정 보류 상태가 됩니다.&#x20;

계정 보류 기간은 최대 30일이며, 유예 기간과는 다르게 계정 보류 상태에서는 정기 결제 콘텐츠를 이용 할 수 없습니다.

또한 계정 보류 중에는 정기 결제가 PurchaseClient.queryPurchasesAsync()에 의해 반환되지 않습니다.&#x20;

사용자의 정기 결제가 계정 보류 상태가 되면  SUBSCRIPTION\_ON\_HOLD 알림이 전송됩니다.&#x20;

이 알림을 받으면 원스토어 인앱결제 API를 호출하여 정기 결제 정보를 업데이트 해야합니다. 계정 보류 상태에서는 정기 결제 리소스의 expiryTimeMillis가 과거로 설정 됩니다. <br>

보류 상태에서는 사용자가 정기 결제 콘텐츠를 이용 할 수 없고, 지정된 보류 기간이 지나면 정기 결제가 해지됩니다.&#x20;

따라서 앱에서는 메시지를 표시하여 사용자가 문제를 해결 할 수 있도록 해야합니다.&#x20;

사용자가 문제를 해결하는데 도움이 되도록 딥링크를 제공할 수 있습니다. &#x20;

사용자가 문제를 해결하여 정기 결제가 복구되면 정기 결제일은 복구된 날짜로 변경됩니다.   &#x20;

계정 보류 기간이 종료되기 전에 사용자가 문제를 해결하지 않으면 SUBSCRIPTION\_CANCELED 알림이 전송 되며, 원스토어 인앱결제 API를 쿼리하여 정기 결제 상태를 업데이트 해야합니다.&#x20;

```json
{
    "acknowledgementState": 0,
+    "autoRenewing": true,
    "paymentState": 0,
    "lastPurchaseId": "22071209361310115799",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1659315600000,
    "pauseStartTimeMillis": null,
    "pauseEndTimeMillis": null,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657586174000,
+    "expiryTimeMillis": 1658242799000,
    "autoResumeTimeMillis": null,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

### 정기 결제 일시 중지 <a href="#id-08." id="id-08."></a>

개발자는 원스토어 개발자센터에서 일시 중지 기능을 사용하도록 설정할 수 있습니다.&#x20;

일시 중지 기능을 사용하도록 설정하면, 사용자는 앱의 구독을 취소하는 대신 일정 기간동안 정기 결제를 일시 중지 할 수 있습니다.&#x20;

일시 중지는 현재 정기 결제 중인 상품의 기한이 만료된 후 시작됩니다. 일시중지 된 동안에는 사용자가 정기 결제 콘텐츠를 이용할 수 없어야 합니다.&#x20;

일시중지 기간이 끝나면 정기 결제가 다시 시작됩니다.&#x20;

사용자는 일시중지 기간이 끝나기 전에 직접 일시중지를 해제하여 정기 결제를 다시 시작할 수도 있습니다. 이 경우 일시중지를 해제하여 정기 결제를 다시 시작한 날짜가 정기 결제일이 됩니다.&#x20;

일시중지 중에는 PurchaseClient.queryPurchasesAsync()에 의해 반환되지 않습니다.  정기 결제가 재개되면 PurchaseClient.queryPurchasesAsync()에 의해 반환됩니다.&#x20;

사용자가 일시중지를 선택하면 SUBSCRIPTION\_PAUSE\_SCHEDULE\_CHANGED인 SubscriptionNotification이 전송됩니다. &#x20;

이 시점에 사용자는 정기 결제 이용기간이 남아있으며, 따라서 정기 결제 콘텐츠를 계속 이용할 수 있어야 합니다. &#x20;

정기 결제 리소스에는 autoRenewing=true, paymentState=1과 미래의 expiryTimeMillis, autoResumeTimeMillis 값이 포함됩니다.&#x20;

```json
{
    "acknowledgementState": 1,
+    "autoRenewing": true,
+    "paymentState": 1,
    "lastPurchaseId": "22071114040010115614",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1660698000000,
+    "pauseStartTimeMillis": 1660748400000,
+    "pauseEndTimeMillis": 1663340399000,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657515841000,
+    "expiryTimeMillis": 1660748399000,
+    "autoResumeTimeMillis": 1660698000000,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

일시중지가 시작되면 SUBSCRIPTION\_PAUSED 인 Subscription Notification이 전송됩니다.&#x20;

일시중지가 시작되면 정기 결제 이용기간이 만료 된 것이며, 따라서 정기 결제 콘텐츠는 이용할 수 없어야 합니다.&#x20;

정기 결제 리소스에는 autoRenewing=true, paymentState=0과 과거의 expiryTimeMillis, autoResumeTimeMillis 값이 포함됩니다.&#x20;

일시중지 기간이 끝나서 정기 결제가 자동으로 재개되거나 사용자가 일시중지를 해제하여 정기 결제를 재개하면 SUBSCRIPTION\_RENEWED 인 SubscriptionNotification이 전송됩니다.&#x20;

이 경우 [정기 결제 갱신](#id-08.-3)과 동일한 처리가 필요합니다.&#x20;

정기 결제가 재개될 때 결제에 문제가 생기면 [정기 결제 계정 보류](#id-08.-8) 상태가 발생하며, [정기 결제 계정 보류](#id-08.-8) 상태와 동일하게 처리되어야 합니다.&#x20;

```json
{
    "acknowledgementState": 1,
+    "autoRenewing": true,
+    "paymentState": 0,
    "lastPurchaseId": "22071114040010115614",
    "linkedPurchaseToken": null,
    "priceAmount": "610",
    "priceAmountMicros": 610000000,
    "nextPriceAmount": "610",
    "nextPriceAmountMicros": 610000000,
    "nextPaymentTimeMillis": 1663290000000,
+    "pauseStartTimeMillis": 1660748400000,
+    "pauseEndTimeMillis": 1663340399000,
    "priceCurrencyCode": "KRW",
    "countryCode": "KR",
    "startTimeMillis": 1657515841000,
+    "expiryTimeMillis": 1660748399000,
+    "autoResumeTimeMillis": 1663290000000,
    "cancelledTimeMillis": null,
    "cancelReason": null,
    "promotionPrice": null,
    "priceChange": null
}
```

### 상품 변경(업그레이드, 다운그레이드) <a href="#id-08." id="id-08."></a>

사용자가 정기 결제 중인 상품을 다른 정기 결제 상품으로 변경하면 기존에 사용 중인 정기 결제는 무효화되고, 새로운 정기 결제가 새 구매 토큰과 함께 생성됩니다.&#x20;

또한 정기 결제 리소스에는 기존 정기 결제를 나타내는 linkedPurchaseToken 이 포함됩니다.

linkedPurchaseToken 을 사용하면 이전 정기 결제를 조회하여 새로운 정기 결제를 동일한 계정과 연결할 수 있습니다.&#x20;

새로운 정기 결제가 생성 되었으므로 해당 결제 건에 대한 [구매 확인](https://onestore-dev.gitbook.io/dev/tools/billing/v21/sdk)이 필요합니다. 정기 결제 리소스는 다음 예시를 참고하세요. &#x20;

```json
{
  "acknowledgementState": 1,
  "autoRenewing": true,
  "paymentState": 1,
  "lastPurchaseId": "22071214572510115940",
+  "linkedPurchaseToken": "220712131914S0115875",
  "priceAmount": "600",
  "priceAmountMicros": 600000000,
  "nextPriceAmount": "600",
  "nextPriceAmountMicros": 600000000,
  "nextPaymentTimeMillis": 1660266000000,
  "pauseStartTimeMillis": null,
  "pauseEndTimeMillis": null,
  "priceCurrencyCode": "KRW",
  "countryCode": "KR",
  "startTimeMillis": 1657605449000,
  "expiryTimeMillis": 1660316399000,
  "autoResumeTimeMillis": null,
  "cancelledTimeMillis": null,
  "cancelReason": null,
  "promotionPrice": null,
  "priceChange": null
}

```

### **정기 결제 관리 화면 열기**    <a href="#id-08." id="id-08."></a>

원스토어는 사용자가 정기 결제 상품을 쉽게 관리하도록 정기 결제 관리 메뉴를 제공합니다. &#x20;

매개변수로 *SubscriptionsParams*에 *PurchaseData*를 포함해서 넣으면 구매 데이터를 확인하여 해당 정기 결제 상품의 관리 화면을 실행합니다.\
\&#xNAN;*SubscriptionParams*을 null 넣을 경우 사용자의 정기 결제 리스트 화면을 실행합니다.

다음은 정기 결제 관리 화면을 띄우는 방법을 나타내는 예제입니다.

{% tabs %}
{% tab title="kotlin" %}

```kotlin
fun launchManageSubscription(@Nullable purchaseData: PurchaseData) {
    val subscriptionParams = when (purchaseData != null) {
        true -> SubscriptionParams.newBuilder()
                    .setPurchaseData(purchaseData)
                    .build()
        else -> null
    }
    purchaseClient.launchManageSubscription(mActivity, subscriptionParams)
}
```

{% endtab %}

{% tab title="java" %}

```java
public void launchManageSubscription(@Nullable PurchaseData purchaseData) {
    SubscriptionParams subscriptionParams = null;
    if (purchaseData != null) {
        subscriptionParams = SubscriptionParams.newBuilder()
            .setPurchaseData(purchaseData)
            .build();
    }
    purchaseClient.launchManageSubscription(mActivity, subscriptionParams);
}
```

{% endtab %}
{% endtabs %}

| 규격 Uri       | onestore://common/subscription/payment/{caller\_package}?purchase\_token={purchase\_token}                                      |                            |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| 규격 parameter | <p>caller\_package</p><p>(mandatory)</p>                                                                                        | 연동 규격을 호출 할 앱의 packageName |
|              | <p>purchase\_token</p><p>(optional)</p>                                                                                         | 특정 구매 상세로 가기위한 구매 Token    |
| 상세 정보        | <ul><li>마이페이지 > 정기결제 목록 화면으로 이동한다.</li><li>purchase\_token 이 존재하는 경우 해당 구매 Token의 정기결제 상세로 이동한다. </li></ul>                     |                            |
| 지원버전         | <p>ONE store Client v7.9.0 이상 ( android:versionName=”7.9.0” android:versionCode=”70900” )</p><p>ONE store Service v7.140 이상</p> |                            |

### **정기 결제 상품 변경 하기**  <a href="#id-08." id="id-08."></a>

사용자는 정기 결제 이용 중에 좀 더 좋은 혹은 저렴한 정기 결제 상품으로 플랜을 변경하고 싶을 수 있습니다.&#x20;

사용자는 다른 정기 결제 상품을 결제하여 정기 결제 상품을 변경 할 수 있고, 개발자는 비례 배분 모드를 설정하여 정기 결제 상품 변경을 처리 할 수 있습니다.&#x20;

설정 할 수 있는 비례 배분 모드(*PurchaseFlowParams.ProrationMode)*&#xB294; 다음과 같습니다.&#x20;

| 비례 배분 모드                                | 설명                                                                                                                        |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| IMMEDIATE\_WITH\_TIME\_PRORATION        | 정기 결제 상품이 즉시 변경 되며, 가격 차이를 기반으로 정기 결제 갱신일이 조정됩니다.                                                                         |
| IMMEDIATE\_AND\_CHARGE\_PRORATED\_PRICE | <p>정기 결제 상품이 즉시 변경 되며, 정기 결제 갱신일은 동일하게 유지됩니다. 나머지 기간에 대한 가격 차이를 기반으로 요금이 청구됩니다.</p><p>(이 모드는 상품 업그레이드 시에만 적용 가능합니다.) </p> |
| IMMEDIATE\_WITHOUT\_PRORATION           | 정기 결제 상품이 즉시 변경 되며, 다음 결제일에 새로운 가격이 청구됩니다. 정기 결제 갱신일은 동일하게 유지됩니다.                                                         |
| DEFERRED                                | 기존 정기 결제 상품이 만료되면 교체가 적용되며 새로운 정기 결제 상품 요금이 청구됩니다.                                                                        |

비례 배분 예

사용자는 현재 정기 결제 상품 A를 매월 정기 결제하고 있습니다. 이 정기 결제는 2,000원 의 요금이 청구되며 매월 1일에 갱신 됩니다.

4월 15일에 사용자는 요금이 **연간 36,000원** 인 연간 정기 결제 상품 B로 상품을 변경 하기로 했습니다.<br>

#### **IMMEDIATE\_WITH\_TIME\_PRORATION**

정기 결제 상품 A가 즉시 종료됩니다. 사용자는 한 달(4월 1일\~30일) 요금을 결제 했는데 정기 결제 기간 중간에 상품을 변경 했으므로 월간 정기 결제 요금의 절반(1,000원)은 새 정기 결제에 적용됩니다. 그러나 새 정기 결제 요금은 연간 36,000원이므로 1,000원의 잔액은 10일(4월 16일\~25일)에 해당합니다. 따라서 4월 26일에 새 정기 결제의 요금으로 36,000원이 청구되며, 다음 해부터 매년 4월 26일에 36,000원이 청구됩니다.&#x20;

#### **IMMEDIATE\_AND\_CHARGE\_PRORATED\_PRICE**

시간 단위 당 정기 결제 상품 B의 가격(36,000원/연 = 3,000원/월)이 정기 결제 상품 A의 가격(2,000원/월)보다 높으므로 이 모드를 사용할 수 있습니다.  정기 결제 상품A는 즉시 종료됩니다. 사용자는 한 달 전체 요금을 결제 했는데 기간의 절반만 사용했으므로 월간 정기 결제 요금의 절반(1,000원)은 새 정기 결제에 적용됩니다. 그러나 새 정기 결제 요금은 1년에 36,000원 이므로 나머지 15일의 요금은 1,500원입니다. 따라서 새 정기 결제 요금으로 500원의 차액이 청구됩니다. 5월 1일에는 새 정기 결제 상품 B의 요금으로 36,000원이 청구되며, 다음 해부터 매년 5월 1일에 36,000원이 청구됩니다.&#x20;

#### **IMMEDIATE\_WITHOUT\_PRORATION**

정기 결제 상품A가 추가 비용 없이 정기 결제 상품B로 즉시 변경 됩니다. 그리고 5월 1일에 새 정기 결제 상품 B의 요금으로 36,000원이 청구되며, 다음 해부터 매년 5월 1일에 36,000원이 청구됩니다.

#### **DEFERRED**

정기 결제 상품A는 4월 30일에 만료될 때까지 계속됩니다. 5월 1일에 정기 결제 상품 B가 적용되며, 새 정기 결제 등급 요금으로 사용자에게 36,000원이 청구됩니다.

정기 결제는 "*구매 요청하기*"와 동일한 API를 사용하여 사용자에게 상품 변경 기능을 제공할 수 있습니다. 다만, 정기 결제의 상품 변경을 위해선 기존 정기 결제 구매 토큰과 비례 배분 모드 값이 필수로 필요합니다.

다음 예와 같이 현재 정기 결제와 변경하여 적용 될 정기 결제 및 비례 배분 모드에 관한 정보를 제공해야 합니다.

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
val subscriptionUpdateParams = SubscriptionUpdateParams.newBuilder()
      .setProrationMode(desiredProrationMode)
      .setOldPurchaseToken(oldPurchaseToken)
      .build()

val purchaseFlowParams = PurchaseFlowParams.newBuilder()
      .setProductId(newProductId)
      .setProductType(productType)
      .setProductName(productName)        // optional
      .setDeveloperPayload(devPayload)    // optional
      .setSubscriptionUpdateParams(subscriptionUdpateParams)
      .build()

purchaseClient.launchPurchaseFlow(activity, purchaseFlowParams)
```

{% endtab %}

{% tab title="Java" %}

```java
SubscriptionUpdateParams subscriptionUpdateParams = SubscriptionUpdateParams.newBuilder()
      .setProrationMode(desiredProrationMode)
      .setOldPurchaseToken(oldPurchaseToken)
      .build();

PurchaseFlowParams purchaseFlowParams = PurchaseFlowParams.newBuilder()
      .setProductId(newProductId)
      .setProductType(productType)
      .setProductName(productName)        // optional 
      .setDeveloperPayload(devPayload)    // optional
      .setSubscriptionUpdateParams(subscriptionUdpateParams)
      .build();

purchaseClient.launchPurchaseFlow(activity, purchaseFlowParams);
```

{% endtab %}
{% endtabs %}

즉시 변경이 이루어지는 비례 배분 모드의 경우 [구매 요청하기](https://onestore-dev.gitbook.io/dev/tools/billing/v21/sdk) 로직을 수행하기 때문에 응답은 *PurchasesUpdatedListener*에서 수신합니다. 또한 [구매내역 조회하기](https://onestore-dev.gitbook.io/dev/tools/billing/v21/sdk)에서도 요청 시 응답을 받을 수 있습니다.

&#x20;*PurchaseClient.acknowledgeAsync()또는, acknowledgePurchase를* 사용하여 [구매 확인](https://onestore-dev.gitbook.io/dev/tools/billing/v21/sdk)처리를 해야 합니다.

원스토어 인앱결제 API는 정기 결제 리소스에서 linkedPurchaseToken 을 반환합니다. linkedPurchaseToken 을 무효화하여 콘텐츠 이용 권한을 얻는 데 이전 토큰이 사용되지 않도록 해야 합니다.&#x20;

지연 변경이 이루어지는 비례 배분 모드의 경우 정기 결제 구매 및 변경 여부와 함께 *PurchasesUpdatedListener*  호출을 수신합니다.&#x20;

변경이 적용될 때까지 PurchaseClient.queryPurchasesAsync() 는 기존 정기 결제 상품 정보의 구매 정보를 계속 반환합니다.

변경이 적용되면 PurchaseClient.queryPurchasesAsync() 는 새로운 정기 결제 상품의 구매 정보를 반환하고, SUBSCRIPTION\_RENEWED 인 Subscription Notification 이 전송됩니다.

지연 변경이 이루어지는 경우 이 알림을 수신하여 *acknowledgePurchase* 을 사용하여 구매확인 처리를 하는 것이 좋습니다.&#x20;

**프로모션이 있는 상품으로 변경하기**&#x20;

원스토어에서는 프로모션이 적용되어 있는 정기 결제 상품을 이용 중인 경우 다른 상품으로 변경 할 수 없습니다.&#x20;

다만, 변경 대상이 되는 상품은 프로모션이 적용되어 있어도 변경이 가능합니다.&#x20;

변경 대상이 되는 상품의 프로모션을 고객이 계속 이용하려면 IMMEDIATE\_WITH\_TIME\_PRORATION 모드를 적용해야 합니다.&#x20;

그 외 다른 비례 배분 모드의 경우 변경 대상이 되는 상품에 프로모션이 적용 되어 있더라도, 프로모션을 적용 받을 수 없습니다.&#x20;

### **정기 결제 기한 연기하기**   <a href="#id-08." id="id-08."></a>

원스토어 인앱결제 API의 deferSubscription 을 사용하여 정기 결제 사용자의 다음 결제일을 연기할 수 있습니다.&#x20;

연기 기간 동안 사용자는 콘텐츠에 엑세스 할 수 있습니다.&#x20;

정기 결제 기한 연기는 다음과 같은 경우 사용 할 수 있습니다.&#x20;

* 특별 이벤트를 통해 기존 사용자에게 일주일 동안 추가로 정기 결제를 사용할 수 있게 해줍니다.
* 시스템 장애 등의 사유로 고객에게 추가 이용 기간을 부여할 수 있습니다.&#x20;

API 호출 당 최소 하루에서 최대 1년까지 결제 기한을 연기 할 수 있습니다. 결제를 더 연기하려면 기한 내에 다시 API를 호출하면 됩니다. <br>

결제 연기 시 이메일 혹은 앱 내에서 알림을 보내 결제일이 변경 되었음을 알릴 수 있습니다.&#x20;

### **정기 결제 가격 변경하기** <a href="#id-08." id="id-08."></a>

원스토어 개발자센터에서 정기 결제 상품의 가격을 변경 할 수 있습니다.  &#x20;

정기 결제 상품의 기본 가격을 변경하면, 새로운 구매는 즉시 변경된 가격으로 반영됩니다.&#x20;

다만, 현재 정기 결제를 사용중인 고객의 경우 7일 후에 변경된 가격에 대한 알림이 전달 되며, 이후 30일 간 가격 변경에 대한 동의 기간이 제공됩니다.&#x20;

{% hint style="info" %}
단, Deferred 모드로 변경 예정인 구독 상품의 가격이 변경 된 경우에는

상품이 변경 된 후 가격 변경 동의 흐름이 시작 됩니다.
{% endhint %}

기간 내에 가격 변경에 대한 동의가 이루어지지 않으면, 해당 정기 결제는 다음 정기 결제일에 취소 됩니다.&#x20;

(가격이 내려가거나 유지되는 경우 별도의 알림이나 동의 절차 없이 7일 이후 다음 갱신일에 변경된 가격으로 결제 됩니다.)

#### 가격 변경에 대한 동의하는 경우   <a href="#id-08." id="id-08."></a>

사용자가 구독료 인상에 동의하는 경우 또는 가격을 인하한 경우에는 동의 기간 이후 변경된 가격으로 결제됩니다.&#x20;

또한 SUBSCRIPTION\_PRICE\_CHANGE\_CONFIRMED 인 Subscription Notification이 전송됩니다.&#x20;

#### 가격 변경에 대해 동의하지 않는 경우  <a href="#id-08." id="id-08."></a>

사용자가 구독료 인상에 동의하지 않는 경우 자동으로 정기 결제가 취소되고, SUBSCRIPTION\_EXPIRED 인 Subscription Notification이 전송됩니다.&#x20;

#### 실수로 가격 변경을 변경한 경우  <a href="#id-08." id="id-08."></a>

실수로 정기 결제 가격을 변경 했다면 가격을 다시 되돌리는 것이 좋습니다.&#x20;

7일 이내에 가격을 되돌린다면, 기존 정기 결제 사용자에게 영향을 미치지 않습니다.

다만, 이 경우에도 신규 구매 고객은 변경 된 가격으로 결제하게 되므로 가격 변경은 신중해야 합니다.&#x20;

#### 두 번의 가격 변경이 발생한 경우  <a href="#id-08." id="id-08."></a>

첫 번째 가격 변경 이후 두 번째 가격 변경이 7일 이내라면, 첫 번째 가격 변경은 무효화되고 두 번째 가격 변경만 적용이 됩니다.&#x20;

다만, 7일 이후라면 사용자는 두 번의 가격 변경에 대한 동의를 해야합니다.&#x20;

가격 변경에 대한 동의 절차는 사용자의 불편을 초래하며, 해당 정기 결제 상품을 유지하는 고객이 줄어들 가능성이 높아집니다.&#x20;

다시 한 번 강조하지만 가격 변경은 신중해야 합니다.&#x20;
