Fix Stripe SDK v15 webhook crash + add webhook Prometheus metrics #350

Closed
opened 2026-04-06 15:31:04 +00:00 by forgejo_admin · 2 comments

Type

Bug

Lineage

Root cause of all webhook delivery failures since basketball-api image was rebuilt with Stripe SDK v15.0.1. Discovered via end-to-end test on 2026-04-06. Related to forgejo_admin/basketball-api#343, #346. Supersedes forgejo_admin/basketball-api#719 (same root cause).

Repo

forgejo_admin/basketball-api

What Broke

Stripe SDK v15.0.1 removed .get() from StripeObject. The webhook handler at src/basketball_api/routes/webhooks.py uses .get() in 23 places on Stripe event data (2 additional .get() calls are on Python dicts/SQLAlchemy and are fine). Every webhook delivery crashes with AttributeError: get at line 301+ before updating the database. Payment succeeds on Stripe's side, but the DB never updates — jersey orders stuck at pending.

Confirmed via live $1 test payment on 2026-04-06: signature verified (new secret works), handler crashed, DB unchanged, Stripe got 500 back.

Root cause: pyproject.toml pins stripe>=11.0 with no upper bound. Docker image build resolved to 15.0.1. Local dev has 14.4.1 (works). Deployed pod has 15.0.1 (crashes).

Repro Steps

  1. Complete a Stripe checkout (any product)
  2. Stripe delivers checkout.session.completed webhook
  3. Handler verifies signature successfully
  4. data_object.get("metadata", {}) on line 301 throws AttributeError: get
  5. Handler returns 500, DB not updated

Expected Behavior

Webhook handler processes checkout.session.completed events without crashing. Player jersey records update automatically (option, size, number, status=paid). /metrics endpoint exposes webhook counters for platform alerting.

Environment

  • Cluster/namespace: prod / basketball-api
  • Deployed Stripe SDK: 15.0.1
  • Local Stripe SDK: 14.4.1
  • Pin in pyproject.toml: stripe>=11.0 (no upper bound)

Acceptance Criteria

  • Stripe SDK pinned stripe>=11.0,<15 in pyproject.toml
  • All 23 .get() calls on Stripe objects in src/basketball_api/routes/webhooks.py replaced with bracket notation or dict() wrapping
  • Live webhook: checkout.session.completed processes without crash, DB updates
  • prometheus_client added to pyproject.toml; /metrics exposes webhook_received_total, webhook_processed_total, webhook_errors_total (by event_type), webhook_last_received_timestamp
  • No new test failures introduced
  • project-westside-basketball — project this affects
  • forgejo_admin/basketball-api#340 — original user report
  • forgejo_admin/basketball-api#343, #346 — earlier investigation
  • forgejo_admin/basketball-api#719 — superseded (same root cause)
  • Blocks: forgejo_admin/pal-e-platform#272 (alerting depends on these metrics)
  • Key files: src/basketball_api/routes/webhooks.py (23 .get() calls), pyproject.toml (Stripe pin + prometheus_client dep), src/basketball_api/routes/health.py or metrics module (Prometheus counters)
### Type Bug ### Lineage Root cause of all webhook delivery failures since basketball-api image was rebuilt with Stripe SDK v15.0.1. Discovered via end-to-end test on 2026-04-06. Related to forgejo_admin/basketball-api#343, #346. Supersedes forgejo_admin/basketball-api#719 (same root cause). ### Repo `forgejo_admin/basketball-api` ### What Broke Stripe SDK v15.0.1 removed `.get()` from `StripeObject`. The webhook handler at `src/basketball_api/routes/webhooks.py` uses `.get()` in 23 places on Stripe event data (2 additional `.get()` calls are on Python dicts/SQLAlchemy and are fine). Every webhook delivery crashes with `AttributeError: get` at line 301+ before updating the database. Payment succeeds on Stripe's side, but the DB never updates — jersey orders stuck at `pending`. Confirmed via live $1 test payment on 2026-04-06: signature verified (new secret works), handler crashed, DB unchanged, Stripe got 500 back. Root cause: `pyproject.toml` pins `stripe>=11.0` with no upper bound. Docker image build resolved to `15.0.1`. Local dev has `14.4.1` (works). Deployed pod has `15.0.1` (crashes). ### Repro Steps 1. Complete a Stripe checkout (any product) 2. Stripe delivers `checkout.session.completed` webhook 3. Handler verifies signature successfully 4. `data_object.get("metadata", {})` on line 301 throws `AttributeError: get` 5. Handler returns 500, DB not updated ### Expected Behavior Webhook handler processes `checkout.session.completed` events without crashing. Player jersey records update automatically (option, size, number, status=paid). `/metrics` endpoint exposes webhook counters for platform alerting. ### Environment - Cluster/namespace: prod / basketball-api - Deployed Stripe SDK: `15.0.1` - Local Stripe SDK: `14.4.1` - Pin in pyproject.toml: `stripe>=11.0` (no upper bound) ### Acceptance Criteria - [ ] Stripe SDK pinned `stripe>=11.0,<15` in `pyproject.toml` - [ ] All 23 `.get()` calls on Stripe objects in `src/basketball_api/routes/webhooks.py` replaced with bracket notation or `dict()` wrapping - [ ] Live webhook: `checkout.session.completed` processes without crash, DB updates - [ ] `prometheus_client` added to `pyproject.toml`; `/metrics` exposes `webhook_received_total`, `webhook_processed_total`, `webhook_errors_total` (by event_type), `webhook_last_received_timestamp` - [ ] No new test failures introduced ### Related - `project-westside-basketball` — project this affects - `forgejo_admin/basketball-api#340` — original user report - `forgejo_admin/basketball-api#343`, #346 — earlier investigation - `forgejo_admin/basketball-api#719` — superseded (same root cause) - Blocks: `forgejo_admin/pal-e-platform#272` (alerting depends on these metrics) - Key files: `src/basketball_api/routes/webhooks.py` (23 `.get()` calls), `pyproject.toml` (Stripe pin + prometheus_client dep), `src/basketball_api/routes/health.py` or metrics module (Prometheus counters)
Author
Owner

Scope Review: NEEDS_REFINEMENT

Review note: review-856-2026-04-04
Template is complete and well-written. Five body-level fixes needed before dispatch:

  • Fix file path to repo-root relative (src/basketball_api/routes/webhooks.py), add health.py as target
  • Correct .get() count from 25 to 23 (2 are db.get/dict.get)
  • Add prometheus_client dependency to AC 4 scope
  • Clarify overlap with #719 (likely same root cause -- duplicate or supersedes?)
  • Reword AC 5 to "No new test failures" (pre-existing failures in #731/#733)
  • [SCOPE] Architecture note arch-basketball-api does not exist in pal-e-docs -- needs creation
## Scope Review: NEEDS_REFINEMENT Review note: `review-856-2026-04-04` Template is complete and well-written. Five body-level fixes needed before dispatch: - Fix file path to repo-root relative (`src/basketball_api/routes/webhooks.py`), add `health.py` as target - Correct .get() count from 25 to 23 (2 are db.get/dict.get) - Add `prometheus_client` dependency to AC 4 scope - Clarify overlap with #719 (likely same root cause -- duplicate or supersedes?) - Reword AC 5 to "No new test failures" (pre-existing failures in #731/#733) - [SCOPE] Architecture note `arch-basketball-api` does not exist in pal-e-docs -- needs creation
Author
Owner

Scope Review: READY

Review note: review-856-2026-04-04-r2
Re-review after refinement. All 5 BODY fixes from previous review confirmed addressed: repo-root file paths, .get() count corrected to 23, prometheus_client in AC, #719 superseded in Lineage, AC 5 reworded.

  • [SCOPE] Architecture note arch-basketball-api still needs creation (carried forward, does not block dispatch)
## Scope Review: READY Review note: `review-856-2026-04-04-r2` Re-review after refinement. All 5 BODY fixes from previous review confirmed addressed: repo-root file paths, .get() count corrected to 23, prometheus_client in AC, #719 superseded in Lineage, AC 5 reworded. - [SCOPE] Architecture note `arch-basketball-api` still needs creation (carried forward, does not block dispatch)
forgejo_admin 2026-04-06 16:09:20 +00:00
Sign in to join this conversation.
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
forgejo_admin/basketball-api#350
No description provided.