핵심 요약
- 진행 중인 구독의 변경 요청(일시정지·재개·해지·인수·승계·플랜 변경)을 관리자에게 보여주고 처리해요.
- 승인·거절은
PUT /v1/order-subscription-requests/:id한 경로에서approval값으로 구분해요. - 목록·상세 조회까지 v1 REST API 3개 경로를 다뤄요. 철회 REST API는 현재 제공되지 않아요.
이런 상황이라면
고객이 구독 해지를 요청했어요. 관리자는 요청 상세에서 금액과 사유를 확인하고 승인하거나 거절해요. 고객 화면은 회원 조회 모드에서 본인의 요청 상태를 확인해요. 신규 구독 자체의 승인·거절은 신규 승인과 구독 거절을 봐요.
이 페이지에서 승인·거절·목록·상세 흐름과 철회 API의 제공 여부를 함께 봐요.
1신청 승인
구독 변경 요청(일시정지·재개·해지·인수·승계·플랜 변경)을 승인해요. 관리자 권한이 필요해요.
API 엔드포인트
https://api.bootapi.com/v1/order-subscription-requests/:request_history_idBasic Auth (supervisor 권한 필요)승인과 거절은 같은 엔드포인트를 쓰고, 요청 본문의 approval 값(approve / reject)으로 구분해요. 이 API는 관리자(Supervisor) 권한이 필요해요.
승인 시나리오
- 해지 요청 승인: 구독이 종료되고 수수료가 정산돼요
- 수수료 조정 승인: 관리자가 수수료를 조정하여 승인할 수 있어요
플로우
- 해지 요청 접수 -> 관리자 확인 / 수수료 조정
- 관리자 확인 / 수수료 조정 -> approve / API 호출
- approve / API 호출 -> 해지 완료 / 환불 처리 (승인)
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
request_history_id |
String | 필수 | 요청 고유 ID (URL 경로) |
approval |
String | 필수 | approve 고정 (거절은 reject) |
reason |
String | 선택 | 승인 사유 (내부 기록용) |
price |
Number | 선택 | 중도인수 승인 시 인수 금액. 중도인수 요청(request_purchase)을 승인할 때는 필수예요 |
tax_free_price |
Number | 선택 | 중도인수 승인 시 비과세 금액 |
termination_fee |
Number | 선택 | 해지 승인 시 조정 수수료 |
last_bill_refund_price |
Number | 선택 | 해지 승인 시 마지막 회차 환불 금액 |
final_fee |
Number | 선택 | 해지 승인 시 최종 청구 금액 |
service_end_at |
String | 선택 | 해지 승인 시 서비스 종료일 |
코드 예제
curl -X PUT "https://api.bootapi.com/v1/order-subscription-requests/{request_history_id}" \
-H "Authorization: Basic {base64(client_key:secret_key)}" \
-H "Content-Type: application/json" \
-d '{
"approval": "approve",
"reason": "승인 처리합니다"
}'bashconst { BootpayCommerce } = require('@bootpay/backend-js');
const commerce = new BootpayCommerce({
client_key: '{client_key}',
secret_key: '{secret_key}'
});
const response = await commerce.orderSubscriptionRequest.approve(
'req_001',
{ reason: '승인 처리합니다' }
);
console.log(response);javascriptfrom bootpay_backend.commerce import BootpayCommerce
commerce = BootpayCommerce('{client_key}', '{secret_key}')
response = commerce.order_subscription_request_approve(
'req_001',
{'reason': '승인 처리합니다'}
)
print(response)pythonuse Bootpay\ServerPhp\BootpayCommerceApi;
$commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
$response = $commerce->orderSubscriptionRequestApprove('req_001', [
'reason' => '승인 처리합니다'
]);
print_r($response);phpimport kr.co.bootpay.store.BootpayStore;
import kr.co.bootpay.store.model.request.TokenPayload;
import kr.co.bootpay.store.model.response.BootpayStoreResponse;
import kr.co.bootpay.store.model.request.orderSubscriptionRequest.OrderSubscriptionRequestUpdateParams;
TokenPayload tokenPayload = new TokenPayload("{client_key}", "{secret_key}");
BootpayStore commerce = new BootpayStore(tokenPayload).withToken();
OrderSubscriptionRequestUpdateParams params = new OrderSubscriptionRequestUpdateParams();
params.orderSubscriptionRequestHistoryId = "req_001";
params.approval = "approve";
params.reason = "승인 처리합니다";
BootpayStoreResponse response = commerce.orderSubscriptionRequest.update(params);
System.out.println(response);javarequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: '{client_key}', secret_key: '{secret_key}')
response = commerce.order_subscription_request_update(
request_history_id: 'req_001',
approval: 'approve',
reason: '승인 처리합니다'
)
puts responserubycommerce := bootpay.NewCommerceApi("{client_key}", "{secret_key}")
response, err := commerce.OrderSubscriptionRequestApprove("req_001", map[string]interface{}{
"reason": "승인 처리합니다",
})
fmt.Println(response)govar commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
var response = await commerce.OrderSubscriptionRequestApprove("req_001", new {
reason = "승인 처리합니다"
});
Console.WriteLine(response);csharp지금은 중도인수(request_purchase) 요청을 승인하면 요청 상태만 request_approved 로 바뀌고, 인수 금액 결제와 구독 상태 변경은 일어나지 않아요. 인수 금액은 이 API 밖에서 따로 청구해야 해요.
응답
성공 시 200 OK예요. 응답 본문 형식은 요청 유형마다 달라요. 처리 뒤 요청 상태(request_status)는 신청 상세로 다시 확인해요.
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
ORDER_SUBSCRIPTION_REQUEST_HISTORY_NOT_FOUND |
계약변경 요청 내역을 찾을 수 없어요. | request_history_id를 확인해요 |
ORDER_SUBSCRIPTION_REQUEST_HISTORY_ALREADY_PROCESSED |
이미 처리된 요청이에요. | 새로운 요청을 생성해요 |
ORDER_SUBSCRIPTION_REQUEST_TYPE_INVALID |
구독 요청 타입이 유효하지 않아요. | approval 은 approve·reject 만 받아요. 해지 복구·만기 요청은 이 API로 승인할 수 없어요 |
ORDER_SUBSCRIPTION_PURCHASE_PRICE_REQUIRED |
중도인수 금액이 필요해요. | 중도인수 요청을 승인할 때 price 를 함께 보내요 |
2신청 거절
구독 변경 요청(해지, 인수, 이전)을 거절해요. 관리자 권한이 필요해요.
API 엔드포인트
https://api.bootapi.com/v1/order-subscription-requests/:request_history_idBasic Auth (supervisor 권한 필요)승인과 같은 엔드포인트예요. 요청 본문의 approval을 reject로 보내면 거절 처리돼요. 이 API는 관리자(Supervisor) 권한이 필요해요.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
request_history_id |
String | 필수 | 요청 고유 ID (URL 경로) |
approval |
String | 필수 | reject 고정 |
reason |
String | 필수 | 거절 사유 |
코드 예제
curl -X PUT "https://api.bootapi.com/v1/order-subscription-requests/{request_history_id}" \
-H "Authorization: Basic {base64(client_key:secret_key)}" \
-H "Content-Type: application/json" \
-d '{
"approval": "reject",
"reason": "최소 구독 기간 미충족"
}'bashconst { BootpayCommerce } = require('@bootpay/backend-js');
const commerce = new BootpayCommerce({
client_key: '{client_key}',
secret_key: '{secret_key}'
});
const response = await commerce.orderSubscriptionRequest.reject(
'req_001',
{
reason: '최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책'
}
);
console.log(response);javascriptfrom bootpay_backend.commerce import BootpayCommerce
commerce = BootpayCommerce('{client_key}', '{secret_key}')
response = commerce.order_subscription_request_reject(
'req_001',
{
'reason': '최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책'
}
)
print(response)pythonuse Bootpay\ServerPhp\BootpayCommerceApi;
$commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
$response = $commerce->orderSubscriptionRequestReject('req_001', [
'reason' => '최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책'
]);
print_r($response);phpimport kr.co.bootpay.store.BootpayStore;
import kr.co.bootpay.store.model.request.TokenPayload;
import kr.co.bootpay.store.model.response.BootpayStoreResponse;
import kr.co.bootpay.store.model.request.orderSubscriptionRequest.OrderSubscriptionRequestUpdateParams;
TokenPayload tokenPayload = new TokenPayload("{client_key}", "{secret_key}");
BootpayStore commerce = new BootpayStore(tokenPayload).withToken();
OrderSubscriptionRequestUpdateParams params = new OrderSubscriptionRequestUpdateParams();
params.orderSubscriptionRequestHistoryId = "req_001";
params.approval = "reject";
params.reason = "최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책";
BootpayStoreResponse response = commerce.orderSubscriptionRequest.update(params);
System.out.println(response);javarequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: '{client_key}', secret_key: '{secret_key}')
response = commerce.order_subscription_request_update(
request_history_id: 'req_001',
approval: 'reject',
reason: '최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책'
)
puts responserubycommerce := bootpay.NewCommerceApi("{client_key}", "{secret_key}")
response, err := commerce.OrderSubscriptionRequestReject("req_001", map[string]interface{}{
"reason": "최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책",
})
fmt.Println(response)govar commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
var response = await commerce.OrderSubscriptionRequestReject("req_001", new {
reason = "최소 구독 기간 미충족 — 3개월 미만 해지 불가 정책"
});
Console.WriteLine(response);csharp응답
성공 시 200 OK예요. 응답 본문 형식은 요청 유형마다 달라요. 거절 뒤 요청 상태(request_rejected)와 사유(action_reason)는 신청 상세로 다시 확인해요.
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
ORDER_SUBSCRIPTION_REQUEST_HISTORY_NOT_FOUND |
계약변경 요청 내역을 찾을 수 없어요. | request_history_id를 확인해요 |
ORDER_SUBSCRIPTION_REQUEST_HISTORY_ALREADY_PROCESSED |
이미 처리된 요청이에요. | 새로운 요청을 생성해요 |
ORDER_SUBSCRIPTION_REJECT_REASON_BLANK |
거절 사유는 필수예요. | reason 을 입력해요 |
3신청 철회
접수된 요청을 철회하는 로직은 서버 모델(request_withdraw)에 있지만 v1 REST 엔드포인트로 연결돼 있지 않아요. 공개된 라우트 중 withdraw는 주문 취소(PUT /v1/order/cancel/:id/withdraw) 하나뿐이에요.
관리자가 요청을 마감하려면 위 거절(approval: "reject")을 사용해요. 구매자가 직접 철회하는 REST API는 현재 없어요.
철회 가능 상태 (request_status)
| 값 | 키 | 설명 | 철회 가능 |
|---|---|---|---|
0 |
request_requested |
요청됨, 관리자 승인 대기 | 가능 |
1 |
request_approved |
관리자 승인 완료 | 불가 |
2 |
request_approved_auto |
자동 승인 완료 | 불가 |
-1 |
request_rejected |
관리자 거절 | 불가 |
-2 |
request_withdrawn |
고객이 철회 | 불가 (이미 철회됨) |
4신청 목록
고객이 제출한 구독 관련 요청(해지, 중도 인수, 승계 등) 목록을 조회하는 API예요. 관리자는 요청 목록을 통해 전체 현황을 파악하고 승인/거절 처리를 진행할 수 있어요.
API 엔드포인트
요청 유형 (request_type)
| 값 | 키 | 설명 |
|---|---|---|
11 |
request_pause |
일시정지 요청 |
12 |
request_resume |
재개 요청 |
21 |
request_transfer |
이전(승계) 요청 |
31 |
request_purchase |
중도 인수 요청 |
41 |
request_termination |
중도 해지 요청 |
42 |
request_termination_restore |
해지 철회 요청 |
51 |
request_migrate_plan |
플랜 변경 요청 |
101 |
request_expired_purchase |
만기 후 구매 |
111 |
request_expired_return |
만기 후 반납 |
121 |
request_expired_extend |
만기 후 연장 |
구독 상태 (status)
응답의 list[].status 는 구독 상태예요 — 요청 상태(request_status)와 다른 값 체계입니다.
자주 나오는 값은 subscribing(구독 중)이고, 전체 목록은 구독 상태에 있어요.
요청 상태 (request_status)
| 값 | 키 | 설명 |
|---|---|---|
0 |
request_requested |
요청됨, 관리자 승인 대기 |
1 |
request_approved |
관리자 승인 완료 |
2 |
request_approved_auto |
자동 승인 완료 |
-1 |
request_rejected |
관리자 거절 |
-2 |
request_withdrawn |
고객이 철회 |
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
project_id |
String | 선택 | 보내면 관리 모드(supervisor:order_subscription_request_list)로 조회해요. 값은 모드 전환에만 쓰이고 조회 범위는 인증된 프로젝트예요. 없으면 회원 모드예요 |
keyword |
String | 선택 | 검색어 (요청 ID, 사용자명 등) |
request_type |
String | 선택 | 요청 유형 필터 (request_termination·request_transfer·request_purchase 등, 요청 유형 참고) |
status |
String | 선택 | 요청 상태 필터 (request_requested·request_approved·request_approved_auto·request_rejected·request_withdrawn). 값 하나만 받아요 |
order_subscription_id |
String | 선택 | 특정 구독 계약의 요청만 조회 |
user_id |
String | 선택 | 회원 모드에서는 JWT가 없을 때 필수인 회원 ID 또는 이 몰의 외부 회원 ID. 관리 모드에서는 회원 필터 |
user_group_id |
String | 선택 | 특정 그룹의 요청만 조회 |
s_at |
String | 선택 | 검색 시작일 (요청 생성일 기준). 비우면 오늘 |
e_at |
String | 선택 | 검색 종료일 (요청 생성일 기준). 비우면 오늘 |
page |
Integer | 선택 | 페이지 번호 (기본값: 1) |
limit |
Integer | 선택 | 페이지당 데이터 수 (기본값: 20, 최대 100) |
project_id가 없는 회원 모드에서는 user_id와 limit만 필터로 사용해요. 페이지 번호와 기간·상태 필터는 관리 모드에서 사용해요.
project_id를 보내면 관리 모드로 프로젝트 전체 요청을 조회해요. 구매자 화면에서는 이를 빼고 Bootpay-User-JWT 헤더 또는 user_id를 보내 본인 요청만 조회해요. Basic 인증은 두 모드 모두 필요해요. 회원 모드에서 둘 다 빠지면 400 REQUIRED_PARAMETER_IS_MISSING이 와요.
필요한 scope는 관리 모드 supervisor:order_subscription_request_list, 회원 모드 user:order_subscription_request_list예요.
코드 예제
curl -X GET "https://api.bootapi.com/v1/order-subscription-requests?project_id={project_id}&page=1&limit=10" \
-H "Authorization: Basic {base64(client_key:secret_key)}" \
-H "Content-Type: application/json"bashconst { BootpayCommerce } = require('@bootpay/backend-js');
const commerce = new BootpayCommerce({
client_key: '{client_key}',
secret_key: '{secret_key}'
});
const response = await commerce.orderSubscriptionRequest.list({
project_id: '{project_id}',
page: 1,
limit: 10,
status: 0 // 요청됨(승인 대기)
});
console.log(response);javascriptfrom bootpay_backend.commerce import BootpayCommerce
commerce = BootpayCommerce('{client_key}', '{secret_key}')
response = commerce.order_subscription_request_list({
'project_id': '{project_id}',
'page': 1,
'limit': 10,
'status': 0
})
print(response)pythonuse Bootpay\ServerPhp\BootpayCommerceApi;
$commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
$response = $commerce->orderSubscriptionRequestList([
'project_id' => '{project_id}',
'page' => 1,
'limit' => 10,
'status' => 0
]);
print_r($response);phpimport kr.co.bootpay.store.BootpayStore;
import kr.co.bootpay.store.model.request.TokenPayload;
import kr.co.bootpay.store.model.response.BootpayStoreResponse;
import kr.co.bootpay.store.model.request.orderSubscriptionRequest.OrderSubscriptionRequestListParams;
TokenPayload tokenPayload = new TokenPayload("{client_key}", "{secret_key}");
BootpayStore commerce = new BootpayStore(tokenPayload).withToken();
OrderSubscriptionRequestListParams params = new OrderSubscriptionRequestListParams();
params.projectId = "{project_id}";
params.page = 1;
params.limit = 10;
params.status = 0;
BootpayStoreResponse response = commerce.orderSubscriptionRequest.list(params);
System.out.println(response);javarequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: '{client_key}', secret_key: '{secret_key}')
response = commerce.order_subscription_request_list(
project_id: '{project_id}',
page: 1,
limit: 10,
status: 0
)
puts responserubycommerce := bootpay.NewCommerceApi("{client_key}", "{secret_key}")
response, err := commerce.OrderSubscriptionRequestList(map[string]interface{}{
"project_id": "{project_id}",
"page": 1,
"limit": 10,
"status": 0,
})
fmt.Println(response)govar commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
var response = await commerce.OrderSubscriptionRequestList(new {
project_id = "{project_id}",
page = 1,
limit = 10,
status = 0
});
Console.WriteLine(response);csharp응답
성공 응답
{
"count": 25,
"list": [
{
"order_subscription_id": "687a1b2c3d4e5f6789012345",
"order_name": "Professional 플랜 구독",
"status": "subscribing",
"username": "홍길동",
"current_duration": 6,
"total_subscription_duration": 12,
"request_histories": [
{
"order_subscription_request_history_id": "687b2c3d4e5f678901234567",
"request_type": "request_termination",
"request_status": "request_requested",
"payment_status": "order_pending",
"requested_at": "2025-07-11T10:00:00+09:00",
"request_reason": "서비스 이용 종료 희망"
}
]
}
]
}json위는 관리 모드 응답이에요. 회원 모드는 { "list": [{ "request_type": "request_pause" }], "count": 1 } 형태이며 count는 이번 응답에 포함된 요청 수예요.
응답 파라미터
요청이 걸린 구독 계약이 list 로 내려오고, 그 구독에 접수된 요청들이 request_histories 배열로 들어가요.
| 파라미터 | 타입 | 설명 |
|---|---|---|
| count | Number | 조회된 요청의 총 개수. 한 구독에 요청이 여러 건이면 list 에 그 구독이 요청 수만큼 반복돼요 |
| list | Array | 요청이 걸린 구독 계약 목록 |
| └─ order_subscription_id | String | 구독 계약 ID |
| └─ order_name | String | 주문명 |
| └─ status | String | 구독 상태 (구독 상태 참고) |
| └─ username | String | 구독자 이름 |
| └─ current_duration | Integer | 현재 회차 |
| └─ total_subscription_duration | Integer | 총 회차 |
| └─ request_histories | Array | 이 구독에 접수된 요청 목록 |
| └─ order_subscription_request_history_id | String | 요청 고유 ID |
| └─ request_type | String | 요청 유형 (요청 유형 참고) |
| └─ request_status | String | 요청 상태 (요청 상태 참고) |
| └─ payment_status | String | 요청에 딸린 결제 상태 |
| └─ requested_at | String | 요청 시각 |
| └─ request_reason | String | 요청 사유 |
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
INVALID_PARAMETER |
status·request_type 값을 해석할 수 없어요. |
숫자 또는 위 표의 키로 보내요 |
REQUIRED_PARAMETER_IS_MISSING |
회원 모드에서 회원 JWT와 user_id가 모두 없어요. |
둘 중 하나를 보내요 |
USER_NOT_FOUND |
user_id가 이 몰의 회원이 아니거나 탈퇴했어요. |
해당 몰의 유효한 회원을 확인해요 |
5신청 상세
특정 구독 요청의 상세 정보를 조회하는 API예요. 요청 사유, 처리 사유, 수수료 계산 내역 등을 확인할 수 있어요.
API 엔드포인트
조회 가능한 정보
- 요청 유형 및 상태
- 요청자 유저 ID, 관련 구독 계약 ID
- 요청 사유 (고객 작성 텍스트)와 처리 사유
- 금액 정보 (해지 수수료, 마지막 회차 환불액, 최종 청구액)
- 추가 청구 내역
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
request_history_id |
String | 필수 | 요청 고유 ID (URL 파라미터) |
project_id |
String | 선택 | 보내면 관리 모드(supervisor:order_subscription_request_detail)로 조회해요. 없으면 회원 모드예요 |
user_id |
String | 선택 | 회원 모드에서 회원 JWT 대신 보낼 회원 ID 또는 이 몰의 외부 회원 ID |
회원 모드에는 Bootpay-User-JWT 헤더 또는 user_id가 필요해요. 필요한 scope는 관리 모드 supervisor:order_subscription_request_detail, 회원 모드 user:order_subscription_request_detail예요.
코드 예제
curl -X GET "https://api.bootapi.com/v1/order-subscription-requests/{request_history_id}?project_id={project_id}" \
-H "Authorization: Basic {base64(client_key:secret_key)}" \
-H "Content-Type: application/json"bashconst { BootpayCommerce } = require('@bootpay/backend-js');
const commerce = new BootpayCommerce({
client_key: '{client_key}',
secret_key: '{secret_key}'
});
const response = await commerce.orderSubscriptionRequest.detail('687a1b2c3d4e5f6789012345', '{project_id}');
console.log(response);javascriptfrom bootpay_backend.commerce import BootpayCommerce
commerce = BootpayCommerce('{client_key}', '{secret_key}')
response = commerce.order_subscription_request_detail('687a1b2c3d4e5f6789012345')
print(response)pythonuse Bootpay\ServerPhp\BootpayCommerceApi;
$commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
$response = $commerce->orderSubscriptionRequestDetail('687a1b2c3d4e5f6789012345');
print_r($response);phpimport kr.co.bootpay.store.BootpayStore;
import kr.co.bootpay.store.model.request.TokenPayload;
import kr.co.bootpay.store.model.response.BootpayStoreResponse;
TokenPayload tokenPayload = new TokenPayload("{client_key}", "{secret_key}");
BootpayStore commerce = new BootpayStore(tokenPayload).withToken();
BootpayStoreResponse response =
commerce.orderSubscriptionRequest.detail("687a1b2c3d4e5f6789012345", "{project_id}");
System.out.println(response);javarequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: '{client_key}', secret_key: '{secret_key}')
response = commerce.order_subscription_request_detail(
request_history_id: '687a1b2c3d4e5f6789012345'
)
puts responserubycommerce := bootpay.NewCommerceApi("{client_key}", "{secret_key}")
response, err := commerce.OrderSubscriptionRequestDetail("687a1b2c3d4e5f6789012345")
fmt.Println(response)govar commerce = new BootpayCommerceApi("{client_key}", "{secret_key}");
var response = await commerce.OrderSubscriptionRequestDetail("687a1b2c3d4e5f6789012345");
Console.WriteLine(response);csharp응답
성공 응답
{
"order_subscription_request_history_id": "687a1b2c3d4e5f6789012345",
"order_subscription_id": "687b2c3d4e5f678901234567",
"user_id": "67e4b4425ec892162491d0ec",
"request_type": "request_termination",
"request_status": "request_requested",
"payment_status": "order_pending",
"request_reason": "서비스 이용 종료 희망",
"requested_at": "2025-07-11T10:00:00+09:00",
"termination_fee": 50000,
"last_bill_refund_price": 0,
"final_fee": 50000,
"created_at": "2025-07-11T10:00:00+09:00",
"updated_at": "2025-07-11T10:00:00+09:00",
"supplemental_charges": []
}json응답 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| order_subscription_request_history_id | String | 요청 고유 ID |
| order_subscription_id | String | 관련 구독 계약 ID |
| user_id | String | 요청자 ID |
| request_type | String | 요청 유형 — 아래 표 참고 |
| request_status | String | 요청 상태 (요청 상태 참고) |
| payment_status | String | 요청에 딸린 결제 상태 |
| request_reason | String | 요청 사유 |
| action_reason | String | 처리(승인·거절) 사유 |
| requested_at | String | 요청 시각 |
| actioned_at | String | 처리 시각 |
| termination_fee | Number | 해지 수수료 (해지 요청) |
| last_bill_refund_price | Number | 마지막 회차 환불 금액 (해지 요청) |
| final_fee | Number | 최종 청구 금액. 양수면 추가 결제, 음수면 환불 (해지 요청) |
| supplemental_charges | Array | 이 구독에 걸린 추가 청구 내역 |
| created_at | String | 요청 생성일 |
| updated_at | String | 최종 수정일 |
요청 유형 (request_type)
| 값 | 키 | 설명 |
|---|---|---|
11 |
request_pause |
일시정지 요청 |
12 |
request_resume |
재개 요청 |
21 |
request_transfer |
이전(승계) 요청 |
31 |
request_purchase |
중도 인수 요청 |
41 |
request_termination |
중도 해지 요청 |
42 |
request_termination_restore |
해지 철회 요청 |
51 |
request_migrate_plan |
플랜 변경 요청 |
101 |
request_expired_purchase |
만기 후 구매 |
111 |
request_expired_return |
만기 후 반납 |
121 |
request_expired_extend |
만기 후 연장 |
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
ORDER_SUBSCRIPTION_REQUEST_HISTORY_NOT_FOUND |
계약변경 요청 내역을 찾을 수 없어요. | request_history_id를 확인해요 |
REQUIRED_PARAMETER_IS_MISSING |
회원 모드에서 회원 JWT와 user_id가 모두 없어요. |
둘 중 하나를 보내요 |
USER_NOT_FOUND |
user_id가 이 몰의 회원이 아니거나 탈퇴했어요. |
해당 몰의 유효한 회원을 확인해요 |
다음 단계 — 구독 운영으로
계약이 승인되면 그다음은 구독 > 운영 영역이에요. 회차가 자동으로 돌기 시작하므로 아래 순서로 이동해요.
전체 라이프사이클은 구독 흐름 설계에서 단일 도식으로 확인해요.
응답의 status 같은 코드값은 정수가 아니라 문자열(enum)로 내려와요. 전체 목록은 커머스 Enum에서 확인해요.
