Skip to content

Fix SubscriptionWebhookHandler followup issues#225

Merged
menvil merged 1 commit into
developfrom
fix/phase16-webhook-handler-followup
Jun 2, 2026
Merged

Fix SubscriptionWebhookHandler followup issues#225
menvil merged 1 commit into
developfrom
fix/phase16-webhook-handler-followup

Conversation

@menvil

@menvil menvil commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Fixes 3 valid issues, skips 3 invalid/out-of-scope

Applied

firstOrFail()firstOrCreate() on credit account lock
Root cause: if a user's CreditAccount row doesn't exist (e.g. UserObserver failed at registration), firstOrFail() throws ModelNotFoundException, causing the webhook to fail and Stripe to retry indefinitely.
Fix: CreditAccount::query()->lockForUpdate()->firstOrCreate(...) — provisions the row under the lock if missing.

Portable JSON operator in alreadyGrantedForInvoice + user_id scope
Root cause: whereRaw("json_extract(metadata_json, '$.stripe_invoice_id') = ?", ...) is SQLite-specific syntax; breaks silently on MySQL/Postgres.
Fix: replaced with Laravel's portable ->where('metadata_json->stripe_invoice_id', $invoiceId). Added user_id scope so the check is correctly isolated per user.

'free' string → Plan::Free enum in handleSubscriptionCancelled
Root cause: inconsistency — handleSubscriptionActivated uses $plan->key (string via DTO) but handleSubscriptionCancelled hardcoded 'free'. The model casts plan to Plan::class.
Fix: use Plan::Free directly.

Skipped

  • CashierSubscriptionCheckoutGateway ->url — not valid: we never set ui_mode, so Cashier always returns hosted checkout with a guaranteed ->url.
  • BillingPlanRepository::toDto validation — config is developer-controlled static data; missing keys are a deploy-time error caught immediately, not a runtime user input validation issue.
  • Migration PII retention — out of scope; requires architectural planning (encryption, pruning jobs, retention policy).

Test plan

  • composer test passes (459 tests)
  • composer lint passes

🤖 Generated with Claude Code


Summary by cubic

Hardens Stripe subscription webhook handling to avoid crashes and duplicate credit grants. Fixes missing credit account errors, uses a portable JSON query, and aligns plan casting.

  • Bug Fixes
    • Create-or-lock credit account: replace firstOrFail() with firstOrCreate() under lockForUpdate() to prevent failures when the account is missing.
    • Portable JSON query + scoping: use metadata_json->stripe_invoice_id and add user_id filter in the invoice check to avoid cross-user matches and work across DBs.
    • Use Plan::Free on cancellation instead of the 'free' string for type-safe updates.

Written for commit c2095fe. Summary will update on new commits.

Review in cubic

- Replace firstOrFail() with firstOrCreate() on credit account lock:
  avoids ModelNotFoundException when credit account is missing
  (e.g. UserObserver failed); account is provisioned under the lock
- Replace SQLite-specific whereRaw json_extract with portable
  Laravel JSON operator (metadata_json->stripe_invoice_id) and
  scope alreadyGrantedForInvoice by user_id
- Use Plan::Free enum instead of hardcoded 'free' string in
  handleSubscriptionCancelled for type consistency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • release

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 014dc113-bd7d-4e66-b263-8b79e19eb38d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/phase16-webhook-handler-followup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@menvil menvil merged commit 10301af into develop Jun 2, 2026
4 checks passed
@menvil menvil deleted the fix/phase16-webhook-handler-followup branch June 2, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant