Charge weekly reservations for every claimed occurrence and confirm the whole series #81

Merged
thatguygriff merged 1 commits from fix/recurring-payment-amount into main 2026-07-22 13:26:16 +00:00
Owner

Closes #79

What & why

From the user demo: booking a recurring weekly lesson only required payment for the first week. Two related defects:

  1. AmountBookingEndpoint::book() claimed up to 12 weekly occurrences but created the payment with $offering->price once. For a per-lesson-priced offering (billing_mode = one_time) the student paid one week while holding the whole term. It now charges price × the occurrences actually claimed; a full_term price is still charged once, since that price already covers the term. The Stripe intent, e-transfer display amount, and HST all follow automatically because they derive from the payment row.
  2. Confirmation — settling the payment (Stripe webhook, e-transfer confirmation, comp) confirmed only the anchor lesson; the rest of the series stayed pending forever. PaymentService::confirmRegistration() now resolves the anchor's series_id and confirms every non-cancelled lesson in the series via a new BookingRepository::updateStatusForSeries().

Tests

  • BookingEndpointTest: weekly booking charges price × claimed occurrences (one_time), and a full_term price exactly once.
  • PaymentServiceTest: markPaid confirms the whole weekly series, not just the anchor.
  • BookingRepositoryTest: updateStatusForSeries SQL guards (valid status, skips cancelled rows).
  • composer test — 370 tests pass; composer lint (PHPStan level 10) and composer cs clean.

Docs updated: docs/features/lesson-booking.md, docs/features/payments.md.

🤖 Generated with Claude Code

Closes #79 ## What & why From the user demo: booking a recurring weekly lesson only required payment for the first week. Two related defects: 1. **Amount** — `BookingEndpoint::book()` claimed up to 12 weekly occurrences but created the payment with `$offering->price` once. For a per-lesson-priced offering (`billing_mode = one_time`) the student paid one week while holding the whole term. It now charges **price × the occurrences actually claimed**; a `full_term` price is still charged once, since that price already covers the term. The Stripe intent, e-transfer display amount, and HST all follow automatically because they derive from the payment row. 2. **Confirmation** — settling the payment (Stripe webhook, e-transfer confirmation, comp) confirmed only the anchor lesson; the rest of the series stayed `pending` forever. `PaymentService::confirmRegistration()` now resolves the anchor's `series_id` and confirms **every non-cancelled lesson in the series** via a new `BookingRepository::updateStatusForSeries()`. ## Tests - `BookingEndpointTest`: weekly booking charges price × claimed occurrences (`one_time`), and a `full_term` price exactly once. - `PaymentServiceTest`: `markPaid` confirms the whole weekly series, not just the anchor. - `BookingRepositoryTest`: `updateStatusForSeries` SQL guards (valid status, skips cancelled rows). - `composer test` — 370 tests pass; `composer lint` (PHPStan level 10) and `composer cs` clean. Docs updated: `docs/features/lesson-booking.md`, `docs/features/payments.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
thatguygriff added 1 commit 2026-07-22 13:22:22 +00:00
Charge weekly reservations for every claimed occurrence and confirm the whole series
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m50s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Tests (PHP 8.2) (pull_request) Successful in 39s
CI / PHPStan (pull_request) Successful in 2m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / Build Plugin Zip (pull_request) Skipped
ff059909a5
A weekly booking on a per-lesson (one_time) priced offering was creating its
single upfront payment for one week's price while reserving up to 12 weeks,
and settling that payment confirmed only the anchor lesson, leaving the rest
of the series pending forever.

- BookingEndpoint now charges price x claimed occurrences for one_time
  billing; a full_term price is still charged once since it covers the term.
- PaymentService::confirmRegistration resolves the anchor lesson's series and
  confirms every non-cancelled row via the new
  BookingRepository::updateStatusForSeries().

Closes #79

Co-Authored-By: Claude Fable 5 <[email protected]>
thatguygriff merged commit 4c2cc31c34 into main 2026-07-22 13:26:16 +00:00
thatguygriff deleted branch fix/recurring-payment-amount 2026-07-22 13:26:16 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Unsupervised/unsupervised-scheduler#81