콘텐츠로 이동

결제

결제 승인·조회·취소 API와 응답으로 돌아오는 PaymentIntent 객체, PaymentCancel 객체를 살펴봅니다.

결제 시도 한 건의 상태와 금액, 취소 시각, 영수증 정보를 담고 있는 객체입니다.

승인·조회·취소 API는 같은 PaymentIntent 형식으로 응답합니다. 상태와 결제수단에 따라 선택 필드의 제공 여부가 달라집니다.

interface PaymentIntent {
/**
* @minLength 21
* @maxLength 21
* 결제의 키값입니다. 캔디페이에서 각 결제별로 자동으로 생성해주는 문자열입니다.
* 각 결제를 식별하는 역할로, 결제 데이터 관리를 위해 반드시 저장해야 합니다.
* 결제 상태가 변해도 값이 유지됩니다.
* 결제 승인, 결제 조회, 결제 취소 API에 사용합니다.
*/
intentKey: string
/**
* @minLength 16
* @maxLength 50
* 주문번호입니다. 결제 요청에서 내 상점이 직접 생성한 영문 대소문자, 숫자, 특수문자 -, _로 이루어진 16자 이상 50자 이하의 문자열입니다.
* 각 주문을 식별하는 역할로, 결제 데이터 관리를 위해 반드시 저장해야 합니다.
* 결제 상태가 변해도 orderId는 유지됩니다.
*/
orderId: string
/**
* 정산Id입니다. 아직 정산이 진행되지 않은 경우 null로 반환됩니다.
*/
settlementId?: string | null
/**
* @maxLength 100
* @example '생수 외 1건'
* 주문의 이름입니다. 상점이 직접 값을 지정해줘야 합니다. 구매상품으로 지정하는 것을 권장합니다.
*/
orderName: string
/**
* 상점아이디(MID)입니다. 캔디페이에서 발급합니다.
*/
mId: number
/**
* 총 결제 금액입니다. 결제가 취소되는 등 결제 상태가 변해도 최초에 결제된 결제 금액으로 유지됩니다
*/
totalAmount: number
/**
* 남은 환불 가능 금액입니다. 부분취소 후에는 totalAmount보다 작아집니다.
*/
refundableAmount?: number
status: 'READY' | 'IN_PROGRESS' | 'DONE' | 'PARTIAL_CANCELED' | 'CANCELED' | 'ABORTED' | 'EXPIRED'
/**
* @pattern yyyy-MM-dd'T'HH:mm:ss±hh:mm
* @example '2022-01-01T00:00:00+09:00'
* 결제를 요청한 날짜와 시간 정보입니다. ISO 8601 형식입니다.
*/
requestedAt: string
/**
* @pattern yyyy-MM-dd'T'HH:mm:ss±hh:mm
* @example '2022-01-01T00:00:00+09:00'
* 결제 승인이 일어난 날짜와 시간 정보입니다. ISO 8601 형식입니다.
*/
approvedAt?: string
/**
* 부분취소 또는 전액취소 상태이면 제공됩니다. 개별 취소 이력 배열이 아닙니다.
*/
cancel?: PaymentCancel
methods: PaymentIntentMethod[]
/** 결제 요청에 저장한 사용자 정의 정보입니다. */
metadata?: Record<string, string | number | boolean | null>
/**
* 발행된 영수증 정보입니다.
*/
receipt?: {
/**
* 결제금액에 대한 캔디페이 통합 영수증입니다. 사용을 권장하지 않습니다.
*/
candyUrl: string
/**
* 구매자에게 제공할 수 있는 결제수단별 영수증입니다. 카드 결제는 매출전표가 제공됩니다.
*/
perMethodUrls: string[]
}
/**
* 결제가 실패 상태이면 (주로 승인 실패) 여기에 에러 객체를 담아 보내줍니다.
*/
failure?: {
code: string
message: string
details:
| { card: { cardId: number } }
| { keyin: null }
| { billing: string }
| { appcard: null }
| { bank: null }
}[]
}
interface PaymentCancel {
/**
* @pattern yyyy-MM-dd'T'HH:mm:ss±hh:mm
* @example '2022-01-01T00:00:00+09:00'
* 결제 취소가 일어난 날짜와 시간 정보입니다. ISO 8601 형식입니다.
*/
canceledAt?: string
}
interface PaymentIntentMethod {
/**
* 결제수단 종류입니다. 이용 가능한 수단은 가맹점 설정에 따라 달라집니다.
*/
type: 'CARD' | 'KEYIN' | 'BILLING' | 'APPCARD' | 'BANK'
/**
* 결제수단별 결제 금액입니다.
*/
amount: number
/**
* 저장된 카드 정보가 있는 결제수단에서 제공됩니다.
*/
card?: {
/**
* 카드번호입니다. 번호의 일부는 마스킹 되어 있습니다. 최대 길이는 20자입니다.
*/
number: string
/**
* 할부 개월 수입니다. 일시불이면 0입니다.
*/
installmentPlanMonths: number
}
}
상태 의미와 후속 처리
READY 승인 대기 상태입니다. 결제 완료로 처리하지 마세요.
IN_PROGRESS 결제수단 인증 결과가 보관되어 있거나 승인을 처리 중인 상태입니다. 결제 완료로 처리하지 마세요.
DONE 승인 완료 상태입니다. 이후 부분취소 또는 전액취소될 수 있습니다.
PARTIAL_CANCELED 일부 금액이 취소된 상태입니다. refundableAmount로 남은 금액을 확인하세요. 추가 취소가 가능합니다.
CANCELED 전액취소 상태입니다.
ABORTED 승인 처리에 실패한 상태입니다. 아래의 거래 확인이 필요한 오류에 유의하세요.
EXPIRED 유효기간이 만료된 상태입니다.

CANCELED, ABORTED, EXPIRED는 해당 결제 시도가 종료된 상태입니다. 단, PAYMENT_RECONCILIATION_REQUIRED를 받았거나 통신 오류로 결과가 불명확하면 ABORTED만 보고 실제 청구가 없다고 판단하지 마세요. PG 승인 또는 보상 취소 결과의 확인이 필요할 수 있습니다.

결제 링크의 expiresAt과 인증 후 승인 가능한 시간은 별개입니다. READY·IN_PROGRESS가 일정 시간 후 반드시 EXPIRED로 변경된다고 가정하지 마세요. 인증 유효시간이 필요한 연동은 캔디페이 담당자에게 확인해주세요.

POST https://api.candypay.co.kr/px/intents/confirm
Content-Type: application/json
{
"intentKey": "string",
"orderId": "string",
"amount": number
}

결제 승인에 성공하면 PaymentIntent 객체가 돌아옵니다. 필드에 값이 제대로 들어왔는지 확인하세요.

결제 승인에 실패했다면 HTTP 상태 코드와 함께 에러 객체가 돌아옵니다.

결제 승인 API에서 발생할 수 있는 에러를 살펴보세요.

같은 주문에서 결제수단 인증을 다시 진행하면 다른 intentKey가 발급될 수 있습니다. orderId는 주문을, intentKey는 결제 시도를 식별하므로 두 값을 함께 저장하세요.

승인 API는 동일 요청에 최초 성공 응답을 재전송하는 방식이 아니며, Idempotency-Key 응답 재사용 대상에도 포함되지 않습니다. 이미 처리 중이거나 완료된 거래에 재요청하면 오류를 받을 수 있습니다. 완료된 주문에 대한 승인 재요청에서는 ALREADY_COMPLETED_PAYMENT 또는 NOT_FOUND가 반환될 수 있으므로, 승인 재요청의 오류만으로 원래 결제가 실패했다고 판단하지 마세요.

승인 응답을 받지 못했거나 5XX 오류를 받으면 해당 intentKey로 결제를 조회하세요. 조회는 캔디페이에 저장된 상태를 반환하며, 조회할 때마다 PG 거래를 재확인하는 방식은 아닙니다. READY·IN_PROGRESS 상태에서는 주문을 확인 대기로 유지하고 신규 결제 시도를 자동 생성하지 마세요. 결과 확정까지의 최대 지연 시간은 보장하지 않으므로, 상태가 확정되지 않으면 캔디페이 담당자에게 주문번호와 intentKey를 전달해주세요.

PAYMENT_RECONCILIATION_REQUIRED는 승인·취소 또는 그 결과 저장 중 확인이 필요한 오류입니다. 자동 재승인을 중단하고 캔디페이에 거래 확인을 요청하세요.

특정 결제 시도의 결과를 확인하거나 웹훅을 검증할 때 사용하세요. 응답의 orderId, mId, totalAmount를 상점에 저장된 주문과 대조하세요.

GET https://api.candypay.co.kr/px/intents/{intentKey}

결제 조회에 성공하면 PaymentIntent 객체가 돌아옵니다. 필드에 값이 제대로 들어왔는지 확인하세요.

결제 조회에 실패했다면 HTTP 상태 코드와 함께 에러 객체가 돌아옵니다.

결제 조회 API에서 발생할 수 있는 에러를 살펴보세요.

해당 가맹점 주문에서 가장 최근에 생성된 결제 시도를 반환합니다. 한 주문에 여러 intentKey가 있다면 승인된 시도와 최신 시도가 다를 수 있습니다. 특정 승인·취소 또는 웹훅의 결과를 확인할 때는 해당 intentKey로 조회하세요.

GET https://api.candypay.co.kr/px/intents/orders/{orderId}

결제 조회에 성공하면 PaymentIntent 객체가 돌아옵니다. 필드에 값이 제대로 들어왔는지 확인하세요.

결제 조회에 실패했다면 HTTP 상태 코드와 함께 에러 객체가 돌아옵니다.

결제 조회 API에서 발생할 수 있는 에러를 살펴보세요.

DONE 또는 PARTIAL_CANCELED 상태에서 취소할 수 있습니다. cancelAmount를 생략하면 남은 환불 가능 금액을 전액 취소합니다. 잔여액보다 작은 금액을 보내면 부분취소되며, 결제수단이 1개인 결제만 지원합니다. 함께결제는 부분취소를 지원하지 않습니다.

같은 취소 작업을 재요청할 때는 같은 Idempotency-Key를 사용하세요. 특히 부분취소를 키 없이 반복하면 별도의 추가 취소로 처리될 수 있습니다. 최초 요청부터 키를 보내고, 다른 취소 작업에는 새 키를 사용하세요.

멱등성 응답 재사용 없이 이미 전액취소된 거래를 다시 취소하면 400 ALREADY_CANCELED_PAYMENT가 반환됩니다. 취소 응답을 받지 못했거나 PAYMENT_RECONCILIATION_REQUIRED가 발생하면 해당 intentKey로 조회하고, 실제 취소 결과가 불명확하면 캔디페이에 확인을 요청하세요.

취소 가능 기간·한도·수수료와 정산 반영 시점은 가맹점 계약 및 PG 정책을 담당자에게 확인해주세요. EXCEED_CANCEL_LIMIT는 PG의 취소 한도 또는 채권방지 제한을 나타낼 수 있으며, 단순히 요청 금액이 잔여액보다 크다는 뜻으로 해석하지 마세요.

POST https://api.candypay.co.kr/px/intents/{intentKey}/cancel
Content-Type: application/json
{
/**
* @maxLength 200
*/
"cancelReason": "string",
/**
* 취소할 금액입니다. 값을 보내지 않으면 남은 금액을 전액 취소하고,
* 남은 금액보다 작은 값을 보내면 부분취소됩니다. (부분취소는 결제수단이 1개인 결제만 지원)
* @minimum 1
*/
"cancelAmount": 1000
}

결제 취소에 성공하면 PaymentIntent 객체가 반환됩니다. status는 부분취소 시 PARTIAL_CANCELED, 전액취소 시 CANCELED이며, cancel.canceledAt과 refundableAmount로 취소 시각과 잔여액을 확인할 수 있습니다. totalAmount는 최초 주문 금액으로 유지됩니다.

결제 취소에 실패했다면 HTTP 상태 코드와 함께 에러 객체가 돌아옵니다.

결제 취소 API에서 발생할 수 있는 에러를 살펴보세요.

주문번호로 취소할 때도 취소 조건과 재요청 처리가 적용됩니다. 같은 취소 작업의 재시도에서는 엔드포인트를 바꾸지 말고 같은 Idempotency-Key를 유지하세요.

POST https://api.candypay.co.kr/px/intents/orders/{orderId}/cancel
Content-Type: application/json
{
/**
* @maxLength 200
*/
"cancelReason": "string",
/**
* 취소할 금액입니다. 값을 보내지 않으면 남은 금액을 전액 취소하고,
* 남은 금액보다 작은 값을 보내면 부분취소됩니다. (부분취소는 결제수단이 1개인 결제만 지원)
* @minimum 1
*/
"cancelAmount": 1000
}

결제 취소에 성공하면 PaymentIntent 객체가 반환됩니다. status, cancel.canceledAt, refundableAmount로 취소 결과를 확인하세요.

결제 취소에 실패했다면 HTTP 상태 코드와 함께 에러 객체가 돌아옵니다.

결제 취소 API에서 발생할 수 있는 에러를 살펴보세요.