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.
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)
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 <[email protected]>
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 <[email protected]>
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements the payments foundation (#7).
Pieces
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}).setPaymentId; comp confirms the lesson.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:
us_etransfer_email).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 acardpayment is createdpendingand 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
Add payments foundation (e-transfer/comp, Stripe config, receipts)to Add payments foundation (e-transfer/comp, Stripe config, receipts, e-transfer email)