subscription.payment_succeeded
Fires on every successful charge against a subscription: the first charge after trial-end, every renewal, every dunning retry that lands. The event is the source-of-truth for "money came in for this subscription"; subscribe if you book revenue per-charge rather than per-cycle.
When it fires
Three code paths:
- First charge — trial ended or there was no trial. Fires alongside (or just after)
subscription.created. - Renewal — cycle rolled over and the renewal charge succeeded. Fires alongside
subscription.renewed. - Retry success — a
past_duesubscription's retry landed. Fires alongsidesubscription.renewed.
Payload
{
"id": "evt_01HX...",
"type": "subscription.payment_succeeded",
"createdAt": "2026-06-01T00:00:14Z",
"accountId": "acc_01HX...",
"data": {
"subscriptionId": "sub_01HX..."
}
}
Slim — fetch the subscription for amount, currency, customer, and current cycle. The reason for the minimal payload is that the renewal cron emits this before billing-detail propagation, so the source-of-truth lookup is GET /v1/payment/subscriptions/:id.
Handler examples
// Node
if (event.type === 'subscription.payment_succeeded') {
const sub = await client.subscriptions.retrieve(event.data.subscriptionId);
await revenue.book({
subscriptionId: sub.id,
amount: sub.planAmount,
currency: sub.planCurrency,
period: { start: sub.currentPeriodStart, end: sub.currentPeriodEnd },
});
}
# Python
if event["type"] == "subscription.payment_succeeded":
sub = client.subscriptions.retrieve(event["data"]["subscriptionId"])
revenue.book(subscription_id=sub["id"], amount=sub["planAmount"], currency=sub["planCurrency"])
// Go
if event.Type == "subscription.payment_succeeded" {
var d struct{ SubscriptionID string `json:"subscriptionId"` }
_ = json.Unmarshal(event.Data, &d)
sub, _ := client.Subscriptions.Retrieve(ctx, d.SubscriptionID)
revenue.Book(ctx, sub)
}
What to do
- Book revenue (one row per event, keyed on
event.idso retries don't double-count). - Restore entitlement if the subscription was previously in
past_due— this charge brought it back. - Trigger your own "thanks for renewing" mail if you don't trust Storlaunch's default receipt.
Common pitfalls
- Double-counting with
subscription.renewed. Both fire on every successful renewal. Pick one for revenue booking and dedupe the other. - Assuming first-charge means trial-converted. The first
payment_succeededcan fire because trial ended OR because there was no trial. Read the previous subscription state from your DB if the distinction matters. - Not deduping retries. The dunning loop fires
payment_failedthenpayment_succeededon retry — both have uniqueevent.ids but reference the same subscription. Be ready for sequences.
Related events
subscription.renewed— the lifecycle sibling.subscription.payment_failed— the negative outcome.