Skip to content

In-Progress / Pending Work

An inventory of areas that are not yet production-ready. Use this as the "what's still open" list for the meeting.


Stripe payments

  • Status: Scaffolded, not wired into checkout. No code path actually charges a card today.
  • What exists:
    • Package installed: stripe/stripe-php: ^18.0 in composer.json (Laravel Cashier is NOT installed).
    • Service bindings: StripeClient singleton registered in app/Providers/AppServiceProvider.php (registerStripeClient()), pulling from config('services.stripe.secret').
    • Config keys: STRIPE_KEY / STRIPE_SECRET declared in config/services.php (lines 39-42). Note: these env vars are NOT in .env.example — they need to be added.
    • Service layer: app/Services/PaymentService.php (~300 lines) implements processStripePayment(), createPaymentIntent(), confirmPaymentIntent(), refundPayment(), getPaymentIntent().
    • Data model: app/Models/PaymentIntent.php + database/migrations/2025_07_28_123621_create_payment_intents_table.php with stripe_payment_intent_id, client_secret, status enum, etc.
    • Transaction model: app/Models/Transaction.php has payment_provider, external_transaction_id, gateway_response fields (provider-agnostic).
    • Payment method enum: app/Enums/PaymentMethodEnum.php includes CREDIT_CARD, DEBIT_CARD, HSA_CARD, FSA_CARD, HRA_CARD, PURSE_ONLY, etc.
  • What's missing:
    • No call site invokes PaymentService anywhere — verified via grep across app/ and routes/. The only file referencing the class is the service itself.
    • StoreOrderAction (app/Http/Actions/Member/Order/StoreOrderAction.php) creates an order from cart but never calls a payment step.
    • OrderService::createOrderFromCart() defaults payment_method = CREDIT_CARD / payment_status = PENDING when total > 0, but nothing collects a card or confirms the intent.
    • No checkout HTTP action / route — search for "checkout" turned up nothing in app/Http/ or routes/.
    • No Stripe webhook controller (app/Http/Controllers/Webhooks/ only contains ShipHeroWebhookController.php). Inbound payment_intent.succeeded / charge.refunded events are not handled.
    • No STRIPE_KEY / STRIPE_SECRET / STRIPE_WEBHOOK_SECRET entries in .env.example.
    • Frontend (Stripe Elements / payment-method-id collection) is not wired to a backend endpoint.
    • Saved payment methods / Stripe Customer creation is not implemented (no stripe_customer_id on User or Member).
  • Risk / blocker: The full service layer was built ahead of integration — easy to look "done" in code review but a live checkout still has to be assembled and tested end-to-end. Refund flow has the same gap (refundPayment() exists but no admin/UI hook calls it).

WEX integration

  • Status: Pending — no code yet. Blocked on partnership agreement.
  • What exists:
    • Enum placeholders only: PaymentMethodEnum::HSA_CARD, FSA_CARD, HRA_CARD, PURSE_ONLY in app/Enums/PaymentMethodEnum.php. None of these enum values are referenced by any business logic — grep for HSA_CARD / FSA_CARD / HRA_CARD returns zero hits outside the enum file itself.
    • Internal "purse" system (app/Services/PurseService.php, app/Services/PursePurchaseService.php, Purse model) exists and works against locally-seeded balances — this is the stand-in for what WEX will eventually back.
    • Business decision logged in business-decisions.md (lines 65-71): "WEX is NOT a payment gateway like Stripe or PayPal. WEX is the entire benefits backbone… Pending: WEX partnership agreement is still being finalized."
  • What's missing:
    • No WexService, WexClient, no config/services.php entry, no env vars, no HTTP client, no API contract.
    • No member lookup, balance check, eligibility check, or purse-deduction API calls.
    • No settlement / reconciliation jobs.
    • Purse data is currently seeded locally — there is no sync from WEX.
  • Risk / blocker: Per business-decisions.md, the team does not yet know:
    • Which WEX APIs will be available (member lookup, balance checks, purse deductions).
    • Where purse data will come from once WEX is live (today: seeded in our DB).
    • Settlement terms (how/when Easy OTC gets paid). This work is fully gated on the WEX partnership being finalized — no useful code can be written until the API contract is known.

Email notifications

  • Status: Partially wired. Transactional flows fire, but MAIL_MAILER=log in .env.example means nothing actually leaves the box in the default config; no welcome email exists.
  • What exists (Mailables in app/Mail/, all implement ShouldQueue, all have matching Blade templates in resources/views/emails/):
    • OrderConfirmationMail — dispatched by OrderPlacedListener (queued listener on OrderPlacedEvent). Event is fired from OrderService::createOrderFromCart() and SubscriptionOrderService when a subscription auto-order is generated.
    • OrderAgentNotificationMail — dispatched by OrderPlacedAgentNotificationListener on the same OrderPlacedEvent (notifies assigned agent(s)).
    • SubscriptionConfirmationMail — fired by SubscriptionCreatedListener on SubscriptionCreatedEvent (dispatched from SubscriptionService::create).
    • SubscriptionPausedMail / SubscriptionResumedMail / SubscriptionCancelledMail / SubscriptionCadenceChangedMail — all wired via matching listeners on their respective subscription events fired from SubscriptionService.
    • SubscriptionUpcomingDeliveryMail — sent by SendUpcomingDeliveryRemindersCommand (subscriptions:send-reminders), scheduled in routes/console.php to run dailyAt('09:00').
    • ContactInquiryMail — fired inline by StoreContactInquiryAction (sent to config('mail.to.address')).
    • MailingListSubscriptionConfirmation — fired inline by StoreMailingListAction and MailingListController (duplicate path — see "Risk" below).
    • Password reset: App\Notifications\ResetPasswordNotification (in app/Notifications/) is wired via User::sendPasswordResetNotification() and triggered by SendResetLinkAction (POST /forgot-password) and app/Filament/Actions/SendPasswordResetAction.php.
  • What's missing:
    • No welcome email. No WelcomeMail, no Welcome* Mailable, no Notification fires on member/user registration. StoreMemberAction and the auth login actions do not dispatch any greeting. (A resources/views/welcome.blade.php exists but it's the default Laravel landing page, not an email.)
    • No mail driver configured for production. .env.example sets MAIL_MAILER=log, MAIL_HOST=127.0.0.1, MAIL_PORT=2525 — nothing points at Postmark / Resend / SES even though config/services.php has slots for postmark, resend, and ses. Until a real driver + credentials are set in the production .env, all "wired" emails just append to the log file.
    • No payment-receipt / refund-confirmation / shipping-confirmation emails. (Shipping events come from ShipHero webhooks but no email Mailable is hooked to them.)
    • No order-cancelled / payment-failed customer email.
    • No admin "low inventory" or "new contact inquiry summary" digest.
    • No email verification flow (User model has the standard fields but no MustVerifyEmail contract / verification email is sent).
  • Risk / blocker:
    • Duplicate mailing-list send path: both app/Http/Actions/MailingList/StoreMailingListAction.php and app/Http/Controllers/MailingListController.php send MailingListSubscriptionConfirmation. Whichever route is registered will fire — risk of double-send if both are wired up.
    • Because everything is queued (ShouldQueue + QUEUE_CONNECTION=redis), emails will silently sit in the queue if the worker isn't running. Worth confirming the worker / Horizon is part of the deploy.
    • Default MAIL_FROM_ADDRESS="easyotc@example.com" in .env.example will need to be replaced before any of this hits real inboxes.