Project 12 // _ultratecheffect
UltraTechEffect
Posting a job is free. The bill arrives one applicant at a time — which means the card has to still work months after anyone typed it in.
client
private client — confirm before publishing
timeline
4 months · Mar 2022 → Jul 2022 · client-owned, no longer running
role
full-stack developer · client work
scope
## the-problem
Job boards charge for the listing, which is the one thing that costs nothing. An employer pays up front and finds out afterwards whether anybody applied. This one inverted it: post as many jobs as you like for free, and pay per applicant received — the price attaches to the outcome instead of the shelf space.
That inversion moves the hard part into the payment layer. At the moment of billing there is no employer present: a candidate on the other side of the world hits apply, and a card that was typed in weeks ago has to be charged with nobody there to confirm it. Card-on-file, off-session, no UI to fall back on.
The second product made it a two-sided problem in a different sense. The same site also sells career coaching, which is an ordinary attended checkout — one price, one card, one buyer watching the spinner. Two payment shapes, opposite in almost every respect, on one account model.
## the-approach
- ▸split the card journey in two: a Stripe SetupIntent at post-job time authorises the card without taking a cent, and the payment method id is stored on the employer alongside their Stripe customer id
- ▸put the charge where the value is — the apply handler creates an off-session, auto-confirming PaymentIntent against that saved card, so the invoice is generated by the applicant rather than by a calendar
- ▸kept the coaching side conventional: an attended PaymentIntent, its client secret returned to the browser, confirmed against Stripe Elements with the buyer watching
- ▸stored each job's address as a GeoJSON point at save time, so "jobs near here" is a radius query rather than a string match on a city name
- ▸layered the facets on top — employment type, seniority and salary band — over the same query the map and the list both read
- ▸gave employers a portal built from their own records: posts, applications and views bucketed by day over a rolling week
- ▸transformed uploads on the way to S3 — full-size and a 400px thumbnail written in one pass, so no resize job runs later
- ▸provisioned the whole box in Terraform — VPC, subnet, gateway, security group, EC2 — with cloud-init installing nginx, certbot, Node and pm2, and a self-hosted Actions runner doing the redeploy
// the two halves of the card — authorised with a buyer, charged without one
// Post-job. The employer is present, and nothing is charged:// a SetupIntent just proves the card is real and stores it.export const saveCard = async (req, res) => {const user = await User.findById(req.userid);const intent = await stripe.setupIntents.create({customer: user.employerProfile.payment.customerId,payment_method_types: ["card"],});res.send(intent.client_secret); // confirmed in the browser};// Weeks later. A candidate hits apply — the employer isn't here,// there's no page to challenge on, so it has to go through// unattended or not at all.export const applyToJob = async (req, res) => {const job = await Job.findById(req.params.id);job.appointments.push({ userData, schedule, resume, appliedBy });await job.save();res.send("Applied");if (job.isAdminJob) return; // house listings bill nobodyconst employer = await User.findById(job.postedBy);chargeCustomer(19,employer.employerProfile.payment.customerId,employer.employerProfile.payment.activeCard,);};const chargeCustomer = (amount, customer, payment_method) =>stripe.paymentIntents.create({amount: amount * 100, currency: "usd",customer, payment_method,off_session: true, confirm: true, // nobody to prompt});
## the-architecture
// architecture — the card is authorised on one path and charged on another
employer
post · portal
- posts free, saves a card
- weekly posts and applications
candidate
search · apply
- radius search plus facets
- profile, résumé, apply
admin console
jobs · coaches · pricing
- users, bookings, blog
- package tiers
Express API
on a custom Next server
- jobs, users, coaches, packages
- JWT, optional on public reads
billing split
setup vs charge
- SetupIntent when posting
- off-session charge on apply
MongoDB
jobs · users · coaches
- jobs as GeoJSON points
- applications on the job record
media
AWS S3
- résumés and avatars
- full-size + 400px thumbnail
Stripe
card on file
- customer + payment method stored
- attended checkout for coaching
SendGrid
- resets and enterprise enquiries
the box itself
Terraform · EC2
- VPC, subnet, gateway, SG
- cloud-init: nginx, certbot, pm2
deploy
self-hosted runner
- push to main
- pm2 stop, build, restart
## the-results
$0
to post; the bill starts at the first applicant
2
payment shapes, one account model
1 push
from main to a Terraform-provisioned box
- ✓billing follows applicants, not listings — an employer with no response pays nothing, and the charge is raised by the apply handler itself
- ✓one account model carries both an unattended card-on-file charge and an ordinary attended checkout
- ✓"near me" is a real geo query: points in, radius out, with the facets applied over the same result set
- ✓the environment is reproducible — the VPC and instance are Terraform, the box's setup is cloud-init, and a push to main redeploys it
- ✓what I'd change, and it's the same flaw three times over: that unattended charge is fire-and-forget. It isn't awaited, its rejection is only logged, no record of it is written against the application, and it carries no idempotency key — so a card that fails off-session (which is the normal outcome when the issuer wants 3-D Secure) loses the money silently, and a retried apply request bills twice. It belongs in a charges collection written in the same step as the application, keyed on the application id, with a Stripe webhook reconciling the outcome and a retry path that emails the employer to re-authorise.
- ✓and the amount is hardcoded rather than read from the employer's package, so the tier the pricing page sells isn't the tier that bills
// want something like this built for you?
start-a-project