b3ed3a67d5a153bd34e2db9f2ad8fed9bc4d4e2c
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c077a653fb
|
Let a studio pick its default payment method and disconnect Stripe
CI / No Debug Code (pull_request) Successful in 5s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / PHPStan (pull_request) Successful in 3m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 5m31s
CI / Build Plugin Zip (pull_request) Skipped
Stripe configuration was a one-way door. Keys could be entered but never removed, and entering them moved every student onto card billing at once, so there was no way to have Stripe live and satisfy yourself that card payments worked before committing the studio to them. Two settings-page changes open both directions: Default payment method (`us_default_payment_method`) is now an explicit choice between card and e-transfer for students with no per-student override, rather than something inferred from whether keys exist. Card remains the default, so a site that adds keys and changes nothing else behaves as before. BillingMethodResolver still degrades a card default to e-transfer while Stripe is unconfigured — there is nothing to charge a card with — and `comp` is deliberately not offerable studio-wide, since it would silently stop billing everybody; an unrecognised stored value reads back as card. Holding the default on e-transfer with Stripe live is the staged-rollout path: move individual students to card on their detail page, watch real charges land, then flip the studio over. Clear Stripe configuration deletes the publishable key, secret key and webhook signing secret and returns the mode to test, so a re-configuration later cannot inherit live. Currency, HST, e-transfer and registration settings are untouched, as are recorded payments. The button only appears when some Stripe value is stored, and reuses the page's existing nonce and `manage_billing` check. `composer test` (915), `composer lint` and `composer cs` all pass. Options only — no schema change, so no version bump. Closes #173 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WyktWmwNRgMYuwe5eBuPZm |
||
|
|
169f7b6a13
|
Accept whole days only for the studio cancellation cutoff
CI / Tests (PHP 8.2) (pull_request) Successful in 38s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m54s
CI / Coding Standards (pull_request) Successful in 3m2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
The Studio Settings cutoff field now takes an integer number of days (step 1, coerced with Val::int) instead of allowing half-day fractions, and displays the stored hours rounded to whole days. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
8b90b8d78d
|
Add cancellation cutoff limiting how close to a lesson a student can cancel
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 2m54s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / Build Plugin Zip (pull_request) Skipped
Students can no longer cancel their own lesson online once it starts within a configured window; instructors and studio admins can always cancel. - Studio default `us_cancellation_cutoff_hours` (stored/computed in hours, entered and displayed in days under Studio Settings → Cancellations). - Optional per-offering override `cancellation_cutoff_hours` (entered in hours); blank inherits the studio default, 0 allows anytime cancellation. - `Booking\CancellationPolicy` resolves the effective window and decides; `BookingEndpoint::cancel()` returns a 403 `cancellation_closed` when too late. The instructor status endpoint and studio-admin student actions bypass it. Closes #93 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
7370755951
|
Add open student registration with email confirmation and approval
CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
Students could previously join by invite only. Add an optional self-approval mode, toggled from Studio Settings → Registration: anyone may sign up on the existing [us_student_register] page, confirm their email via a tokenised link, and then be approved by a studio admin before the account is usable. - Enabling the toggle mirrors WordPress's own membership settings (users_can_register + default_role = us_student) and snapshots their previous values so disabling restores them. - WordPress's native registration form is blocked while open registration is on (login_init redirect + registration_errors fail-safe + register_url) so it cannot bypass signup policy acceptance. - Pending accounts: unconfirmed email cannot log in; confirmed but unapproved can log in but the booking capability is withheld and the booking page shows an "awaiting approval" screen. - Approve/reject from Students → Pending Students; reject hard-deletes the account so the email is freed to re-apply. - Invite registration is unchanged; both modes coexist. Account lifecycle lives in user meta (RegistrationStatus); no new tables. Closes #63 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
061d09e034
|
Harden booking, offering exposure, payments, and invites
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m41s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Build Plugin Zip (pull_request) Has been skipped
Security fixes from a pen-test review (issues #31–#37): - #31 Booking no longer trusts a client-supplied offering_id: a slot-tied offering is authoritative and any offering used must belong to the slot's instructor, closing a free/misrouted-payment bypass. - #34 Availability slot creation rejects an offering the instructor does not own (AvailabilityEndpoint now takes OfferingRepository). - #32 Offering/question/policy listing endpoints now require book_lesson instead of being public (no anonymous consumer exists); Offering::toArray also omits etransfer_email from listings as defense-in-depth. - #33 Slots are claimed atomically (UPDATE ... WHERE is_booked = 0) before a lesson is inserted, preventing a double-booking race. - #35 A single weekly booking is capped (MAX_WEEKLY_OCCURRENCES) and only creates lessons for slots it actually claimed. - #36 Stripe secret/webhook keys are write-only in the settings UI and a blank submit keeps the stored value; secrets are never echoed back into HTML. - #37 Pending invites expire after 14 days (Invite::isAcceptable), enforced at registration and surfaced on the admin invites list. Adds BookingEndpointTest plus Invite/Offering/AvailabilityRepository coverage and minimal WP_REST_Request/WP_REST_Response stubs. composer test (200), lint, and cs all green. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
925a4b79ba
|
Add live Stripe card charges (PaymentIntent + Elements + webhook)
CI / No Debug Code (pull_request) Successful in 40s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m13s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m9s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m8s
CI / Build Plugin Zip (pull_request) Has been skipped
Completes the deferred half of payments: real credit-card processing on top of the existing ledger/e-transfer/comp foundation. - StripeGateway wraps stripe/stripe-php: creates idempotent PaymentIntents (amount in cents, registration ids in metadata) and verifies webhook signatures. Stripe calls sit behind protected seams for unit testing. - PaymentService::createIntent resolves the client-side step for a new registration (card → client secret; e-transfer → display data; comp → none) with caller-ownership enforcement. - PaymentService::handleWebhook finalises a payment exactly once on payment_intent.succeeded (mark paid → confirm → receipt) and marks it failed on payment_intent.payment_failed. - PaymentEndpoint: POST /payments/intent (book_lesson) and public, signature-verified POST /payments/webhook. - PaymentRepository: setStripeIntentId / findByStripeIntentId. - StudioSettings: us_stripe_webhook_secret option, with the webhook URL and required events surfaced on the settings page. - Front end: shared payment.js mounts Stripe Payment Elements and confirms the card (or shows e-transfer instructions); Stripe.js enqueued only when configured. Wired into booking and group-class flows. Tests: new StripeGatewayTest; PaymentService card-intent + webhook cases; repository coverage. composer test/lint/cs all green. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
553cfafa49
|
Add HST/tax support and payment reporting with HST aggregation
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Coding Standards (pull_request) Successful in 1m1s
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / No Debug Code (pull_request) Successful in 4s
CI / PHPStan (pull_request) Successful in 1m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
CI / Build Plugin Zip (pull_request) Has been skipped
Studio Settings gains a default HST rate; the rate is frozen onto each payment at booking and computed against the pre-tax subtotal, with the total billed as subtotal + tax. The rate is overridable per booking on My Lessons while unpaid (recomputing the tax amount), comped registrations are never taxed, and receipts break out subtotal/HST/total. Builds the payments report (roadmap #8) from us_payments: a monthly per-instructor view with subtotal, HST collected, and grand-total aggregation, plus a nonce-protected CSV export via admin-post. Studio admins see all instructors and can filter; instructors are scoped to their own rows. The Payment Report menu is gated on export_payments so instructors (who lack manage_billing) can reach it. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
9873cb5e30
|
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
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]> |
||
|
|
6c4097b385
|
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
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]> |