11 min read

Webhook

A webhook is an endpoint used for server-to-server real-time notifications through HTTP.

Description

In order to keep your users purchases statuses up-to-date on your server, you can setup an URL that iaptic will call whenever there's an important event changing the state of that user.

This Webhook will be called when the user validates a receipt or when Apple, Google, Stripe (and other platforms) themselves sends a notification, for subscription events for instance.

The webhook URL is setup from the Settings page.

Requirements

If any of those requirements are not met, your webhook endpoint will not be called.

Webhook Request

Iaptic will make a request to your Webhook URLs using the POST method. The body will be a JSON object containing the webhook type and the associated data.

The format is described here: class Webhook.PurchasesUpdated.

Sandbox Webhook URL

In the settings you will find the Webhook URL and Sandbox Webhook URL fields.

  • If only the Webhook URL is set, all notifications will be sent to that URL.
  • If the Sandbox Webhook URL is set, it'll receive notifications for users making "sandbox" purchases.
    • the "Webhook URL" will then only receive notifications for users having made "production" purchases.

Multiple URLs

In each of those fields, you can set multiple URLs by separating them with a comma.

Important

  • Your endpoint should use SSL, i.e. be an https:// endpoint. Self-signed certificates are accepted.
  • Make sure to return status 200 for all calls, even for unrecognized webhook types. The webhook might be extended with new message types.
  • If your server responds with a status other than 200, iaptic will retry the webhook call. See Retry Strategy for more details.

Testing your Webhook

Next to the Webhook URL field, there's a Test button. Clicking this button will send a test webhook notification to ALL your Webhook URLs.

The content of the webhook will be as follows:

{
    "type": "test",
    "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

See class Webhook.Test

Notifications

Since version 3.3, webhook calls include a notification field. This field lets your server know why the call was made.

It can be used to build analytics, for instance in your code:

const notification = webhook.notification;
if (notification.reason != "RECEIPT_VALIDATED") { // storing those is rarely useful
  notificationsDB.insert(
    notification.id, notification.date, notification.reason,
    notification.productId, notification.purchaseId);
}

// ...

// Count purchases since January 2023
notificationsDB.pseudoSQL('SELECT count(*) WHERE date > "2023-01-01" AND reason = "PURCHASED"');


// Count renewals since January 2023
notificationsDB.pseudoSQL('SELECT count(*) WHERE date > "2023-01-01" AND reason = "RENEWED"');

Check NotificationReason for the list of possible notification reasons.

Webhook Timing

Different platforms send webhooks at different stages:

  1. Google Play:

    • Webhooks are sent only after purchase acknowledgment
    • Unacknowledged purchases won't trigger webhooks
    • Purchases must be verified and acknowledged within 3 days
  2. Apple App Store:

    • Initial purchase notifications are sent immediately
    • Subscription status updates are sent in real-time
    • Server-to-server notifications don't depend on acknowledgment

Webhook Types

  1. RECEIPT_VALIDATED:

    • Sent after successful receipt validation
    • Indicates the purchase is valid but may not be acknowledged yet
  2. PURCHASED:

    • Sent after purchase is both validated and acknowledged
    • For Google Play, this only happens after explicit acknowledgment
    • For Apple, this happens after successful validation
  3. RENEWED:

    • Sent when a subscription renews successfully
    • Includes the new expiry date
  4. EXPIRED:

    • Sent when a subscription expires
    • Includes the expiry date

Webhook Order

Webhooks may be received in any order. For example:

  • A RECEIPT_VALIDATED webhook might arrive after PURCHASED
  • Multiple webhooks might be sent for the same event
  • Your server should handle duplicate notifications gracefully

Debugging Missing Webhooks

If you're not receiving expected webhooks:

  1. For Google Play:

    • Verify the purchase was acknowledged by the app
    • Check the transaction ID in iaptic dashboard
    • Review client-side logs for validation errors
  2. For Apple App Store:

    • Verify the receipt was validated
    • Check server-to-server notification settings
    • Review notification history in App Store Connect

Error Handling Best Practices

  1. Always Return 200 Status

    • Return HTTP 200 even for webhooks you don't process
    • Return HTTP 200 even for validation errors (e.g., "Invalid user ID", "User not found", ...)
    • Only return non-200 status for actual server errors
  2. Webhook Processing

    • Process what you can, ignore what you don't understand
    • Log unrecognized webhook types for future compatibility
    • Handle duplicate notifications gracefully

Monitoring Webhook Health

To ensure your webhooks are working properly:

  1. Check webhook logs for non-200 responses
  2. Set up alerts for webhook processing errors
  3. Regularly test all webhook endpoints
  4. Monitor webhook latency and success rates

Retry Strategy

When a webhook call fails (non-200 response), iaptic implements the following retry mechanism:

  • Up to 7 retry attempts with exponential backoff (1h, 2h, 3h, etc.)
  • Automatic safety net retry after 4-6 minutes
  • Maximum 8 attempts within a 24-hour window
  • After all retries fail, the webhook is marked as failed
  • 100 successive failures will trigger blacklisting of your endpoint

Blacklisted URLs

Endpoints will be blacklisted if they consistently fail:

  • 100 successive non-200 responses triggers blacklisting
  • Blacklisting lasts for 30 days
  • All webhook calls to blacklisted endpoints are suspended, except TEST webhooks

If you want to restore a blacklisted URL:

  1. Fix the endpoint to return 200 status for all webhook types
  2. Use the "Test Webhook" feature in the dashboard to force a call
  3. If the test succeeds, the endpoint will be immediately removed from the blacklist

Important: A blacklisted endpoint means you might miss important purchase updates. Always monitor your webhook endpoints' health and ensure they handle all webhook types correctly.