Add payments foundation (e-transfer/comp, Stripe config, receipts, e-transfer email) #25

Merged
thatguygriff merged 2 commits from feature/payments into main 2026-06-08 13:49:39 +00:00
Owner

Implements the payments foundation (#7).

  • No Stripe configured → everything works on e-transfer (pending payment confirmed by a studio admin → confirms the booking + emails a receipt).
  • Stripe configured → default credit card.
  • Per-student override (card / e-transfer / comp) on the student detail; comp auto-confirms with no charge.

Pieces

  • Schema: us_payments (amount DECIMAL dollars, method, status, receipt number, stripe intent id, etransfer_email).
  • src/Payment/: Payment, PaymentRepository, StudioSettings, BillingMethodResolver, ReceiptMailer, PaymentService, PaymentController (confirmation queue), PaymentEndpoint (PATCH /payments/{id}).
  • Integration: booking & enrolment create the payment from the offering price; setPaymentId; comp confirms the lesson.
  • Admin: Studio Settings + Payments menus (manage_billing); per-student billing method on the student detail.

E-transfer destination email

Resolved at booking time (offering override → studio default) and frozen onto the payment, so each record keeps where the student was directed. Correctable per booking:

  • Studio-default field on Studio Settings (us_etransfer_email).
  • Per-offering override field on the offering (instructor).
  • My Lessons — instructor edits the e-transfer email per pending lesson payment.
  • Payments queue — studio admin can correct the email at confirmation (student sent it to the wrong place).

Deferred to a follow-up

The live Stripe card charge — PaymentIntent + Stripe.js Elements + webhook + stripe/stripe-php (and Jest for the Stripe JS). Until then a card payment is created pending and confirmed like an e-transfer.

Tests

tests/Unit/Payment/ (VO, repository, resolver, service incl. e-transfer freezing, mailer). composer test (148 tests), composer cs, PHPStan level 6, and no-debug all pass.

Refs #7

🤖 Generated with Claude Code

Implements the **payments foundation** (#7). - **No Stripe configured → everything works on e-transfer** (pending payment confirmed by a studio admin → confirms the booking + emails a receipt). - **Stripe configured → default credit card.** - **Per-student override** (card / e-transfer / **comp**) on the student detail; comp auto-confirms with no charge. ## Pieces - **Schema**: `us_payments` (amount DECIMAL dollars, method, status, receipt number, stripe intent id, **etransfer_email**). - **`src/Payment/`**: `Payment`, `PaymentRepository`, `StudioSettings`, `BillingMethodResolver`, `ReceiptMailer`, `PaymentService`, `PaymentController` (confirmation queue), `PaymentEndpoint` (`PATCH /payments/{id}`). - **Integration**: booking & enrolment create the payment from the offering price; `setPaymentId`; comp confirms the lesson. - **Admin**: **Studio Settings** + **Payments** menus (`manage_billing`); per-student billing method on the student detail. ## E-transfer destination email Resolved at booking time (**offering override → studio default**) and **frozen** onto the payment, so each record keeps where the student was directed. Correctable per booking: - Studio-default field on **Studio Settings** (`us_etransfer_email`). - Per-offering override field on the offering (instructor). - **My Lessons** — instructor edits the e-transfer email per pending lesson payment. - **Payments queue** — studio admin can correct the email at confirmation (student sent it to the wrong place). ## Deferred to a follow-up The live Stripe card charge — PaymentIntent + Stripe.js Elements + webhook + `stripe/stripe-php` (and Jest for the Stripe JS). Until then a `card` payment is created `pending` and confirmed like an e-transfer. ## Tests `tests/Unit/Payment/` (VO, repository, resolver, service incl. e-transfer freezing, mailer). `composer test` (148 tests), `composer cs`, PHPStan level 6, and no-debug all pass. Refs #7 🤖 Generated with [Claude Code](https://claude.com/claude-code)
thatguygriff added 1 commit 2026-06-08 13:24:24 +00:00
Add payments foundation (e-transfer/comp, Stripe config, receipts)
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 1m2s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Build Plugin Zip (pull_request) Has been skipped
6c4097b385
Implements the payments foundation for #7. Without Stripe credentials
everything works on e-transfer (pending payment confirmed by a studio
admin); when Stripe keys are configured the default flips to credit card.
Per-student override (card/etransfer/comp) is set on the student detail.

- Schema: us_payments (amount DECIMAL dollars, method, status, receipt,
  stripe intent id).
- src/Payment/: Payment VO, PaymentRepository, StudioSettings (Stripe
  options + isStripeConfigured + settings page), BillingMethodResolver
  (per-student override; default card if configured else etransfer),
  ReceiptMailer, PaymentService (create at registration, link payment_id,
  comp->paid+confirm, markPaid->confirm+receipt), PaymentController
  (e-transfer confirmation queue), PaymentEndpoint (PATCH /payments/{id}).
- Booking + enrolment create the payment from the offering price; comp
  auto-confirms the lesson; setPaymentId on both repositories.
- Admin: Studio Settings + Payments menus (manage_billing); per-student
  billing method on the student detail page.
- Docs: payments.md + README updated.

Deferred to a follow-up: the live Stripe card charge (PaymentIntent +
Stripe.js Elements + webhook + stripe/stripe-php). Until then a card
payment is created pending and confirmed like an e-transfer.

Tests: tests/Unit/Payment/ (VO, repository, resolver, service, mailer).
composer test (147), cs, and PHPStan level 6 all pass.

Refs #7

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
thatguygriff added 1 commit 2026-06-08 13:47:08 +00:00
Add e-transfer destination email (studio default + offering/booking overrides)
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 46s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / PHPStan (pull_request) Successful in 1m12s
CI / Build Plugin Zip (pull_request) Has been skipped
9873cb5e30
The e-transfer destination is resolved at booking time (offering override ->
studio default) and frozen onto the payment, so each record keeps where the
student was directed. It can then be corrected per booking.

- StudioSettings: us_etransfer_email option + a Default e-transfer email field
  on the Studio Settings page.
- Offering: etransfer_email column/field (instructor override) across VO, repo,
  REST endpoint, admin controller, and form.
- Payment: etransfer_email column on the payment (frozen record) +
  PaymentRepository::updateEtransferEmail; PaymentService freezes it from the
  offering override or studio default at creation; booking/enrolment pass the
  offering override.
- My Lessons: instructors edit the e-transfer email per pending lesson payment
  (ownership-checked).
- Payments queue: studio admin can correct the email at confirmation (for when
  a student sends it to the wrong place).
- Docs updated.

Tests: Payment/Offering rows + PaymentService freezing. composer test (148),
cs, and PHPStan level 6 all pass.

Refs #7

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
thatguygriff changed title from Add payments foundation (e-transfer/comp, Stripe config, receipts) to Add payments foundation (e-transfer/comp, Stripe config, receipts, e-transfer email) 2026-06-08 13:48:48 +00:00
thatguygriff merged commit b73d81421f into main 2026-06-08 13:49:39 +00:00
thatguygriff deleted branch feature/payments 2026-06-08 13:49:39 +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#25