fix(pricing): publish every mapped tier and name every silent cause #512
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-515-pricing-multi-tier-and-visible-reasons"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
/pricing published only the standard tier:
resolvereadcfg.standard_price_idand nothing else, so an admin who had mapped the lifetime or early-adopter price had "configured pricing" by every visible signal and still got a 404. It now iterates all three mapped tiers (lifetime, early adopter, standard, in that order), emitting a card for each id that resolves to an active price with an amount.enabledstays!tiers.is_empty(), so a fully unmapped install still 404s and the public body shape is unchanged.Five different failures used to collapse into the same silence, only one of which logged anything. Each branch now produces an
UnpublishedReason(switch off, no tier mapped, Stripe not configured, Stripe list failed, and a per-tier price-unresolved carrying archived / no-amount / not-visible-under-app-tag), logs itself once at info for the deliberate states and warn for the misconfigured ones, and is served by the new admin-onlyGET /v1/admin/pricing/status. That endpoint is built from the same resolve the public page runs and bypasses the cache, so the diagnosis cannot drift from the behaviour. The Pricing tiers admin page renders it above the form: a success box naming the published tiers, or one error box per reason, including the app-tag case that makes a dashboard-created product's price invisible tolist_prices.The web tier stopped discarding its errors.
calls::pricingalso decoded the wrong shape:/v1/pricinganswers with a bare body rather than the{success, data, meta}envelope, so the envelope-awareparsefailed every decode andpublic_ctx'sunwrap_or_defaultturned that into "pricing is unpublished" with nothing logged anywhere.parse_barereads the documented public shape, andpublic_ctx,content::pricing, the docs pages, the onboarding gate and the Pricing tiers fetches all log before falling back, with the reason the fallback is correct stated at each site.Cache invalidation now covers every admin action that can change the answer:
update_stripe_config(the secret key and the app tag both decide which prices are visible),create_stripe_priceandarchive_stripe_price, alongside the existingupdate_tier_config. A source-scanning test inadmin_stripe.rsfails the build if a future price-mutating handler (the BUNYIP-511 replace endpoint, say) skips the invalidate.#BUNYIP-515
/pricing published only the standard tier: `resolve` read `cfg.standard_price_id` and nothing else, so an admin who had mapped the lifetime or early-adopter price had "configured pricing" by every visible signal and still got a 404. It now iterates all three mapped tiers (lifetime, early adopter, standard, in that order), emitting a card for each id that resolves to an active price with an amount. `enabled` stays `!tiers.is_empty()`, so a fully unmapped install still 404s and the public body shape is unchanged. Five different failures used to collapse into the same silence, only one of which logged anything. Each branch now produces an `UnpublishedReason` (switch off, no tier mapped, Stripe not configured, Stripe list failed, and a per-tier price-unresolved carrying archived / no-amount / not-visible-under-app-tag), logs itself once at info for the deliberate states and warn for the misconfigured ones, and is served by the new admin-only `GET /v1/admin/pricing/status`. That endpoint is built from the same resolve the public page runs and bypasses the cache, so the diagnosis cannot drift from the behaviour. The Pricing tiers admin page renders it above the form: a success box naming the published tiers, or one error box per reason, including the app-tag case that makes a dashboard-created product's price invisible to `list_prices`. The web tier stopped discarding its errors. `calls::pricing` also decoded the wrong shape: `/v1/pricing` answers with a bare body rather than the `{success, data, meta}` envelope, so the envelope-aware `parse` failed every decode and `public_ctx`'s `unwrap_or_default` turned that into "pricing is unpublished" with nothing logged anywhere. `parse_bare` reads the documented public shape, and `public_ctx`, `content::pricing`, the docs pages, the onboarding gate and the Pricing tiers fetches all log before falling back, with the reason the fallback is correct stated at each site. Cache invalidation now covers every admin action that can change the answer: `update_stripe_config` (the secret key and the app tag both decide which prices are visible), `create_stripe_price` and `archive_stripe_price`, alongside the existing `update_tier_config`. A source-scanning test in `admin_stripe.rs` fails the build if a future price-mutating handler (the BUNYIP-511 replace endpoint, say) skips the invalidate. #BUNYIP-515