Skip to content

Nueral: The portal and shop behind a rally to Marrakech

£120ktotal sales across the venture

Nearly every driver on Nueral's second rally to Marrakech sent their travel documents through a portal I designed and built, so there was little left to chase. It went live in early March 2026, about five weeks before the rally.

  • Co-⁠founder and sole engineer
  • Aug 2024 to Apr 2026
  • Offline now

£120ktotal sales across the venture

How the Nueral platform fits togetherTwo lanes in one stack. Solid parts ran live: document collection. Dotted parts were built but never took a booking. At the front, a driver, one of about 50 people on each rally. Main bookings went straight to Stripe Payment Links, and a one-off import brought those customers into the backend (step 5). The driver used the customer portal to upload travel documents. Both meet Commerce, the MedusaJS backend, which spans both lanes (step 1). On its built end stand two custom modules: payment plans (step 2) and team booking (step 3). Checkout, built with Stripe Elements on our own site (step 4), calls Commerce through a one-shot guard that fixed the go-live race (step 7). Behind Commerce, passports are stored encrypted in PostgreSQL, and an admin reads them only through a logged export (step 6).CommerceMedusa backendPostgreSQLPassports encryptedAdminLogged exportPayment LinksMain bookingsCustomer portalDriverCheckoutPayment plansTeam bookingRan live: document collectionBuilt: never took a bookingCustomers imported1234567
Each number is a decision below. Press one to read it.Scroll sideways to see all of it
How the Nueral platform fits togetherTwo lanes in one stack. Solid parts ran live: document collection. Dotted parts were built but never took a booking. At the front, a driver, one of about 50 people on each rally. Main bookings went straight to Stripe Payment Links, and a one-off import brought those customers into the backend (step 5). The driver used the customer portal to upload travel documents. Both meet Commerce, the MedusaJS backend, which spans both lanes (step 1). On its built end stand two custom modules: payment plans (step 2) and team booking (step 3). Checkout, built with Stripe Elements on our own site (step 4), calls Commerce through a one-shot guard that fixed the go-live race (step 7). Behind Commerce, passports are stored encrypted in PostgreSQL, and an admin reads them only through a logged export (step 6).CommerceMedusa backendPostgreSQLPassports encryptedAdminLogged exportPayment LinksMain bookingsCustomer portalDriverCheckoutPayment plansTeam bookingRan live: document collectionBuilt: never took a bookingCustomers imported1234567
Solid ran live: document collection. Dotted was built but never took a booking. Numbers match the decisions below.Scroll sideways to see all of it

Diagram. Solid ran live: document collection. Dotted was built but never took a booking. Numbers match the decisions below.

Solid ran live: document collection. Dotted was built but never took a booking. Numbers match the decisions below.

The site is offline, so this page shows a clip from our trailer, my Figma designs with placeholder names and photos, and the storefront running on my machine with demo data. Open the Figma file (opens in a new tab)

01Problem

Nueral ran 10-day car rallies from Wooler in Northumberland to Marrakech, in early April 2025 and 2026, with about fifty people on each. Every driver had to book, pay and send passport details before we left. On the first rally there was no platform. Documents came in over WhatsApp and payments went through Stripe payment links. It worked, but everything around the payment was chased by hand.

I was the only engineer, with a day job, so it had to be something one person could run. The rally left in early April, ready or not. A rally place comes with team members, add-⁠ons and usually a payment plan. And we planned to run other creators' events on it, so a new event is set up in the admin, not in code.

Trailer 0:20

A white Renault Clio with Driving Wooler to Marrakesh on its doors, sliding on a dusty track.
An English lane, a Defender convoy, Le Mans, the Pyrenees, the Gibraltar ferry, a desert track, camels near Marrakech.

02Decisions

  1. Medusa, after a spike against WordPress

    Our earlier competitions site ran on WordPress, so staying there was the obvious choice. In May 2024 I spiked Medusa against it. Anything custom in WordPress meant PHP plugins, and I didn't know PHP well. Medusa gave me carts, orders, customers and an admin in TypeScript, with room for my own modules. In return I had to learn a new framework under time pressure and start from scratch.

  2. Payment plans as a payment provider

    Most drivers wanted to pay in instalments. My first version, in December 2025, was a standalone module beside Medusa's cart. In January I rewrote it as a payment provider in the normal checkout. The deposit is charged up front, and the balance runs on a Stripe subscription schedule that cancels itself after the last instalment. Every call that charges a card or creates a schedule carries an idempotency key, so a retry can't charge anyone twice.

  3. Let a captain pay for the team, or invite each driver

    Rally places were often booked as a team, so I had to choose between one order per person and one order for the whole team. I built both. A captain can pay for everyone or send invite links. A joiner's cart is rebuilt from the captain's choices, so nobody picks the wrong add-⁠ons. It added a lot of moving parts.

  4. Checkout designed in code, from research

    I designed the landing and product pages in Figma, and checkout in code as I built it, because the business didn't leave time for a Figma pass. So I worked from Baymard Institute's checkout research: one column, Apple Pay and Google Pay first on mobile, no forced account. It stays on our own site with Stripe Elements. Stripe's hosted checkout would have been quicker, but it bypasses Medusa's cart, and orders would depend on a webhook arriving in time.

    Team choice, designed. A full and a monthly price for each.

    Team choice, built. One team price, no monthly price.

    Checkout, built. Express pay first, then one column.

    Team choice, designed. A full and a monthly price for each. Team choice, built. One team price, no monthly price. Checkout, built. Express pay first, then one column.
  5. Bring the payment-⁠link customers in

    Bookings for the second rally had already gone through payment links, so the portal's first users had never seen its checkout. I wrote an import that creates their accounts from a spreadsheet and puts each driver in their rally's group, so they could upload documents. Their dashboards looked empty, because their orders lived in Stripe, not Medusa. So I switched off the setup wizard and hid the events view rather than ship something that looked broken.

    Customer portal, built. A driver with one step left before she's ready to go.

    Customer portal, built. A driver with one step left before she's ready to go.
  6. Encrypt passports, and log every export

    Passport numbers and expiry dates are encrypted at rest with AES-256-GCM, each value with its own random IV. Customers see only the last four digits, and only admin routes can decrypt. Exporting a group's documents needs the admin to type a reason, and the access log records who, why and from where. Nobody can search by passport number, but for fifty people that was fine.

  7. Fix the go-⁠live race at its source

    In the go-⁠live week I noticed in the logs that checkout could complete an order twice: from the pay button, and from an effect watching for the payment to be authorised. Stripe's capture webhook raced both. My first fix turned automatic capture off. I reverted it within the hour. That evening I added Medusa's Redis-⁠backed locking and a one-⁠shot guard on every checkout path. The double call started in the storefront, so that's where the guards went.

03Result

Leaving-⁠party tickets went live on the platform, but none sold. They came down after an unexpected event stopped us promoting them. The configurator, team booking and payment plans were built, but none took a booking before the venture concluded in April 2026.

The travel documents form was the smallest part, and the one that mattered most.

Passport form 0:14

The passport part of the travel details form for a demo driver, filled in and saved, the passport number masked to its last four digits, marked secure.
The passport number is masked to its last four digits as it saves. A demo driver types her place of birth, picks the country, types her passport number, picks the expiry date, the form saves and masks the number to its last four digits.

04How I worked

It was just me on design and engineering. My co-⁠founder Dan helped with a few storefront fixes before launch, and started the competition module, which I debugged and finished.

I wrote a short spec before each big piece and built against it. Most of the work happened in evenings and weekends, then in a sprint through February and March 2026. I used AI agents for a good share of the code. I set the direction and made the decisions, and I reviewed and tested everything before it shipped.

  • Next.js
  • Vercel
  • MedusaJS
  • Render
  • PostgreSQL
  • Redis
  • Stripe

05What I'd do differently

We went too wide. As a team we chose to build far more than ran live, including a competition that only existed to support one marketing plan. What ran in the end was onboarding, the part that collected documents. Next time I'd push for agreeing the exact MVP up front, the one job the next rally couldn't do without, and build only that until it was finished and in use, then widen.