Event Architecture

Refund and dispute lifecycle

Understand how refunds, disputes, and expired checkout attempts affect entitlement state and runtime access.

Start free in test mode. Go live when you're ready.

Category: Event Architecture3 sections

How reversal and suspension affect runtime state

Refunds, disputes, and expired checkout attempts all change billing state, but they do not all mutate entitlement state in the same way. Licenzy keeps runtime state conservative so access is not over-granted after payment reversal or failed finalization.

  • A full refund on time_pass cancels the entitlement and expires access immediately.
  • A full refund on usage_pack cancels the entitlement and sets usage_remaining to 0.
  • Partial refunds are audit-only and do not mutate entitlement state by themselves.
  • Subscription refund or dispute handling suspends entitlement state conservatively as past_due.
  • checkout.session.expired marks the unfinalized attempt failed and creates no entitlement.

Operational flows

Use these flows when you reason about how billing reversals show up in runtime behavior:

  • Full refund on time_pass or usage_pack: Stripe refund -> entitlement mutation -> access revoked -> typically entitlement.updated.
  • Subscription dispute or refund: Stripe dispute/refund -> subscription-backed runtime state suspended conservatively as past_due -> access denied -> typically subscription.updated when runtime state changes.
  • Partial refund: Stripe partial refund -> no entitlement mutation -> no runtime access change. Do not assume a runtime outbound event if state did not change.

Where the broader lifecycle still lives

Stripe subscription and invoice lifecycle remains authoritative for renewal, recovery, and final cancellation. Licenzy consumes those billing signals and keeps the runtime layer conservative.

checkout.session.expired is also part of this boundary: it creates no entitlement, enables no new access, and may emit no outbound runtime event.

Use runtime state propagation for the full trigger-to-access matrix, and outbound webhook delivery contract when you need to reason about what your receiver should do after those state changes are emitted.