# Testing Laravel Payments Kit

Use this page as a quick checklist for local and sandbox tests. Keep `stripe listen` running while testing from the browser.

```bash
stripe listen --forward-to http://your-app.test/stripe/webhook
```

Copy the signing secret printed by the CLI into `STRIPE_WEBHOOK_SECRET` for the environment that receives the webhook.

## Frontend Test Cards

Use a future expiration date, any CVC, and any postal code unless the case says otherwise.

| Scenario | Card | Expected result |
| --- | --- | --- |
| Successful payment | `4242 4242 4242 4242` | Checkout completes and Stripe sends the successful payment or invoice events. |
| 3D Secure challenge for subscriptions and invoices | `4000 0027 6000 3184` | Stripe opens the 3DS dialog. Approving pays the invoice; failing keeps the invoice open. |
| First subscription checkout with failed 3DS | `4000 0027 6000 3184` | If the first 3DS challenge fails before Stripe creates the subscription, expect `payment_intent.payment_failed`, not a subscription update. |
| Always blocked by Radar | `4100 0000 0000 0019` | Payment is blocked as fraud. Useful for testing failed checkout and blocked payment handling. |
| Highest Radar risk | `4000 0000 0000 4954` | Charge has highest risk. Radar rules decide whether it is blocked. |
| Elevated Radar risk | `4000 0000 0000 9235` | Charge has elevated risk. Useful for review/manual checks. |
| High early fraud warning score | `4000 0084 0000 0159` | Charge has a high early fraud warning score. Radar rules decide the outcome. |
| Early fraud warning / dispute flow | `4000 0000 0000 0259` | Use from the real frontend flow and watch `stripe listen` for fraud warning or dispute events. |
| Default payment method failure for invoices | `4000 0000 0000 0341` | Use with a saved default payment method and a subscription invoice or Test Clock. |

## Stripe CLI Triggers

These triggers are useful for checking the webhook endpoint, signature validation, event storage, and event routing.

```bash
stripe trigger checkout.session.completed
stripe trigger checkout.session.expired
stripe trigger payment_intent.succeeded
stripe trigger payment_intent.payment_failed
stripe trigger payment_intent.requires_action
stripe trigger invoice.paid
stripe trigger invoice.payment_failed
stripe trigger invoice.payment_action_required
stripe trigger customer.subscription.created
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted
stripe trigger charge.dispute.created
stripe trigger charge.dispute.updated
stripe trigger charge.dispute.closed
stripe trigger charge.refunded
```

Stripe CLI fixtures use synthetic objects. They prove the webhook flow works, but they do not always update your real local order or subscription records.

## Events That Need Real Data

`refund.created` and `radar.early_fraud_warning.*` are not always available as direct `stripe trigger` events. Test them with real sandbox actions:

- Create a real payment, then refund it through the Dashboard, API, or Laravel Payments Kit.
- Create a real frontend payment with the early fraud warning card and watch the forwarded webhooks.
- Use `charge.refunded` only to test generic webhook routing for refund-related state.

## Verify Local Processing

```bash
php artisan tinker --execute="dump(\SuppliesSoft\LaravelPaymentsKit\Models\StripeWebhookEvent::latest('id')->take(10)->get(['stripe_event_id','type','processed_at'])->toArray());"
```

For subscriptions:

```bash
php artisan tinker --execute="dump(\SuppliesSoft\LaravelPaymentsKit\Models\StripeSubscription::latest('updated_at')->take(5)->get(['stripe_subscription_id','status','cancel_at_period_end','updated_at'])->toArray());"
```

For disputes:

```bash
php artisan tinker --execute="dump(\SuppliesSoft\LaravelPaymentsKit\Models\StripeDispute::latest('id')->first()?->toArray());"
```

For refunds:

```bash
php artisan tinker --execute="dump(\SuppliesSoft\LaravelPaymentsKit\Models\StripeRefund::latest('id')->first()?->toArray());"
```

## Important Notes

- `success_url` is not proof of payment. Use signed webhooks.
- `payment_intent.payment_failed` without `invoice` or `subscription` usually cannot update a subscription record.
- `invoice.payment_action_required` is the important subscription/invoice event for customer action required.
- For renewal and cancellation lifecycle tests tied to a real subscription, use Stripe Test Clocks.
