가이드

웹훅 이벤트

1. 이벤트 종류

이벤트 타입

의미

payment.completed

일반결제 완료

subscription_payment.completed

정기결제 완료

payment.cancel_requested

일반결제 취소 요청

subscription.cancel_requested

정기결제 해지 신청

🪏 공통 규칙

  • 공통 필드 id, type, version, occurredAt에 대한 설명은 연동 가이드를 확인해 주세요

  • 표의 타입은 string, number, boolean, object, array만 사용해요.

  • options[]는 단일 옵션이어도 항상 배열로 반환돼요.

  • id와 시각 값은 발송 시점마다 달라져요.

  • 시각 정보는 ISO-8601 KST(+09:00) 표준을 따라요.

  • 금액은 long 타입, KRW예요.

  • enum 값은 실제 payload 원문을 그대로 적고, 필요한 곳에는 한글 의미를 함께 풀어 적었어요.

🪏 팁

  • 아래 payment.completed를 기준 스키마로 확인해 주세요.

  • 나머지 3개 이벤트는 payment.completed 스키마와 거의 유사합니다. 다른 필드 몇가지만 확인해 주세요.

  • JSON 예시는 테스트 발송 형식 기준으로 작성했어요.

2. payment.completed (일반결제 완료)

필드 명세

최상위

경로

타입

의미

merchantUid

string

그로블이 발급한 결제 식별자

buyer

경로

타입

의미

buyer.type

string

MEMBER(회원) 또는 GUEST(비회원)

buyer.displayName

string

구매자 표시 이름
회원은 닉네임, 비회원은 입력 이름 기준

buyer.email

string

구매자 이메일

buyer.phoneNumber

string

content.type == GOODS일 때만 포함

content

경로

타입

의미

content.id

string

상품 ID

content.title

string

구매 시점 상품명

content.type

string

DOCUMENT(자료) · SERVICE(서비스) · GOODS(제품) · EVENT(이벤트)

content.paymentType

string

항상 ONE_TIME(일반결제)

options[]

경로

타입

의미

options[].optionId

string

옵션 ID

options[].name

string

옵션명

options[].description

string

옵션 설명

options[].quantity

number

구매 수량

options[].unitOriginalPrice

number

할인 전 단가

options[].unitDiscountedPrice

number

할인 후 단가

options[].discount

object

옵션 할인 블록

options[].discount.type

string

AMOUNT(원화 할인) 또는 PERCENT(% 할인)

options[].discount.value

number

할인 값

options[].discount.timeDeal

object

타임딜 할인일 때만 포함

options[].discount.timeDeal.label

string

타임딜 라벨

options[].additionalOptions

array

추가 옵션 배열. 없으면 []

options[].additionalOptions[].id

string

추가 옵션 ID

options[].additionalOptions[].name

string

추가 옵션명

options[].additionalOptions[].quantity

number

추가 옵션 수량

options[].additionalOptions[].unitPrice

number

추가 옵션 단가

options[].subtotal

number

옵션 소계

pricing

경로

타입

의미

pricing.currency

string

항상 KRW

pricing.originalAmount

number

할인 전 총액

pricing.optionDiscountAmount

number

옵션/타임딜 할인 합계이며, 쿠폰 할인 미포함

pricing.finalAmount

number

실제 결제 금액

pricing.couponDiscountAmount

number

쿠폰 할인 금액

pricing.coupon

object

쿠폰 블록. 없으면 생략

pricing.coupon.code

string

쿠폰 코드

pricing.coupon.name

string

쿠폰 이름

pricing.coupon.type

string

쿠폰 할인 유형

pricing.coupon.discountAmount

number

쿠폰 할인 금액

paymentMethod

경로

타입

의미

paymentMethod.type

string

CARD(카드 결제) 또는
FREE(무료 결제)

paymentMethod.cardName

string

카드사명

paymentMethod.maskedCardNumber

string

마스킹된 카드번호

shipping

content.type == GOODS일 때만 포함됩니다.

경로

타입

의미

shipping.address

string

주소

shipping.streetAddress

string

도로명/지번 주소

shipping.detailAddress

string

상세 주소

shipping.deliveryRequest

string

배송 요청 사항

questionAnswers[]

경로

타입

의미

questionAnswers[].question

string

질문 제목

questionAnswers[].questionType

string

질문 유형

questionAnswers[].required

boolean

응답 필수 여부

questionAnswers[].answer

string

구매자 응답

questionAnswers[].displayOrder

number

질문 표시 순서

payment

경로

타입

의미

payment.purchasedAt

string

구매 완료 시각

JSON 예시 👀
{
    "id": "evt_test_a1b2c3d4e5f60718293a4b5c",
    "type": "payment.completed",
    "version": "2026-04-21",
    "occurredAt": "2026-04-20T14:22:11+09:00",
    "data": {
        "object": {
            "merchantUid": "test_merchant_0001",
            "buyer": {
                "type": "MEMBER",
                "displayName": "테스트 구매자",
                "email": "test@buyer.example"
            },
            "content": {
                "id": "test_content_0001",
                "title": "테스트 상품",
                "type": "DOCUMENT",
                "paymentType": "ONE_TIME"
            },
            "options": [
                {
                    "optionId": "test_option_0001",
                    "name": "기본 옵션",
                    "description": "테스트용 기본 옵션",
                    "quantity": 1,
                    "unitOriginalPrice": 10000,
                    "unitDiscountedPrice": 10000,
                    "additionalOptions": [],
                    "subtotal": 10000
                }
            ],
            "pricing": {
                "currency": "KRW",
                "originalAmount": 10000,
                "optionDiscountAmount": 0,
                "finalAmount": 10000,
                "couponDiscountAmount": 0
            },
            "paymentMethod": {
                "type": "CARD",
                "cardName": "테스트카드",
                "maskedCardNumber": "1234-****-****-5678"
            },
            "questionAnswers": [],
            "payment": {
                "purchasedAt": "2026-04-20T14:22:11+09:00"
            }
        }
    }
}



3. subscription_payment.completed (정기결제 완료)

payment.completed와 거의 같은 구조에 subscription 블록이 추가됩니다.

payment.completed 대비 차이

경로

타입

의미

buyer.type

string

회원 구독은 MEMBER(회원),
비회원 구독은 GUEST(비회원)

content.paymentType

string

항상 SUBSCRIPTION(정기결제)

options[]

array

항상 길이 1

options[].quantity

number

항상 1

options[].additionalOptions

array

항상 []

paymentMethod

object

billingReason == INITIAL일 때만 포함
무료 구독이면 type == FREE

questionAnswers[]

array

billingReason == INITIAL일 때만 포함

subscription

경로

타입

의미

subscription.billingReason

string

INITIAL(최초 결제) 또는
RENEWAL(정기 갱신)

subscription.currentRound

number

현재 결제 회차

subscription.nextBillingDate

string

YYYY-MM-DD 형식의 다음 결제 예정일

subscription.status

string

항상 ACTIVE(활성)

subscription.activatedAt

string

최초 정기결제 활성화 시각

subscription.lastBillingSucceededAt

string

마지막 정기결제 성공 시각

JSON 예시 👀
{
    "id": "evt_test_b1c2d3e4f5a60718293b4c5d",
    "type": "subscription_payment.completed",
    "version": "2026-04-21",
    "occurredAt": "2026-04-20T14:22:11+09:00",
    "data": {
        "object": {
            "merchantUid": "test_merchant_0001",
            "buyer": {
                "type": "MEMBER",
                "displayName": "테스트 구매자",
                "email": "test@buyer.example"
            },
            "content": {
                "id": "test_content_0001",
                "title": "테스트 구독 상품",
                "type": "DOCUMENT",
                "paymentType": "SUBSCRIPTION"
            },
            "options": [
                {
                    "optionId": "test_option_0001",
                    "name": "월간 플랜",
                    "description": "테스트용 월간 구독 옵션",
                    "quantity": 1,
                    "unitOriginalPrice": 9900,
                    "unitDiscountedPrice": 9900,
                    "additionalOptions": [],
                    "subtotal": 9900
                }
            ],
            "pricing": {
                "currency": "KRW",
                "originalAmount": 9900,
                "optionDiscountAmount": 0,
                "finalAmount": 9900,
                "couponDiscountAmount": 0
            },
            "paymentMethod": {
                "type": "CARD",
                "cardName": "테스트카드",
                "maskedCardNumber": "1234-****-****-5678"
            },
            "questionAnswers": [],
            "payment": {
                "purchasedAt": "2026-04-20T14:22:11+09:00"
            },
            "subscription": {
                "billingReason": "INITIAL",
                "currentRound": 1,
                "nextBillingDate": "2026-05-20",
                "status": "ACTIVE",
                "activatedAt": "2026-04-20T14:22:11+09:00",
                "lastBillingSucceededAt": "2026-04-20T14:22:11+09:00"
            }
        }
    }
}



4. payment.cancel_requested (일반결제 취소 요청)

payment.completed 대비 차이

경로

타입

의미

merchantUid

string

원 결제와 동일한 값

content.type

string

SERVICE · GOODS · EVENT 중 하나

pricing.coupon

object

포함되지 않음

paymentMethod.type

string

CARD 또는 FREE

paymentMethod.cardName

string

PG 조회 실패 시 null 가능

paymentMethod.maskedCardNumber

string

PG 조회 실패 시 null 가능

cancelRequest

경로

타입

의미

cancelRequest.reason

object

사유 블록

cancelRequest.reason.code

string

취소 요청 사유
OTHER_PAYMENT_METHOD(다른 수단으로 결제할게요)
CHANGED_MIND(마음이 바뀌었어요)
FOUND_CHEAPER_CONTENT(더 저렴한 콘텐츠를 찾았어요)
ETC(기타)

cancelRequest.reason.label

string

사유 한글 라벨

cancelRequest.detailReason

string

상세 사유. 없으면 생략

cancelRequest.requestedBy

string

현재 BUYER(구매자) 고정

cancelRequest.requestedAt

string

취소 요청 시각

참고

  • buyer.phoneNumbershippingcontent.type == GOODS일 때만 포함됩니다.

JSON 예시 👀
{
    "id": "evt_test_c1d2e3f4a5b60718293c4d5e",
    "type": "payment.cancel_requested",
    "version": "2026-04-21",
    "occurredAt": "2026-04-20T14:22:11+09:00",
    "data": {
        "object": {
            "merchantUid": "test_merchant_0001",
            "buyer": {
                "type": "MEMBER",
                "displayName": "테스트 구매자",
                "email": "test@buyer.example",
                "phoneNumber": "01012345678"
            },
            "content": {
                "id": "test_content_0001",
                "title": "테스트 실물 상품",
                "type": "GOODS",
                "paymentType": "ONE_TIME"
            },
            "options": [
                {
                    "optionId": "test_option_0001",
                    "name": "기본 옵션",
                    "description": "테스트용 기본 옵션",
                    "quantity": 1,
                    "unitOriginalPrice": 10000,
                    "unitDiscountedPrice": 10000,
                    "additionalOptions": [],
                    "subtotal": 10000
                }
            ],
            "pricing": {
                "currency": "KRW",
                "originalAmount": 10000,
                "optionDiscountAmount": 0,
                "finalAmount": 10000,
                "couponDiscountAmount": 0
            },
            "paymentMethod": {
                "type": "CARD",
                "cardName": "테스트카드",
                "maskedCardNumber": "1234-****-****-5678"
            },
            "shipping": {
                "address": "서울시 강남구 테헤란로 123",
                "streetAddress": "서울시 강남구 테헤란로 123",
                "detailAddress": "101동 202호",
                "deliveryRequest": "문 앞에 놓아주세요"
            },
            "questionAnswers": [],
            "cancelRequest": {
                "reason": {
                    "code": "CHANGED_MIND",
                    "label": "마음이 바뀌었어요"
                },
                "detailReason": "색상이 마음에 들지 않아요",
                "requestedBy": "BUYER",
                "requestedAt": "2026-04-20T14:22:11+09:00"
            },
            "payment": {
                "purchasedAt": "2026-04-19T14:22:11+09:00"
            }
        }
    }
}

5. subscription.cancel_requested (정기결제 해지 신청)

subscription_payment.completed 대비 차이

경로

타입

의미

paymentMethod

object

포함되지 않음

questionAnswers[]

array

포함되지 않음

shipping

object

포함되지 않음

subscription

경로

타입

의미

subscription.status

string

항상 CANCELLED(해지 신청 완료)

subscription.currentRound

number

해지 시점의 누적 결제 회차

subscription.nextBillingDate

string

YYYY-MM-DD 형식의 예정된 결제 청구일

subscription.activatedAt

string

최초 정기결제 활성화 시각

subscription.cancelledAt

string

해지 신청 시각

subscription.price

number

직전 결제 금액

subscription.currency

string

항상 KRW

subscription.lastBillingFailureReason

string

직전 실패 사유. 없으면 생략

cancelRequest

경로

타입

의미

cancelRequest.reason

object

해지 신청 사유 블록

cancelRequest.reason.code

string

해지 신청 사유
OTHER_PAYMENT_METHOD(다른 수단으로 결제할게요)
CHANGED_MIND(마음이 바뀌었어요)
FOUND_CHEAPER_CONTENT(더 저렴한 콘텐츠를 찾았어요)
ETC(기타)

cancelRequest.reason.label

string

해지 신청 사유 한글 라벨

cancelRequest.detailReason

string

상세 사유. 값이 있으면 포함

cancelRequest.requestedBy

string

현재 BUYER(구매자) 고정

cancelRequest.requestedAt

string

해지 신청 시각

JSON 예시 👀
{
    "id": "evt_test_d1e2f3a4b5c60718293d4e5f",
    "type": "subscription.cancel_requested",
    "version": "2026-04-21",
    "occurredAt": "2026-04-20T14:22:11+09:00",
    "data": {
        "object": {
            "merchantUid": "test_merchant_0001",
            "buyer": {
                "type": "MEMBER",
                "displayName": "테스트 구매자",
                "email": "test@buyer.example"
            },
            "content": {
                "id": "test_content_0001",
                "title": "테스트 구독 상품",
                "type": "DOCUMENT",
                "paymentType": "SUBSCRIPTION"
            },
            "options": [
                {
                    "optionId": "test_option_0001",
                    "name": "월간 플랜",
                    "description": "테스트용 월간 구독 옵션",
                    "quantity": 1,
                    "unitOriginalPrice": 9900,
                    "unitDiscountedPrice": 9900,
                    "additionalOptions": [],
                    "subtotal": 9900
                }
            ],
            "pricing": {
                "currency": "KRW",
                "originalAmount": 9900,
                "optionDiscountAmount": 0,
                "finalAmount": 9900,
                "couponDiscountAmount": 0
            },
            "payment": {
                "purchasedAt": "2026-01-20T14:22:11+09:00"
            },
            "subscription": {
                "status": "CANCELLED",
                "currentRound": 3,
                "nextBillingDate": "2026-05-05",
                "activatedAt": "2026-01-20T14:22:11+09:00",
                "cancelledAt": "2026-04-20T14:22:11+09:00",
                "price": 9900,
                "currency": "KRW"
            },
            "cancelRequest": {
                "reason": {
                    "code": "ETC",
                    "label": "기타"
                },
                "detailReason": "지금은 더 이상 사용하지 않아요",
                "requestedBy": "BUYER",
                "requestedAt": "2026-04-20T14:22:11+09:00"
            }
        }
    }
}