결제된 주문에 대해 전체 또는 부분 취소를 요청해요.
서버 Basic 인증으로 호출해요. Bootpay-User-JWT 또는 secretKey 인증의 user_id를 지정하면 해당 회원의 주문만 취소 요청할 수 있어요. 이 경우 구매자 규칙이 적용되어 cancel_immediately, cancel_requester 등 관리자용 필드는 무시되고 즉시 취소 여부와 요청자는 서버가 정해요. 필드와 오류의 전체 계약은 주문 처리 API를 확인해요.
API 엔드포인트
취소 유형
| 유형 | 설명 |
|---|---|
| 전체 취소 | 결제된 전체 금액을 한 번에 취소 |
| 부분 취소 | 결제 금액 중 일부만 선택적으로 취소 (일부 카드사 미지원) |
취소 방식
| 방식 | 파라미터 | 설명 |
|---|---|---|
| 상품 기준 | cancel_products |
특정 상품을 선택하여 취소 |
| 금액 기준 | cancel_price |
금액을 직접 지정하여 취소 |
- 주문 취소 요청 -> 취소 범위?
- 취소 범위? -전액-> 전체 취소 / cancel_price 미지정
- 취소 범위? -일부-> 부분 취소 방식?
- 부분 취소 방식? -> 상품 기준 / cancel_products
- 부분 취소 방식? -> 금액 기준 / cancel_price
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
order_number |
String | 필수 | 주문 생성시 발급된 주문 번호 |
user_id |
String | 선택 | 구매자 취소 요청에 사용할 회원 ID. secretKey 인증에서만 지정할 수 있어요 |
request_cancel_parameters |
Object | 선택 | 취소 요청 파라미터. 생략하면 남은 금액 전액을 취소 요청해요 |
└─ cancel_id |
String | 선택 | 가맹점 취소 ID (중복 시 ORDER_CANCEL_ID_ALREADY_REQUESTED) |
└─ cancel_price |
Integer | 선택 | 취소 금액 (0 또는 미지정 시 전액) |
└─ cancel_tax_free_price |
Integer | 선택 | 취소 비과세 금액 |
└─ cancel_requester |
String | 선택 | 취소 요청자명 (기본 관리자) |
└─ cancel_message |
String | 선택 | 취소 사유 |
└─ cancel_products |
Array | 선택 | 취소할 상품 목록 |
└─ product_id |
String | 필수 | 취소할 상품 ID (주문 products[].product_id) |
└─ product_option_id |
String | 선택 | 옵션 상품이면 옵션 ID |
└─ cancel_quantity |
Integer | 필수 | 취소 수량 (1 이상, 취소 가능 수량 이하) |
└─ cancel_order_subscription_bills |
Array | 선택 | 구독 주문의 회차별 취소 목록 |
└─ order_subscription_bill_id |
String | 필수 | 취소할 구독 회차 ID |
└─ cancel_quantity |
Integer | 필수 | 취소할 구독 수량 |
└─ cancel_immediately |
Boolean | 선택 | 즉시 취소 여부. false(기본)면 취소 요청만 만들고 승인을 기다려요 |
상품 단위 취소의 환불액은 서버가 쿠폰·적립금 배분까지 계산해요. 이미 금액 취소가 진행된 주문은 상품 단위 취소를 할 수 없어요. 재시도 시 중복 요청을 피하려면 같은 주문에 고유한 cancel_id를 지정해 보관해요.
코드 예제
const { BootpayCommerce } = require('@bootpay/backend-js')
const commerce = new BootpayCommerce({
client_key: 'your-commerce-client-key',
secret_key: 'your-commerce-secret-key',
mode: 'production'
})
const response = await commerce.order.cancel({
order_number: '25071182085082524116',
request_cancel_parameters: {
cancel_price: 10000,
cancel_message: '고객 요청에 의한 취소',
cancel_requester: '관리자'
}
})
console.log(response)javascriptfrom bootpay_backend import BootpayCommerce
commerce = BootpayCommerce(
client_key='your-commerce-client-key',
secret_key='your-commerce-secret-key',
mode='production'
)
response = commerce.order.cancel(
order_number='25071182085082524116',
request_cancel_parameters={
'cancel_price': 10000,
'cancel_message': '고객 요청에 의한 취소',
'cancel_requester': '관리자'
}
)
print(response)pythonuse Bootpay\ServerPhp\BootpayCommerceApi;
$commerce = new BootpayCommerceApi('your-commerce-client-key', 'your-commerce-secret-key');
$response = $commerce->order->cancel([
'order_number' => '25071182085082524116',
'request_cancel_parameters' => [
'cancel_price' => 10000,
'cancel_message' => '고객 요청에 의한 취소',
'cancel_requester' => '관리자'
]
]);
print_r($response);phpimport kr.co.bootpay.store.BootpayStore;
import kr.co.bootpay.store.model.request.TokenPayload;
import kr.co.bootpay.store.model.request.order.cancel.OrderCancelParams;
import kr.co.bootpay.store.model.request.order.cancel.RequestCancelParameter;
import kr.co.bootpay.store.model.response.BootpayStoreResponse;
TokenPayload tp = new TokenPayload("your-commerce-client-key", "your-commerce-secret-key");
BootpayStore commerce = new BootpayStore(tp).withToken();
RequestCancelParameter cancelParameter = new RequestCancelParameter();
cancelParameter.cancelPrice = 10000.0;
cancelParameter.cancelMessage = "고객 요청에 의한 취소";
cancelParameter.cancelRequester = "관리자";
OrderCancelParams params = new OrderCancelParams();
params.orderNumber = "25071182085082524116";
params.requestCancelParameters = cancelParameter;
BootpayStoreResponse response = commerce.orderCancel.request(params);
System.out.println(response);javarequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: 'your-commerce-client-key', secret_key: 'your-commerce-secret-key')
response = commerce.request_order_cancel(
order_number: '25071182085082524116',
cancel_price: 10000,
cancel_message: '고객 요청에 의한 취소',
cancel_requester: '관리자'
)
puts responserubyimport "github.com/bootpay/backend-go/v2"
commerce := bootpay.NewCommerceApi("your-commerce-client-key", "your-commerce-secret-key")
response, err := commerce.Order.Cancel(bootpay.OrderCancelParams{
OrderNumber: "25071182085082524116",
RequestCancelParameters: bootpay.CancelParameters{
CancelPrice: 10000,
CancelMessage: "고객 요청에 의한 취소",
CancelRequester: "관리자",
},
})
fmt.Println(response)gousing Bootpay.Commerce;
var commerce = new BootpayCommerceApi("your-commerce-client-key", "your-commerce-secret-key");
var response = await commerce.Order.Cancel(new {
order_number = "25071182085082524116",
request_cancel_parameters = new {
cancel_price = 10000,
cancel_message = "고객 요청에 의한 취소",
cancel_requester = "관리자"
}
});
Console.WriteLine(response);csharp응답
성공 응답
주문 필드(주문 상세와 같은 키)에 이번 취소 요청이 order_cancellation_request_history 로 붙어 와요. 아래는 주요 필드만 추린 예시예요.
{
"order_number": "25071182085082524116",
"order_name": "Professional 플랜",
"price": 29900,
"status": "payment_completed",
"receipt_status": "receipt_success",
"cancelled_price": 0,
"order_cancellation_request_history": {
"order_cancellation_request_history_id": "687a1b2c3d4e5f6789012345",
"order_cancellation_request_id": "687a1b2c3d4e5f6789012345",
"cancel_id": "3f1c0e8a-2b6d-4f7e-9a51-7c2d8e4b6a10",
"cancel_price": 10000,
"cancel_requester": "관리자",
"cancel_message": "고객 요청에 의한 취소",
"cancel_immediately": false,
"status": "cancel_requested",
"created_at": "2025-07-11T02:52:09Z"
}
}json에러 응답
HTTP 상태 코드(404)는 헤더로 오고, 본문에는 코드 이름이 문자열로 와요.
{
"error_code": "ORDER_NOT_FOUND",
"message": "주문내역을 찾지 못했습니다."
}json주문 상태 (status)
응답 최상위의 status는 주문 상태예요 — 취소 요청 상태(order_cancellation_request_history.status)와 다른 값 체계입니다.
취소를 요청해도 결제가 살아 있는 동안은 payment_completed(결제 완료)로 남고, 취소가 끝나면
cancellation_completed(취소 완료)가 돼요. 전체 값은 주문 상태를 봐요.
취소 요청 상태 (order_cancellation_request_history.status)
| 키 | 값 | 설명 |
|---|---|---|
cancel_calculated |
0 |
취소 예상 금액만 계산된 상태 |
cancel_requested |
1 |
취소 요청 접수, 승인 대기 |
cancel_approved |
2 |
취소 승인 완료 |
cancel_approval_failed |
-1 |
취소 승인 실패 |
cancel_approval_critical_failed |
-2 |
치명적 오류로 승인 실패 |
cancel_rejected |
-10 |
취소 요청 거절 |
cancel_withdrawn |
-11 |
취소 요청 철회 |
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
ORDER_NOT_FOUND |
주문내역을 찾지 못했어요. | order_number 를 확인해요 (order_id 아님) |
ORDER_NOT_SUCCESS |
결제가 완료된 주문이 아니에요. | 주문 상태를 확인해요 |
ORDER_CANCEL_ID_ALREADY_REQUESTED |
이미 요청된 취소 ID 예요. | cancel_id 를 새로 만들거나 비워요 |
ORDER_CANCEL_IMMEDIATELY_UNABLE |
즉시 취소가 불가능한 상태예요. | cancel_immediately 를 빼고 요청 후 승인해요 |
ORDER_CANCEL_PRICE_OVER |
취소 가능한 금액보다 취소 요청 금액이 더 커요. | 남은 취소 가능 금액을 확인해요 |
ORDER_STATUS_NOT_CANCELABLE |
취소가 불가능한 주문정보예요. | 주문 상태를 확인해요 |
ORDER_PRODUCT_ALREADY_CANCELLED |
이미 취소된 상품이에요. | 주문 상품의 취소 상태를 확인해요 |
취소는 매입 완료 후 진행돼요. 환불은 영업일 기준 3~5일 이내 결제자에게 반환돼요.
