We’ve migrated our documentation to a new site, which means some URLs have changed. If you hit a broken link, submit a support ticket.
Subscriptions

Webhook Handling & Stripe Billing Events

Webhook handling

Here is how Stripe Billing Events are integrated into Piano, so that you can understand how critical subscription actions take place between the Stripe and Piano systems.

Stripe Billing Events

Event Type

Description

Piano Billing Integration Processing

customer.subscription.deleted

Customer's subscription ends.

Expire Piano subscription by setting to Expired status when deleted in Stripe (by setting it to expired it will become inactive).

invoice.created

New invoice created.

Extend Piano access, confirm or update TaxRate ID, verify invoice.

  1. Extend Piano access to the next billing date plus 3 days. This is the normal renewal behavior and it happens days before the renewal date.

  2. Confirm or update TaxRate ID.

  3. Verify invoice. If invoice invalid, then stop payments collection, extend Piano access (so that it is not shorter than 3 days, so that end-user does not lose Piano access), alert and troubleshoot.

invoice.deleted

Draft invoice deleted.

Piano access not extended, set payment status to ABORTED. If Stripe invoice is deleted, charge is skipped for that cycle.

invoice.upcoming

Emit an event X days before an auto-charged subscription invoice is generated (configurable; recommended X = 3 days).

Verify the invoice.

If the invoice is invalid, then:

  • stop payments collection

  • extend Piano access (so that it is not shorter than 3 days, so that end-user does not lose Piano access)

  • alert and troubleshoot

tax_rate.updated

Occurs whenever a tax rate is updated.

Tax rates created by Piano are immutable.

If a tax rate is modified directly in Stripe, Piano flags the original tax rate ID as corrupted and creates a new tax rate in Stripe.

invoice.finalization_failed

Draft invoice cannot be finalized.

Extend Piano access, so that it is not shorter than 3 days. Piano payment is NOT PENDING (we do not touch Piano payment on this event). Alert, so that Piano can fix the invoice and re-finalize it.

invoice.finalized

Draft invoice finalized.

Handling depends on the payment path:

  • Stripe-native, synchronous (card, wallet): ignored. Access has already been extended on invoice.created, and invoice.paid renews the subscription.

  • Stripe-native, asynchronous (SEPA, Bacs, ACH): initiate renewal of the Piano subscription to the next billing period, advance the period counter, and set payment status to PENDING. The Piano subscription renewal conversion is not created at this point.

  • BYOP, any method: initiate the renewal and trigger the payment request to your processor. Piano marks the invoice paid out of band on success.

invoice.paid

Invoice payment succeeds.

Renew Piano subscription, set payment status to COMPLETED.

For synchronous payment:

  1. Renew Piano subscription to the next billing period.

  2. Piano payment is PAID.

  3. Create Piano subscription renewal conversion.

For asynchronous payment:

  1. Piano payment is PAID.

  2. Create Piano subscription renewal conversion.

invoice.payment_action_required

Payment requires user action.

Start preset 30-day grace period, extend access, send failure notification. If the end-user updates the method, payment either succeeds or fails, at which point Smart Retry starts. If the user does not update the method and the preset 30-day period runs its course, Piano expires the subscription.

invoice.payment_failed

Payment attempt fails.

Grace period logic applies.

Piano does not inspect the decline code. It reads the next_payment_attempt field on the invoice:

  • A date is present: Stripe has a retry planned. Start a grace period for the Piano subscription and keep access active.

  • The field is null: Stripe has no further retry planned, either because the retry period is over or because the decline code is one Stripe does not retry. Expire the Piano subscription to EXPIRED_WITH_ERROR and revoke access. Piano payment is CANCELED.

Stripe returns null on hard declines, so a hard decline ends the subscription at the first attempt.

subscription_schedule.aborted

Subscription schedule aborted.

Internal System Validation only.

subscription_schedule.canceled

Subscription schedule canceled.

Internal System Validation only.

subscription_schedule.completed

Subscription schedule completed.

Internal System Validation only.

subscription_schedule.created

Subscription schedule created.

Internal System Validation only.

subscription_schedule.expiring

Subscription schedule expiring.

Internal System Validation only.

subscription_schedule.released

Subscription schedule released.

Internal System Validation only.

subscription_schedule.updated

Subscription schedule updated.

Internal System Validation only.

Last updated: