CarCheck NG
Vehicle history reports for the Nigerian used-car market — built so the data contracts can be dropped in without touching the product.
- Role
- Architect & sole engineer
The application
Screens, captured from the running app
Captured from the running MVP. The data adapters are placeholders, and the app says so on every surface — including in these screens.




The problem
Nigeria imports a large share of its used cars from the United States. A buyer standing in a lot in Lagos has no practical way to find out whether the car in front of them was in a salvage auction in Texas eighteen months ago — and a foreign report, even if they could buy one, says nothing about local customs status, registration history, or whether the vehicle is flagged with the police here.
So the product is two halves: the US accident/salvage/title history, and the local checks a foreign report structurally cannot supply.
The thing worth being honest about
The app is the easy part. The data is the entire business.
Carvertical, Carfax and AutoCheck are not data creators — they are aggregation-and-reporting layers over licensed feeds. This codebase is that layer, and it was built assuming the licensing conversations happen later and independently.
That shaped the architecture more than any other decision. Every data source sits behind a provider interface, so a live feed replaces a placeholder without the product code knowing the difference.
Mock data that refuses to be mistaken for real data
It is a working MVP: decode, order, payment, reconciliation, report generation, PDF, caching and cost accounting are all real and exercised end to end. Two of the three data adapters currently fabricate records.
Nothing about that is hidden, and hiding it was never an option — a vehicle history report that might be invented is worse than no report at all. A single predicate, usingMockData(), drives four separate disclosures:
- a site-wide banner
- a banner on the report itself
- a badge on each individual mocked source
- a warning block inside the generated PDF
Because they all read from one function, they vanish together the moment real adapters are configured. There is no path where a disclosure is forgotten in one place and left in another.
Payments and reconciliation
Paystack for naira payments, with a webhook receiving settlement events and a separate settle route that reconciles an order against what actually arrived — because a payment webhook you never verify is a payment you don't have. Report generation is cached and cost-accounted per source, since each upstream lookup will eventually carry a real per-call price.
Two auth paths, deliberately separate
Customers get accounts so a report bought once stays retrievable. Staff get a different door entirely: a magic link that expires in fifteen minutes, gating the internal console. Sharing an auth path between customers and staff is how privilege escalation happens quietly.
Stack
Next.js App Router with TypeScript, Prisma over PostgreSQL, React PDF for the downloadable report, Zod at every boundary, Three.js for the landing visual, and Paystack for payments.