Commit Graph

2 Commits (cfc4985f8934c8be676a1de32ba42c7345215a01)

Author SHA1 Message Date
nexxo 507c976035 Pin the one question that must never throw
isConfigured() had no coverage at all: replacing its body with main's
`filled($this->secret())` left all 1795 tests green, while at runtime the
same mutation throws StripeNotConfigured. Six callers ask that question
precisely to avoid an exception — CheckoutController:87 would answer a
customer with a 500 instead of a sentence.

The merge makes it urgent rather than merely untidy: git auto-merges
HttpStripeClient.php without a conflict marker, so nothing forces anyone
to look at the one line where the two branches disagree.

Proved red by that exact mutation before it went in, then reverted:

  ⨯ it answers the configured question instead of throwing it
    StripeNotConfigured: No Stripe secret is stored for the [test] operating mode.
    at app/Services/Stripe/HttpStripeClient.php:376

Http::fake() + assertNothingSent(): the question is asked on every
checkout and may only read the vault.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:58:22 +02:00
nexxo 32a8bd9a88 Refuse the sale instead of reaching Stripe without a key
Task 5: Billing::purchase() now checks SecretVault for a stripe.secret before
touching anything else — no customer, no plan lookup, no Order row — and the
customer reads a plain sentence instead of a 500 page. HttpStripeClient's own
secret() throws the same StripeNotConfigured exception rather than sending a
request with an empty bearer token and letting Stripe's 401 stand in for the
real cause.

isConfigured() was fixed alongside it: it used to call secret() too, which
would have turned every "is Stripe set up?" check (the checkout controller,
the catalogue sync commands) into an uncaught exception the moment a key went
missing — the opposite of what this task is for. It now reads the vault
directly.

The new guard is unconditional, including for the cart-only purchase types
(storage, addon, plan changes) that never call Stripe themselves — a cart
order nobody can ever pay for is pointless to create. That broke every
existing Billing/Cart/Downgrade test that calls purchase() without a stored
key; each affected fixture now deposits one via the new withStripeSecret()
Pest helper. A few of those tests (CustomDomainAccessTest, DowngradeTest) were
passing already but for the wrong reason — the new guard, not the check they
were named for — since both only assert an order was NOT created; they get
the same fixture fix so they still prove what they claim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 11:58:01 +02:00