Skip to main content

Subscription Events

DEEPayment sends Subscription and Invoice events to the merchant-level HTTPS Webhook URL configured for your merchant by DEEPayment operations.

Subscription Events use the same Merchant API Access Key, Secret Key, headers, and six-line HMAC-SHA256 signing protocol as other merchant webhooks. Always verify the exact HTTP body bytes before processing an event.

Events are real-time signals. Use the corresponding query API when an event is delayed, arrives out of order, conflicts with local state, or does not cover the state you need.

If the Webhook URL is not configured or is disabled, Subscription and Invoice business processing continues but events are not delivered. Pending delivery tasks are retained for a later retry after the configuration is enabled.

Event Types​

EventMeaning
subscription.activeSubscription entered ACTIVE.
subscription.past_dueSubscription has overdue billing.
subscription.canceledSubscription is canceled.
subscription.unpaidSubscription entered UNPAID.
subscription.failedSubscription establishment failed.
invoice.payment_failedInvoice collection failed.
invoice.paidInvoice was paid and linked to a Payment Order.
invoice.voidedInvoice was voided and will no longer be collected.
invoice.uncollectibleAutomatic collection for the Invoice ended without confirmed payment.

There are no events for the reserved TRIALING status, PENDING_CHECKOUT, or ACTIVATING. Query the Subscription to confirm those states.

HTTP Headers​

Content-Type: application/json
X-Access-Key: your_access_key
X-Timestamp: 1784023200
X-Signature: lowercase_hex_hmac_sha256
X-Body-Encryption: AES-256-GCM

X-Timestamp is the send time of this HTTP delivery in Unix seconds and may change on retry. X-Body-Encryption is present only when body encryption is enabled for the merchant; plaintext deliveries omit it. Payload occurredAt is the domain event creation time in Unix milliseconds and stays unchanged.

Signature​

Find the Secret Key associated with X-Access-Key in the same environment. Compute the hash from the exact HTTP body bytes sent on the wire:

bodyHash = lowercase_hex(SHA256(rawBody))

stringToSign = "POST" + "\n"
+ escapedPath + "\n"
+ rawQuery + "\n"
+ accessKey + "\n"
+ timestamp + "\n"
+ bodyHash

signature = lowercase_hex(
HMAC-SHA256(secretKey, stringToSign)
)
  • escapedPath and rawQuery come from the configured Webhook URL. rawQuery excludes ?; use an empty line when the URL has no query.
  • Preserve rawQuery exactly. Do not sort, re-encode, or drop empty values.
  • accessKey and timestamp are the exact X-Access-Key and X-Timestamp header values.
  • Compare the received and computed signatures safely after validating the timestamp window.

When X-Body-Encryption: AES-256-GCM is present, rawBody is the encrypted envelope. Verify X-Signature against those exact envelope bytes before decrypting. Reject an unknown encryption header or a decryption failure; do not fall back to plaintext parsing. A retry may use a new timestamp and encryption nonce, so its wire body, body hash, and signature may change. The decrypted business payload and its eventId remain stable.

Common Envelope​

FieldTypeDescription
eventIdstringStable event identifier and merchant idempotency key.
merchantIdintegerDEEPayment merchant ID.
eventTypestringEvent type from the table above.
occurredAtintegerDomain event creation time in Unix milliseconds.

Every Subscription and Invoice event includes the frozen paymentMethod. The current supported value is CARD.

Subscription State Event​

{
"eventId": "NTF_example_active",
"merchantId": 10001,
"eventType": "subscription.active",
"occurredAt": 1784023200000,
"statusVersion": 3,
"subscriptionNo": "sub_example_001",
"merchantSubscriptionNo": "membership_user_1001",
"planNo": "plan_example_001",
"customerNo": "cus_example_001",
"paymentMethod": "CARD",
"status": "ACTIVE",
"currentPeriodStart": 1782864000000,
"currentPeriodEnd": 1785542399000,
"nextBillingTime": 1785542400000,
"canceledTime": 0,
"cancelReason": ""
}

The same shape is used by the five Subscription events. status is ACTIVE, PAST_DUE, CANCELED, UNPAID, or FAILED. statusVersion is monotonic only within the same subscriptionNo; accept a Subscription event only when its version is greater than the last version you processed for that Subscription.

Invoice Payment Failed Event​

{
"eventId": "NTF_example_failed",
"merchantId": 10001,
"eventType": "invoice.payment_failed",
"occurredAt": 1784023200000,
"invoiceNo": "inv_example_002",
"subscriptionNo": "sub_example_001",
"merchantSubscriptionNo": "membership_user_1001",
"amount": "199.00",
"paidAmount": "0",
"currency": "MXN",
"paymentMethod": "CARD",
"status": "PAYMENT_FAILED",
"dueTime": 1784023100000,
"attemptCount": 1
}

Invoice Voided And Uncollectible Events​

invoice.voided and invoice.uncollectible use the same Invoice identity and amount fields as invoice.payment_failed. Their status values are VOID and UNCOLLECTIBLE, respectively.

{
"eventId": "NTF_example_voided",
"merchantId": 10001,
"eventType": "invoice.voided",
"occurredAt": 1784023200000,
"invoiceNo": "inv_example_003",
"subscriptionNo": "sub_example_001",
"merchantSubscriptionNo": "membership_user_1001",
"amount": "199.00",
"paidAmount": "0",
"currency": "MXN",
"paymentMethod": "CARD",
"status": "VOID",
"dueTime": 1784023100000,
"attemptCount": 0
}
{
"eventId": "NTF_example_uncollectible",
"merchantId": 10001,
"eventType": "invoice.uncollectible",
"occurredAt": 1784023200000,
"invoiceNo": "inv_example_004",
"subscriptionNo": "sub_example_001",
"merchantSubscriptionNo": "membership_user_1001",
"amount": "199.00",
"paidAmount": "0",
"currency": "MXN",
"paymentMethod": "CARD",
"status": "UNCOLLECTIBLE",
"dueTime": 1784023100000,
"attemptCount": 3
}

These Invoice events do not change the Subscription contract. Query the Subscription separately when deciding customer access.

Invoice Paid Event​

{
"eventId": "NTF_example_paid",
"merchantId": 10001,
"eventType": "invoice.paid",
"occurredAt": 1784023200000,
"invoiceNo": "inv_example_001",
"subscriptionNo": "sub_example_001",
"merchantSubscriptionNo": "membership_user_1001",
"orderNo": "FP_example_001",
"merchantOrderNo": "SUBINV_example_001",
"amount": "199.00",
"paidAmount": "199.00",
"currency": "MXN",
"paymentMethod": "CARD",
"status": "PAID",
"payTime": 1784023199000,
"metadata": {
"planNo": "plan_example_001",
"customerNo": "cus_example_001"
}
}

invoice.paid confirms Invoice payment, not settlement. Settlement status is not exposed by the current Subscription API.

Processing Order​

  1. Resolve credentials

    Reject a missing or unknown X-Access-Key, find its Secret Key, and preserve the exact wire body bytes.

  2. Verify the wire signature

    Validate the Unix-second timestamp window, rebuild the six-line stringToSign from the configured URL, and verify X-Signature before parsing the body.

  3. Decrypt strictly when enabled

    If X-Body-Encryption: AES-256-GCM is present, decrypt the already verified envelope. Otherwise parse the verified raw body as JSON.

  4. Store idempotently

    Use payload eventId as a unique key. A duplicate event must not grant access or post billing twice. For Subscription events, also reject a statusVersion that is not greater than the last version processed for that Subscription.

  5. Reconcile current state

    Different event types are not guaranteed to arrive in order. Query the relevant object when the event conflicts with local state.

  6. Return 2xx

    Return any HTTP 2xx only after successful processing. Non-2xx responses trigger retry.

Delivery is at least once. If DEEPayment receives no usable 2xx response after the merchant processed an event, the same eventId may be delivered again.

Query API object statuses are integers; event payload statuses are strings. Parse each contract independently.