Payment events

For engineers handling payment webhooks sent by Stripe, Paddle, or Solidgate after a funnel checkout. You'll have a clear per-scenario matrix plus an idempotent handler skeleton. You need a webhook endpoint registered in your payment provider's dashboard.

Scenarios#

ScenarioWhat firesSource of app_user_id
One-time purchasecheckout.session.completed (Stripe) / transaction.completed (Paddle) / order.approved (Solidgate)metadata.app_user_id on the session / transaction / order
Subscription, first paymentcheckout.session.completed + invoice.paid (Stripe) / subscription.created + transaction.completed (Paddle)metadata.app_user_id on the subscription or customer
Subscription renewalinvoice.paid (Stripe) / transaction.completed (Paddle) / subscription.renewed (Solidgate)metadata.app_user_id on the subscription
Subscription schedule phase changeinvoice.paid + subscription_schedule.updated (Stripe)Subscription metadata
Stripe upsellcheckout.session.completed with mode=payment after the original subscriptionSession metadata
Cancellation / refundcustomer.subscription.deleted / charge.refunded (Stripe) / subscription.canceled (Paddle) / subscription.canceled (Solidgate)Subscription metadata

Stripe event matrix#

EventFires onWhen to handle
checkout.session.completedAny completed Checkout SessionAlways. Primary signal for first payment.
invoice.paidEach subscription invoiceFor subscription renewals. Also the first payment if using subscription schedules.
customer.subscription.updatedSubscription state changePlan change, trial end, quantity change.
customer.subscription.deletedSubscription canceledRevoke access.
charge.refundedRefund issuedRevoke access if the refund is a full cancellation.

Paddle event matrix#

EventFires onWhen to handle
transaction.completedCompleted transaction (one-time or subscription payment)Always. Primary payment signal.
subscription.createdNew subscriptionStart entitlement.
subscription.updatedSubscription state changePlan, pause, resume.
subscription.canceledSubscription canceledRevoke at period end.

Solidgate event matrix#

EventFires onWhen to handle
order.approvedOrder completedStart or renew entitlement.
subscription.renewedRenewal billedExtend entitlement.
subscription.canceledSubscription canceledRevoke at period end.

Idempotency#

Every event has an id. Every payment object Zellify creates carries app_user_id. Use one or both as your idempotency key:

  • Dedupe on event.id to ignore repeat deliveries of the same event.
  • Key entitlement writes by app_user_id so repeated events converge on the same user record.

Reading app_user_id from events#

Missing app_user_id: If app_user_id is missing from metadata on an event, the event likely refers to an object not created by Zellify (manual refund in the dashboard, direct API call, test object). Handle defensively; do not assume a user mapping.
Checkout metadata
What Zellify attaches on which objects per provider.
app_user_id
Your idempotency key across every event.
Mobile app with custom backend
End-to-end recipe consuming these events.
Using existing webhooks
Adapt an existing handler.