isProduction() || ! str_starts_with((string) config('cashier.secret'), 'sk_test')) { $this->error('Refusing to run: this command requires Stripe test keys and a non-production environment.'); return self::FAILURE; } config(['subscriptions.tax_rates' => []]); $passed = true; $check = function (string $label, bool $ok) use (&$passed): void { $this->line(($ok ? 'PASS' : 'FAIL').' '.$label); $passed = $passed && $ok; }; $lookup = config('subscriptions.plans.monthly.stripe_lookup_key'); $prices = Cashier::stripe()->prices->all(['lookup_keys' => [$lookup], 'limit' => 1]); $priceId = $prices->data[0]->id ?? null; if ($priceId === null) { $this->error("No Stripe price found for lookup key '{$lookup}'."); return self::FAILURE; } $user = User::factory()->create([ 'email' => 'refund-sandbox-'.uniqid().'@whisper.test', 'created_at' => now(), ]); Feature::for($user)->activate(SubscriptionExperiment::class, SubscriptionExperiment::PAY_NOW); try { $user->newSubscription('default', $priceId)->create('pm_card_visa'); $subscription = $user->subscription('default'); $check('subscription active after immediate charge', $subscription->active() && $subscription->stripe_status === 'active'); $check('canSelfRefund is true before refund', $this->offer->canSelfRefund($user)); $paymentIntentId = $subscription->latestPayment()?->asStripePaymentIntent()->id; $check('latestPayment() resolves a payment intent', $paymentIntentId !== null); app(RefundSelfServe::class)->handle($user->fresh()); $subscription = $user->subscription('default')->fresh(); $check('refunded_at is stamped', $subscription->refunded_at !== null); $check('subscription is canceled', $subscription->canceled()); $check('canSelfRefund is false after refund', ! $this->offer->canSelfRefund($user->fresh())); $intent = Cashier::stripe()->paymentIntents->retrieve($paymentIntentId, ['expand' => ['latest_charge']]); $charge = $intent->latest_charge; $check('Stripe charge shows a full refund', is_object($charge) && $charge->refunded === true); $this->line(' amount_refunded='.($charge->amount_refunded ?? 'n/a')); } catch (\Throwable $exception) { $this->error($exception::class.': '.$exception->getMessage()); $passed = false; } finally { try { if ($user->hasStripeId()) { Cashier::stripe()->customers->delete($user->stripe_id); } } catch (\Throwable) { // best-effort sandbox cleanup } $user->forceDelete(); } $this->newLine(); $this->{$passed ? 'info' : 'error'}($passed ? 'Refund flow verified.' : 'Refund flow verification FAILED.'); return $passed ? self::SUCCESS : self::FAILURE; } }