-
Notifications
You must be signed in to change notification settings - Fork 732
Description
Cashier Stripe Version
16.3.0
Laravel Version
12.53.0
PHP Version
8.4.18
Database Driver & Version
No response
Description
Description:
The handleCustomerSubscriptionUpdated method in WebhookController (Line 161 in v16.0) uses isset($data['trial_end']) to determine if the local trial_ends_at column should be updated.
When a subscription's trial ends in Stripe, the webhook payload contains "trial_end": null. In PHP, isset(null) returns false. Consequently, the update block is skipped, and the local trial_ends_at column is never cleared.
Impact:
The local database retains a stale, future timestamp for a trial that has already ended in Stripe.
onTrial() returns true erroneously if the stale timestamp is still in the future relative to the server/test-clock.
Mutations like resume() fail with a 400 error from Stripe because Cashier attempts to re-send the stale (now invalid) trial_end date.
Suggested Fix:
Change the check to array_key_exists('trial_end', $data) or use ! is_null($data['trial_end']) if coupled with an explicit else to nullify the column.
Steps To Reproduce
- Create a subscription with a trial period.
- Wait for the trial to end in Stripe (either naturally or by using a Stripe Test Clock to advance time).
- Stripe sends a customer.subscription.updated webhook. The payload contains "status": "active" and "trial_end": null.
- Cashier processes the webhook via WebhookController@handleCustomerSubscriptionUpdated.
- Check the subscriptions table in the database.
- Actual Result: The trial_ends_at column still contains the old timestamp because isset($data['trial_end']) returns false when the value is null.
- Expected Result: The trial_ends_at column should be updated to NULL to reflect the actual state in Stripe.