The webhook ‘invoice.payment_succeeded’ actually does distinguish between the first charge of a new subscription and subsequent renewal charges.
The webhook sends an invoice object, which includes 'billing_reason'
– the possible values of which are noted in the Stripe Docs – The Invoice object:
billing_reason
(string)
“Indicates the reason why the invoice was created.
subscription_cycle
indicates an invoice created by a subscription advancing into a new period. subscription_create
indicates an invoice created due to creating a subscription. subscription_update
indicates an invoice created due to updating a subscription. subscription
is set for all old invoices to indicate either a change to a subscription or a period advancement. manual is set for all invoices unrelated to a subscription (for example: created via the invoice editor). The upcoming value is reserved for simulated invoices per the upcoming invoice endpoint.”
If billing_reason == 'subscription_cycle'
the webhook is for a subscription
renewal.
If billing_reason == 'subscription_create'
the webhook is for a brand new subscription.