Webhooks
SubX sends events to registered URLs whenever subscription status changes. Payload integrity can be verified with HMAC-SHA256 signatures.
Event Types
| Event | Description |
|---|---|
INITIAL_PURCHASE | Initial purchase completed |
RENEWAL | Subscription auto-renewal succeeded |
CANCELLATION | Subscription cancelled (expires on next renewal date) |
UNCANCELLATION | Cancellation reversed (subscription reactivated) |
BILLING_ISSUE | Payment failed (expired card, insufficient funds, etc.) |
EXPIRATION | Subscription fully expired |
PRODUCT_CHANGE | Subscription product changed (upgrade/downgrade) |
REFUND | Refund processed |
Payload Structure
All webhooks send JSON via POST in the following format:
webhook-payload.json
{
"id": "evt_abc123",
"type": "INITIAL_PURCHASE",
"timestamp": "2026-02-18T09:30:00Z",
"projectId": "proj_abc123",
"data": {
"customerId": "cust_xyz789",
"appUserId": "user_12345",
"productId": "prod_monthly",
"productIdentifier": "com.example.pro.monthly",
"entitlements": ["pro"],
"store": "APP_STORE",
"currency": "KRW",
"price": 9900,
"transactionId": "txn_abc123",
"purchaseDate": "2026-02-18T09:30:00Z",
"expirationDate": "2026-03-18T09:30:00Z"
}
}Request Headers
| Header | Description |
|---|---|
Content-Type | application/json |
X-SubX-Signature | HMAC-SHA256 signature |
X-SubX-Event | Event type (e.g. INITIAL_PURCHASE) |
Signature Verification
Verify payload integrity using the Signing Secret issued when registering the webhook. Always verify the signature before processing the event.
Node.js Example
import crypto from "crypto";
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express 핸들러 예시
app.post("/webhooks/subx", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-subx-signature"] as string;
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(payload);
switch (event.type) {
case "INITIAL_PURCHASE":
// 최초 구매 처리
handleInitialPurchase(event.data);
break;
case "RENEWAL":
// 갱신 처리
handleRenewal(event.data);
break;
case "CANCELLATION":
// 취소 처리
handleCancellation(event.data);
break;
// ... 기타 이벤트
}
// 200 응답 (필수)
res.status(200).json({ received: true });
});중요: Important: Webhook handlers must return a 200 status code. Otherwise, SubX will retry the delivery.
Retry Policy
If webhook delivery fails (timeout or non-2xx response), it will automatically retry:
| Attempt | Wait Time | Cumulative Time |
|---|---|---|
| 1st retry | After 1 min | +1 min |
| 2nd retry | After 10 min | +11 min |
| 3rd retry | After 1 hour | +1 hr 11 min |
If all 3 retries fail, the event is recorded as failed. You can view failed events in the webhook logs on the dashboard and manually resend them.
Best Practices
- 서명 검증 필수: Validate the X-SubX-Signature header on every webhook.
- 멱등성 처리: The same event ID (evt_...) may be sent more than once. Use event IDs to prevent duplicate processing.
- 빠른 응답: Return a 200 response within 5 seconds. Queue long-running tasks and process them asynchronously.
- HTTPS 필수: Webhook URLs must use HTTPS.
Next Steps
- Getting Started — SubX 설정 전체 과정을 확인합니다.
- REST API — 전체 API 엔드포인트를 확인합니다.
