fix(admin): show the real Stripe webhook URL and fix the copy #510

Merged
Claude-Run merged 1 commit from fix/BUNYIP-510-stripe-webhook-url-copy into main 2026-08-11 02:49:17 +02:00
Member

The Webhook endpoints block on /admin/stripe had only a placeholder for the endpoint URL, and the placeholder was missing the /v1 prefix the route actually lives under (webhook::configure mounts /webhooks/stripe inside the /v1 scope). An admin who followed it registered a URL that 404s in Stripe, and every subscription and payment event was silently lost.

The field is now prefilled with {api_public_origin}/v1/webhooks/stripe (still editable, since a deployment can front the API on a different public path), and a caption names BUNYIP_API_PUBLIC_ORIGIN as the source. Config::api_public_origin falls back to the internal api_url, so when the resolved origin is not a public https:// origin the caption renders as a warning naming the variable to set rather than presenting an unreachable Docker hostname as correct. The placeholder gains /v1 too.

The block description now states the direction explicitly ("Receives information from Stripe about checkout, subscription, and payment events") and what bunyip does with them: activate a membership after checkout, keep subscription status and tier in step, grant or revoke entitlements, send payment receipt and failure emails. A status line driven by has_webhook_secret says whether processing is live, including the explicit statement that bunyip rejects every incoming Stripe event until a signing secret is saved, which is what stripe_webhook does (BUNYIP-203). The empty state takes the same direction-explicit phrasing.

create_stripe_webhook already encrypts and stores the signing secret Stripe returns and hot-reloads StripeService, so the "copy this into the Webhook secret field" instruction was stale on both screens. It is gone from the block description and from the success branch of the post-create page, which now says the secret was saved automatically and shows it once as a record. The secret == None branch (endpoint created in the Stripe dashboard) keeps the manual instruction and states that webhooks stay rejected until a secret is saved. The confirmation page moved into a pure webhook_created_page so both branches are unit-tested.

BUNYIP_API_PUBLIC_ORIGIN was read by bunyip-web but passed through by neither compose file, so the caption pointed at a variable that had no effect; both now forward it (prod defaults to unset, dev to the loopback origin that is genuinely public there).

#BUNYIP-510

The Webhook endpoints block on /admin/stripe had only a placeholder for the endpoint URL, and the placeholder was missing the `/v1` prefix the route actually lives under (`webhook::configure` mounts `/webhooks/stripe` inside the `/v1` scope). An admin who followed it registered a URL that 404s in Stripe, and every subscription and payment event was silently lost. The field is now prefilled with `{api_public_origin}/v1/webhooks/stripe` (still editable, since a deployment can front the API on a different public path), and a caption names `BUNYIP_API_PUBLIC_ORIGIN` as the source. `Config::api_public_origin` falls back to the internal `api_url`, so when the resolved origin is not a public https:// origin the caption renders as a warning naming the variable to set rather than presenting an unreachable Docker hostname as correct. The placeholder gains `/v1` too. The block description now states the direction explicitly ("Receives information from Stripe about checkout, subscription, and payment events") and what bunyip does with them: activate a membership after checkout, keep subscription status and tier in step, grant or revoke entitlements, send payment receipt and failure emails. A status line driven by `has_webhook_secret` says whether processing is live, including the explicit statement that bunyip rejects every incoming Stripe event until a signing secret is saved, which is what `stripe_webhook` does (BUNYIP-203). The empty state takes the same direction-explicit phrasing. `create_stripe_webhook` already encrypts and stores the signing secret Stripe returns and hot-reloads `StripeService`, so the "copy this into the Webhook secret field" instruction was stale on both screens. It is gone from the block description and from the success branch of the post-create page, which now says the secret was saved automatically and shows it once as a record. The `secret == None` branch (endpoint created in the Stripe dashboard) keeps the manual instruction and states that webhooks stay rejected until a secret is saved. The confirmation page moved into a pure `webhook_created_page` so both branches are unit-tested. `BUNYIP_API_PUBLIC_ORIGIN` was read by bunyip-web but passed through by neither compose file, so the caption pointed at a variable that had no effect; both now forward it (prod defaults to unset, dev to the loopback origin that is genuinely public there). #BUNYIP-510
fix(admin): show the real Stripe webhook URL and fix the copy
All checks were successful
E2E / PR gate (pull_request) Successful in 22s
Check / fmt + clippy + build + tests (pull_request) Successful in 4m39s
Create release / Create release from merged PR (pull_request) Has been skipped
b0a6a25c92
The Webhook endpoints block on /admin/stripe had only a placeholder for the endpoint URL, and the placeholder was missing the `/v1` prefix the route actually lives under (`webhook::configure` mounts `/webhooks/stripe` inside the `/v1` scope). An admin who followed it registered a URL that 404s in Stripe, and every subscription and payment event was silently lost.

The field is now prefilled with `{api_public_origin}/v1/webhooks/stripe` (still editable, since a deployment can front the API on a different public path), and a caption names `BUNYIP_API_PUBLIC_ORIGIN` as the source. `Config::api_public_origin` falls back to the internal `api_url`, so when the resolved origin is not a public https:// origin the caption renders as a warning naming the variable to set rather than presenting an unreachable Docker hostname as correct. The placeholder gains `/v1` too.

The block description now states the direction explicitly ("Receives information from Stripe about checkout, subscription, and payment events") and what bunyip does with them: activate a membership after checkout, keep subscription status and tier in step, grant or revoke entitlements, send payment receipt and failure emails. A status line driven by `has_webhook_secret` says whether processing is live, including the explicit statement that bunyip rejects every incoming Stripe event until a signing secret is saved, which is what `stripe_webhook` does (BUNYIP-203). The empty state takes the same direction-explicit phrasing.

`create_stripe_webhook` already encrypts and stores the signing secret Stripe returns and hot-reloads `StripeService`, so the "copy this into the Webhook secret field" instruction was stale on both screens. It is gone from the block description and from the success branch of the post-create page, which now says the secret was saved automatically and shows it once as a record. The `secret == None` branch (endpoint created in the Stripe dashboard) keeps the manual instruction and states that webhooks stay rejected until a secret is saved. The confirmation page moved into a pure `webhook_created_page` so both branches are unit-tested.

`BUNYIP_API_PUBLIC_ORIGIN` was read by bunyip-web but passed through by neither compose file, so the caption pointed at a variable that had no effect; both now forward it (prod defaults to unset, dev to the loopback origin that is genuinely public there).

#BUNYIP-510
Claude-Run deleted branch fix/BUNYIP-510-stripe-webhook-url-copy 2026-08-11 02:49:17 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!510
No description provided.