How Spot My Mac Is Built

An open-source guide to the architecture, hosting, database, payments, deployment and security behind Spot My Mac.

1. Architecture Overview

Spot My Mac is built using a modern, serverless stack. It requires no traditional servers to maintain, scaling infinitely via edge networks and serverless database providers.

GitHub ↓ Vercel ↓ Frontend + Serverless API ↓ Supabase ↓ Auction / Bids / Payment status ↓ Dodo Payments ↓ Payment webhook ↓ Supabase
  • GitHub: Hosts the source code and acts as the source of truth for deployments.
  • Vercel: Hosts the static frontend assets and executes the backend serverless API routes on demand.
  • Supabase: A Postgres database that securely stores all persistent data (spots, bids, payments).
  • Dodo Payments: Handles secure checkout and financial transactions.

2. GitHub — Source Code

The repository contains both the frontend (HTML/CSS/JS) and the backend API routes (in the /api folder). Changes are committed and pushed directly to GitHub.

Because Vercel is connected directly to the repository, pushing to the configured branch (e.g., main) automatically triggers a new deployment.

SECURITY WARNING: The repository should never contain .env files, API keys, webhook secrets, Supabase service-role keys, or any other credentials.

VIEW SOURCE CODE

3. Vercel — Hosting & Deployment

Vercel is a platform for frontend frameworks and static sites, built to integrate with headless content, commerce, or database systems. Here is how to deploy it:

  1. Step 1: Create a Vercel account.
  2. Step 2: Import the GitHub repository into Vercel.
  3. Step 3: Select the repository's root directory as the framework preset (it is a plain HTML/Node project, Vercel will automatically detect the api/ routes).
  4. Step 4: Deploy the project.
  5. Step 5: Add environment variables under: Vercel → Project → Settings → Environment Variables.

Environment variables in Vercel can be scoped to different environments:

  • Development: Used when running the project locally via vercel dev.
  • Preview: Used for deployments generated from pull requests or non-production branches.
  • Production: The live values used on your main domain.

Vercel provides a free .vercel.app deployment URL immediately. A custom domain (such as spotmymac.com) can be connected to the project via the Domains tab. Note that after making any changes to environment variables, the project requires a new deployment (redeployment) for the new values to take effect.

4. Supabase — Database

Supabase provides the Postgres database for the application. You must create a Supabase project and create the required database tables.

The backend communicates with Supabase predominantly using the SUPABASE_SERVICE_ROLE_KEY in serverless API routes to bypass Row Level Security (RLS) restrictions safely. Row Level Security (RLS) should be enabled on all tables. Since operations like calculating deposits or updating payments happen server-side, no sensitive credentials are exposed to the browser client.

Database Schema Overview

Table Purpose What it stores
spots Auction inventory The individual sponsorship positions. In the code, this acts as the relational base for joining bids via foreign keys.
bids Auction activity Sponsor bids, brand details, submitted logos, and associated bid/payment status information.
auction_settings Auction configuration Global configuration such as the auction start and end times.
visitors Live visitor tracking Anonymous session IDs and last active timestamps to power the live visitor counter.

Table Structures & Implementations

If you are deploying your own instance, you will need to create the following exact schemas, as they map directly to the API logic in the /api directory.

1. spots

Queried indirectly via the backend /api/getSpots. The frontend codebase actually hardcodes the spot visual metadata (dimensions, sizes) and merges it with database state based on the id. The only required column is id.

CREATE TABLE spots ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY ); -- Insert spots with IDs 1 through 11 to match the frontend array.

2. bids

This is the core table where all bids are inserted by /api/submitBid and updated by /api/webhook using the Service Role Key.

CREATE TABLE bids ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), spot_id BIGINT REFERENCES spots(id), brand_name TEXT NOT NULL, email TEXT NOT NULL, website TEXT, x_handle TEXT, logo_url TEXT, bid_amount NUMERIC NOT NULL, deposit_amount NUMERIC NOT NULL, payment_status TEXT DEFAULT 'pending', -- 'pending' | 'paid' status TEXT DEFAULT 'pending', -- 'pending' | 'review' | 'approved' checkout_id TEXT, created_at TIMESTAMPTZ DEFAULT now() );

3. auction_settings

Queried via /api/getSettings using the public SUPABASE_ANON_KEY. Because it uses the anonymous key, this table requires an RLS policy allowing public SELECT.

CREATE TABLE auction_settings ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, start_at TIMESTAMPTZ, end_at TIMESTAMPTZ ); -- Contains a single row providing global timing.

4. visitors

Upserted dynamically by the /api/analytics serverless heartbeat using the Service Role Key.

CREATE TABLE visitors ( session_id UUID PRIMARY KEY, last_active_at TIMESTAMPTZ DEFAULT now() );

Supabase Storage & Extras

The system relies heavily on Supabase Storage to hold the uploaded sponsor logos. You must create a public bucket named exactly logos. Without this bucket, the /api/submitBid route will throw a 500 error when attempting to push the Base64 image buffer into Storage.

CORS & Policies: Since Supabase Storage assets are accessed directly via standard image URLs in the browser (and the logo_url is saved in the bids table), you must ensure the logos bucket policies allow public anonymous reads.

5. Dodo Payments — Payment Setup

Spot My Mac uses Dodo Payments to handle deposits.

  1. Create a Dodo Payments account.
  2. Create a product named "Spot My Mac — Sponsorship Deposit".
  3. Configuration must be: One-time payment, with Pay What You Want enabled, and set an appropriate minimum price.

This is NOT a fixed-price product. The bidder enters their total desired bid amount on the website, and the backend dynamically calculates a 20% deposit.

deposit = bid × 20% $100 bid → $20 deposit $500 bid → $100 deposit $1,000 bid → $200 deposit

The backend then passes this dynamically calculated amount directly into the Dodo Checkout Session creation API. The customer does not manually type the deposit amount in Dodo. The API key used to generate this session must remain server-side so malicious users cannot generate fake sessions or modify prices.

6. Environment Variables

The following variables must be configured in Vercel. Never commit actual values to GitHub.

Variable Purpose
DODO_PAYMENTS_API_KEY Secret key used by the backend to create checkout sessions.
DODO_PAYMENTS_WEBHOOK_KEY Secret used to verify that webhooks actually originated from Dodo.
DODO_AUCTION_DEPOSIT_PRODUCT_ID The ID of the Pay What You Want product created in the Dodo dashboard.
DODO_MODE Set to test_mode for development or live for production.
APP_BASE_URL The URL of your deployed application (used for returning from checkout).
SUPABASE_URL The URL of your Supabase project.
SUPABASE_ANON_KEY Public anonymous key for non-sensitive reads.
SUPABASE_SERVICE_ROLE_KEY Secret admin key used exclusively in backend routes.

Dodo's test mode allows you to simulate successful and failed payments using test credit card numbers without moving real money. Always use test mode until you are fully ready to launch.

7. Dodo Webhooks

Webhooks are crucial because relying solely on the browser redirecting back to a "success" page is easily exploitable. A webhook is a secure, server-to-server HTTP request from Dodo telling your backend that a payment definitively succeeded.

Customer pays ↓ Dodo processes payment ↓ Dodo sends webhook (payment.succeeded) ↓ /api/webhook receives request ↓ Backend updates Supabase ↓ Bid/payment status updated to "paid"

The current application listens on /api/webhook for the payment.succeeded event. The webhook extracts the bid_id from the payment metadata and updates the corresponding bid record in Supabase to payment_status: 'paid'.

Future Improvement: While the webhook endpoint currently processes the event directly, a robust production deployment should verify the cryptographic webhook signature using the DODO_PAYMENTS_WEBHOOK_KEY to absolutely guarantee the request came from Dodo.

8. Complete Payment Flow

Here is the end-to-end user journey during the auction:

  1. User selects spot: They click on an available lid position.
  2. User enters bid: They type their bid amount, email, brand info, and upload a logo.
  3. Website sends bid to backend: The frontend POSTs this data to /api/submitBid.
  4. Backend validates bid: It checks if the logo upload succeeds and the amount is valid.
  5. Backend calculates 20% deposit.
  6. Dodo Checkout Session created: The backend talks to Dodo to generate a unique checkout URL.
  7. User pays deposit: The user is redirected to Dodo and enters card details.
  8. Dodo sends webhook: Upon success, Dodo POSTs to the webhook endpoint.
  9. Backend verifies webhook: The backend receives it and identifies the bid.
  10. Supabase updates payment status: The bid is marked as paid.
  11. Bid becomes paid / under review: The spot shows it is under review in the UI.
  12. Creator manually reviews sponsor/logo: The owner must log into their Supabase dashboard, open the bids table, review the sponsor's details, and manually change the status column to 'approved'. This explicitly authorizes the bid to become visible in the live auction table on the frontend, locking the spot.

If a payment fails, the webhook is not sent, and the bid remains stuck in a "pending" state indefinitely. If a sponsor is later outbid by a higher amount, their deposit is manually or automatically refunded depending on the Dodo configuration. Remember: Payment confirmation must come from the webhook rather than trusting the browser redirect.

9. Deploy Your Own Instance

Follow these steps to launch your own version:

  1. Fork and clone the GitHub repository.
  2. Create your own Supabase project.
  3. Configure the database (create bids, spots, visitors, auction_settings tables and the logos storage bucket).
  4. Create your own Dodo Payments account.
  5. Create the sponsorship deposit product (PWYW).
  6. Obtain the required Dodo credentials (API key, Webhook secret, Product ID).
  7. Import the repository into Vercel.
  8. Add all required environment variables into the Vercel project settings.
  9. Deploy the Vercel project.
  10. Configure the Dodo webhook in the Dodo dashboard using your deployed HTTPS URL (e.g., https://your-domain.com/api/webhook).
  11. Connect your custom domain in Vercel.
  12. Test everything thoroughly in Dodo test_mode.
  13. Only after successful testing, switch your environment variables to live credentials.

10. Local Development

To run Spot My Mac locally, ensure you have Node.js installed.

Because the project uses Vercel Serverless Functions in the /api directory, the correct command to start the local development server is using the Vercel CLI.

npx vercel dev

Create a local .env file in the root of the project with the same keys listed above. The Vercel CLI will automatically load these variables.

Note that Dodo webhooks cannot normally reach localhost directly. During local webhook testing, you will need to use an HTTPS tunnel (like Ngrok or localtunnel) to expose your local port 3000 to the internet.

11. Security Rules

  • Never expose Dodo API keys in frontend JavaScript. Always keep them in server-side API routes.
  • Never expose Supabase service-role credentials. The service role bypasses all RLS rules.
  • Never commit .env files. They are explicitly ignored in the .gitignore file for this reason.
  • Webhooks should be signature verified. Always validate that webhooks are authentically from your payment provider.
  • Payment status should be determined server-side. Never trust the client sending a "payment successful" boolean.
  • Validate bid amounts server-side rather than trusting the browser to pass the correct deposit amount.

12. Customize It

To make the project your own, you will need to change the following configuration assets (safe to modify):

  • Branding: Change logos, colors, and typography in index.html and style.css.
  • Mac/laptop image: Replace macbook_lid.png with your own laptop lid.
  • Spot configuration: Update the absolute positioning of spots in the CSS and the database spot coordinates.
  • Pricing: Modify the base pricing in the Supabase spots table and API routes.
  • Copy & Social Links: Update the text and footer links in index.html.

You should completely understand the backend API code in /api before attempting to modify the core payment and bidding logic.

13. Verify It Yourself

Don't trust the screenshot. Run it yourself.

The source is public and the live auction/payment flow can be thoroughly inspected and tested directly on the platform.

VIEW SOURCE CODE TRY THE LIVE AUCTION