Solar_Axis1: SolarAxis: The Marketplace That Only Charges When the Expert Shows Up

A PERN stack solar platform uses manual Stripe capture, private Daily.co rooms, and service-layer orchestration to turn a consultation into a trust contract.

8 min read • View on GitHub • More from WajahatNazir01

A solar customer and an expert stand on opposite sides of a glass barrier made from a payment authorization slip. A card is held near an authorized hold receipt on one side, while a private video room opens only when the expert steps through a marked start gate on the other. The image explains how SolarAxis turns payment timing into a trust mechanism.
SolarAxis does not treat billing as a checkout step. It makes payment contingent on the consultation actually starting, so the product enforces the promise it makes.
Key Takeaways

The clever part is not solar. It is the billing rule.

Most marketplaces charge when the booking is made. SolarAxis does the opposite: it authorizes the card first, then waits for the expert to actually join before it captures the payment. That one choice changes the whole product from a scheduling app into a trust system.

That matters because the platform is selling a promise, not just a call. The customer does not have to wonder whether they paid for a no-show, and the expert does not have to fight for manual invoicing after the fact. The code makes the fairness rule explicit.

How the trust loop works end to end

The key mechanism is not a payment API or a video API on its own. It is the way SolarAxis waits for a real event to resolve the financial hold.

requestConsultation() -> create PaymentIntent (capture_method: 'manual')
                     -> create consultation record (pending)
                     -> create private Daily room

admin joins room
  -> startConsultation()
  -> stripe.paymentIntents.capture(id)
  -> consultation becomes active

no-show / timeout
  -> cancelConsultation()
  -> stripe.paymentIntents.cancel(id)
  -> hold is released

The sequence is simple, but the timing is doing the heavy lifting. Stripe is not used as a checkout endpoint. It is used as a conditional guarantee that only resolves when the consultation becomes real.

Three interlocked mechanical parts sit on a workbench: a payment authorization tag, a private room key, and a consultation record card. The authorization tag is held in a spring slot, the room key turns only when an admin hand enters, and the record flips from pending to captured when the mechanism completes. The image explains how payment, identity, and session state are synchronized.
This is the platform’s real work: three separate systems resolving one business event without drifting out of sync.

The system is split by responsibility, not by framework fashion.

The backend looks mature because it refuses to let one layer do everything. Controllers handle HTTP concerns. Services own the Stripe and Daily orchestration. Prisma handles typed persistence. Middleware protects the sensitive paths.

That separation is not cosmetic. It keeps the cancellation path, the capture path, and the room creation path from collapsing into one large route handler. For a workflow with money and live presence in the loop, that discipline matters.

PatternWhat happensWhy it matters
Fat controllerHTTP, payment, video, and database logic live in one route handler.Hard to test, harder to recover when one step fails.
Service layer separationController delegates to focused services for Stripe, Daily.co, and Prisma.Each business step is isolated, readable, and easier to retry or cancel.
Middleware guardsAdmin-only actions are blocked before the service layer runs.Sensitive operations stay constrained to the right role.

This is the kind of structure that makes a prototype feel like a system. It gives the repo a backbone, not just a working demo.

Daily rooms turn a consultation into a private event

The video layer is not just embedded. SolarAxis creates a private room per consultation and mints different tokens depending on whether the participant is an admin or a customer. That makes identity part of the call itself, not something bolted on around it.

The frontend then manages the room lifecycle, including cleanup when the session ends. That sounds small until you realize it prevents orphaned calls, stale rooms, and ambiguous session states. Video becomes a managed event with a beginning and an end.

The data model is stricter than a generic lead form.

A lot of service apps treat intake as one blob of form data. SolarAxis does not. It separates request types into dedicated models such as installation, maintenance, consultation, and upgradation, then connects them through a central Request record.

That design keeps the system typed around real business categories. It also avoids the usual mess where a single table slowly turns into an unstructured bucket of optional fields that nobody trusts six months later.

Generic intake formSolarAxis model
One flexible form with many optional fieldsDedicated models for each request type
Hard to validate by request kindValidation can follow the service being requested
Loose downstream reportingCleaner analytics and clearer operational handling
Easy to start, hard to scaleStricter up front, easier to evolve

Why this stack feels mature even as an MVP

The version numbers are not the point, even if they signal modern tooling. Express 5, React 19, Tailwind 4, Prisma migrations, and asyncHandler together say something more useful: this repo is organized like an operational product, not a page bundle.

It has the signs of a builder who thinks in lifecycle, failure modes, and boundaries. The interesting part is not that the app can book a solar consultation. It is that the app knows what must happen before money changes hands.