Here’s a clean, end-to-end “1 free domain credit” implementation you can drop into your stack. It includes:

Backend: pricing/availability + register routes, Stripe webhook (mints credit), markup, OpenSRS client stub, DB schema, and credit helpers.

Frontend: minimal React UI to surface the credit and apply it in the cart.

Backend
1) server/lib/markup.js
// server/lib/markup.js
//
// Domain pricing markup utilities.
// - Flat + percent markup
// - Per-TLD overrides
// - 2-decimal rounding

const DEFAULTS = {
  flat: Number(process.env.MARKUP_FLAT ?? 3.00),
  percent: Number(process.env.MARKUP_PERCENT ?? 10), // %
  perTld: {
    // Example overrides (tune to your targets)
    ".com": { flat: 2, percent: 15 },
    ".net": { flat: 2, percent: 8 },
    ".org": { flat: 3, percent: 15 },
    ".co":  { flat: 6, percent: 20 },
    ".ai":  { flat: 10, percent: 5 },
  },
};

function round2(n) {
  return Math.round((Number(n) + Number.EPSILON) * 100) / 100;
}

function applyMarkup({ wholesale, tld, config = {} }) {
  const merged = {
    flat: DEFAULTS.flat,
    percent: DEFAULTS.percent,
    perTld: { ...DEFAULTS.perTld, ...(config.perTld || {}) },
  };

  const key = (tld || "").toLowerCase();
  const ovr = merged.perTld[key] || {};
  const flat = (ovr.flat ?? merged.flat);
  const percent = (ovr.percent ?? merged.percent);

  let retail = Number(wholesale);
  if (!Number.isFinite(retail)) throw new Error("Invalid wholesale price");

  retail = retail * (1 + (Number(percent) / 100));
  retail = retail + Number(flat);

  return round2(retail);
}

module.exports = { applyMarkup };

2) server/lib/opensrs.js (client stub)
// server/lib/opensrs.js
//
// Minimal OpenSRS helpers. Replace stubs with real API calls.

const crypto = require("crypto");

const OPEN_SRS_TEST_MODE = process.env.OPEN_SRS_TEST_MODE === "true";

function extractTld(domain) {
  const m = String(domain).toLowerCase().match(/(\.[a-z0-9-]+)$/i);
  return m ? m[1] : "";
}

// TODO: Implement real OpenSRS lookups.
// For now, this stub simulates availability & premium detection.
async function lookupDomain(domain) {
  const tld = extractTld(domain);
  // Simulated wholesale pulled from your CSV-like defaults (fallback)
  const WHOLESALE = {
    ".com": 14.50, ".net": 16.50, ".org": 9.99, ".biz": 21.00, ".info": 25.00, ".co": 14.99, ".ai": 169.50,
  };
  const wholesale = WHOLESALE[tld] ?? 20.00;

  // Simulate availability: 70% available in test mode; otherwise require real API
  let available = true;
  if (OPEN_SRS_TEST_MODE) {
    const h = crypto.createHash("md5").update(domain).digest("hex");
    available = (parseInt(h.slice(0, 2), 16) % 10) < 7; // 70%
  }

  // Simulate premium flag (very naive)
  const premium = [".ai"].includes(tld) || /gold|premium|invest/i.test(domain);

  return {
    domain,
    tld,
    available,
    premium,
    wholesale: {
      registration: wholesale,
      renewal: wholesale,
      currency: "USD",
    },
  };
}

// TODO: Implement real OpenSRS registration call.
async function registerDomain({ domain, years = 1, contact, nameservers = [] }) {
  if (!OPEN_SRS_TEST_MODE) {
    // Call the real OpenSRS API here and return its response.
  }
  // Simulated successful registration result
  const now = new Date();
  const exp = new Date(now);
  exp.setFullYear(now.getFullYear() + years);
  return {
    success: true,
    order_id: "sim-" + Math.random().toString(36).slice(2),
    domain,
    registered_at: now.toISOString(),
    expires_at: exp.toISOString(),
  };
}

module.exports = { lookupDomain, registerDomain, extractTld };

3) server/lib/credits.js
// server/lib/credits.js
//
// Helpers to mint, fetch, validate, redeem domain credits.

const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const DEFAULT_CAP_CENTS = Number(process.env.FREE_DOMAIN_WHOLESALE_CAP ?? 1500); // $15
const DEFAULT_ELIGIBLE_TLDS = (process.env.FREE_DOMAIN_TLDS ?? ".com,.net,.org,.co")
  .split(",").map(s => s.trim().toLowerCase()).filter(Boolean);
const CREDIT_EXPIRY_DAYS = Number(process.env.FREE_DOMAIN_CREDIT_EXPIRY_DAYS ?? 60);

async function mintCredit({ userId, subscriptionId }) {
  const expiresAt = new Date(Date.now() + CREDIT_EXPIRY_DAYS * 86400 * 1000);
  const res = await pool.query(
    `INSERT INTO domain_credits (user_id, status, cap_cents, eligible_tlds, issued_at, expires_at, source_subscription_id)
     VALUES ($1, 'available', $2, $3, NOW(), $4, $5)
     RETURNING *`,
    [userId, DEFAULT_CAP_CENTS, JSON.stringify(DEFAULT_ELIGIBLE_TLDS), expiresAt, subscriptionId]
  );
  return res.rows[0];
}

async function getAvailableCredit(userId) {
  const res = await pool.query(
    `SELECT * FROM domain_credits
     WHERE user_id = $1 AND status = 'available' AND expires_at > NOW()
     ORDER BY issued_at DESC
     LIMIT 1`,
    [userId]
  );
  return res.rows[0] || null;
}

async function hasCreditInLast12Months(userId) {
  const res = await pool.query(
    `SELECT 1 FROM domain_credits
     WHERE user_id = $1 AND issued_at >= (NOW() - INTERVAL '12 months')
     LIMIT 1`,
    [userId]
  );
  return res.rowCount > 0;
}

async function redeemCredit({ creditId, domain }) {
  await pool.query(
    `UPDATE domain_credits
     SET status = 'used', used_domain = $2, used_at = NOW()
     WHERE id = $1 AND status = 'available'`,
    [creditId, domain]
  );
}

module.exports = { mintCredit, getAvailableCredit, hasCreditInLast12Months };

4) server/routes/domains.js
// server/routes/domains.js
//
// GET /api/domain/price?q=example.com   -> availability + wholesale + retail + coverage
// POST /api/domain/register              -> registers domain using credit (covers cap) + charges delta via Stripe

const express = require("express");
const router = express.Router();
const Stripe = require("stripe");
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const { applyMarkup } = require("../lib/markup");
const { lookupDomain, registerDomain } = require("../lib/opensrs");
const { getAvailableCredit, redeemCredit } = require("../lib/credits");
const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const CURRENCY = process.env.CURRENCY || "usd";

function isEligibleTld(tld, list) {
  return list.includes(String(tld || "").toLowerCase());
}

router.get("/price", async (req, res) => {
  try {
    const userId = req.user?.id || req.query.userId; // adapt to your auth
    const q = String(req.query.q || "").trim();
    if (!q) return res.status(400).json({ error: "missing_domain" });

    const info = await lookupDomain(q);
    const wholesale = info.wholesale.registration;
    const retail = applyMarkup({ wholesale, tld: info.tld });

    // Credit coverage
    let credit = null;
    let coveredCents = 0;
    if (userId) {
      credit = await getAvailableCredit(userId);
      if (credit && !info.premium && isEligibleTld(info.tld, credit.eligible_tlds)) {
        coveredCents = Math.min(Math.round(wholesale * 100), credit.cap_cents); // cover wholesale up to cap
      }
    }

    return res.json({
      domain: info.domain,
      tld: info.tld,
      available: info.available,
      premium: info.premium,
      pricing: {
        wholesale: { registration: wholesale, currency: info.wholesale.currency },
        retail: { registration: retail, currency: CURRENCY.toUpperCase() },
      },
      credit: credit ? {
        credit_id: credit.id,
        eligible: !info.premium && isEligibleTld(info.tld, credit.eligible_tlds),
        cap_cents: credit.cap_cents,
        covered_cents: coveredCents,
      } : null,
    });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "price_failed" });
  }
});

router.post("/register", async (req, res) => {
  try {
    const userId = req.user?.id || req.body.userId; // adapt to your auth
    const { domain, contact, nameservers, payment_method_id } = req.body;
    if (!userId || !domain) return res.status(400).json({ error: "missing_params" });

    const info = await lookupDomain(domain);
    if (!info.available) return res.status(400).json({ error: "domain_unavailable" });
    if (info.premium) return res.status(400).json({ error: "premium_not_covered" });

    const wholesale = info.wholesale.registration;
    const retail = applyMarkup({ wholesale, tld: info.tld });

    // Fetch credit (if any)
    const credit = await getAvailableCredit(userId);
    let coveredCents = 0;
    if (credit && isEligibleTld(info.tld, credit.eligible_tlds)) {
      coveredCents = Math.min(Math.round(wholesale * 100), credit.cap_cents);
    }

    const retailCents = Math.round(retail * 100);
    const toChargeCents = Math.max(0, retailCents - coveredCents);

    // If there's an amount to charge, create a PaymentIntent and confirm it
    let charge = null;
    if (toChargeCents > 0) {
      charge = await stripe.paymentIntents.create({
        amount: toChargeCents,
        currency: CURRENCY,
        payment_method: payment_method_id,
        confirm: true,
        automatic_payment_methods: { enabled: true },
        metadata: { kind: "domain_registration", domain },
      });
      if (charge.status !== "succeeded" && charge.status !== "requires_capture" && charge.status !== "processing") {
        return res.status(402).json({ error: "payment_failed", stripe_status: charge.status });
      }
    }

    // Proceed to register domain via OpenSRS
    const reg = await registerDomain({ domain, years: 1, contact, nameservers });

    // Save domain record
    const result = await pool.query(
      `INSERT INTO domains (user_id, domain, tld, registered_at, expires_at, registrar, premium, subscription_linked, autorenew)
       VALUES ($1, $2, $3, $4, $5, 'OpenSRS', $6, true, true)
       RETURNING *`,
      [userId, info.domain, info.tld, reg.registered_at, reg.expires_at, info.premium]
    );

    // Redeem credit if used
    if (credit && coveredCents > 0) {
      await redeemCredit({ creditId: credit.id, domain: info.domain });
    }

    return res.json({
      success: true,
      domain: result.rows[0],
      payment_intent: charge ? charge.id : null,
      opensrs_order: reg.order_id,
    });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "register_failed" });
  }
});

module.exports = router;

5) server/webhooks/stripe.js
// server/webhooks/stripe.js
//
// POST /webhooks/stripe
// - Mints a domain credit when a subscription activates (and user has not received one in last 12 months)

const express = require("express");
const router = express.Router();
const Stripe = require("stripe");
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
const { mintCredit, hasCreditInLast12Months } = require("../lib/credits");
const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// Map your Stripe customer to your internal user_id
async function userIdFromStripeCustomer(customerId) {
  const r = await pool.query(
    `SELECT user_id FROM stripe_customers WHERE customer_id = $1 LIMIT 1`,
    [customerId]
  );
  return r.rows[0]?.user_id || null;
}

router.post("/", express.raw({ type: "application/json" }), async (req, res) => {
  let event;
  try {
    const sig = req.headers["stripe-signature"];
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.error("Webhook signature verification failed.", err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  try {
    if (event.type === "checkout.session.completed") {
      const session = event.data.object;
      if (session.mode === "subscription" && session.customer) {
        const userId = await userIdFromStripeCustomer(session.customer);
        if (userId && !(await hasCreditInLast12Months(userId))) {
          await mintCredit({ userId, subscriptionId: session.subscription });
        }
      }
    }

    if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated") {
      const sub = event.data.object;
      if (sub.status === "active" && sub.customer) {
        const userId = await userIdFromStripeCustomer(sub.customer);
        if (userId && !(await hasCreditInLast12Months(userId))) {
          await mintCredit({ userId, subscriptionId: sub.id });
        }
      }
    }

    // (Optional) invoice.paid logic for “after N paid months” gating can go here.

    res.json({ received: true });
  } catch (e) {
    console.error("Webhook processing failed", e);
    res.status(500).json({ error: "webhook_failed" });
  }
});

module.exports = router;

6) DB schema (PostgreSQL)
-- domains table
CREATE TABLE IF NOT EXISTS domains (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  domain TEXT NOT NULL,
  tld TEXT NOT NULL,
  registered_at TIMESTAMP WITH TIME ZONE NOT NULL,
  expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
  registrar TEXT NOT NULL DEFAULT 'OpenSRS',
  premium BOOLEAN NOT NULL DEFAULT false,
  subscription_linked BOOLEAN NOT NULL DEFAULT false,
  autorenew BOOLEAN NOT NULL DEFAULT true
);

-- credits table
CREATE TABLE IF NOT EXISTS domain_credits (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  status TEXT NOT NULL CHECK (status IN ('available','used','expired','revoked')),
  cap_cents INTEGER NOT NULL,
  eligible_tlds JSONB NOT NULL,
  issued_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
  used_domain TEXT,
  used_at TIMESTAMP WITH TIME ZONE,
  source_subscription_id TEXT
);

-- map Stripe customers to your users (simple helper table)
CREATE TABLE IF NOT EXISTS stripe_customers (
  user_id BIGINT UNIQUE NOT NULL,
  customer_id TEXT UNIQUE NOT NULL
);

7) Express wiring (example)
// server/index.js
const express = require("express");
const app = express();

// IMPORTANT: Stripe webhook must use raw body:
app.use("/webhooks/stripe", require("./webhooks/stripe"));

// JSON for your normal API:
app.use(express.json());

app.use("/api/domain", require("./routes/domains"));

app.listen(process.env.PORT || 3000, () => {
  console.log("Server running");
});

8) Environment variables
DATABASE_URL=postgres://user:pass@host:5432/db
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
CURRENCY=usd

OPEN_SRS_TEST_MODE=true

MARKUP_FLAT=3
MARKUP_PERCENT=10

FREE_DOMAIN_TLDS=.com,.net,.org,.co
FREE_DOMAIN_WHOLESALE_CAP=1500     # $15
FREE_DOMAIN_CREDIT_EXPIRY_DAYS=60

Frontend (React)
1) Included domain banner
// IncludedDomainBanner.jsx
import React from "react";

export default function IncludedDomainBanner({ credit }) {
  if (!credit) return null;
  return (
    <div className="rounded-xl border p-3 bg-emerald-50 text-emerald-800">
      <strong>Pro Perk:</strong> 1 domain included (up to ${(credit.cap_cents/100).toFixed(2)} wholesale) on eligible TLDs.
    </div>
  );
}

2) Search result row with credit application
// DomainSearchResult.jsx
import React from "react";

export default function DomainSearchResult({ data, onRegister }) {
  if (!data) return null;
  const retail = data?.pricing?.retail?.registration ?? 0;
  const covered = data?.credit?.covered_cents ?? 0;
  const outOfPocket = Math.max(0, Math.round(retail * 100) - covered) / 100;

  const badge = data.credit?.eligible && covered > 0
    ? `Free with Pro (covers $${(covered/100).toFixed(2)})`
    : data.credit ? `Pro credit available` : null;

  return (
    <div className="border rounded-xl p-4 flex items-center justify-between">
      <div>
        <div className="text-lg font-semibold">{data.domain}</div>
        <div className="text-sm text-gray-600">
          {data.available ? "Available" : "Taken"} {data.premium && "• Premium"}
          {badge && <span className="ml-2 text-emerald-700 font-medium">{badge}</span>}
        </div>
      </div>
      <div className="text-right">
        <div className="text-xl font-bold">${retail.toFixed(2)}/yr</div>
        {covered > 0 && <div className="text-sm text-gray-600">You pay ${outOfPocket.toFixed(2)}</div>}
        <button
          className="mt-2 px-4 py-2 rounded-lg bg-black text-white disabled:opacity-50"
          disabled={!data.available || data.premium}
          onClick={() => onRegister({ domain: data.domain })}
        >
          {outOfPocket === 0 ? "Claim Included Domain" : "Register"}
        </button>
      </div>
    </div>
  );
}

3) Example usage with API calls
// DomainSearchPage.jsx
import React, { useState } from "react";
import IncludedDomainBanner from "./IncludedDomainBanner";
import DomainSearchResult from "./DomainSearchResult";

export default function DomainSearchPage({ userId }) {
  const [q, setQ] = useState("");
  const [data, setData] = useState(null);
  const [credit, setCredit] = useState(null);
  const [pmId, setPmId] = useState(null); // your saved Stripe payment method id

  async function checkPrice() {
    const r = await fetch(`/api/domain/price?q=${encodeURIComponent(q)}&userId=${encodeURIComponent(userId)}`);
    const j = await r.json();
    setData(j);
    if (j?.credit) setCredit(j.credit);
  }

  async function registerDomain({ domain }) {
    const r = await fetch(`/api/domain/register`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        userId,
        domain,
        contact: {
          first_name: "Your",
          last_name: "Customer",
          email: "user@example.com",
          phone: "+1.5555555555",
          address1: "123 Street",
          city: "City",
          state: "ST",
          country: "US",
          postal_code: "00000"
        },
        nameservers: ["ns1.yourns.com", "ns2.yourns.com"],
        payment_method_id: pmId, // Stripe PM id if out-of-pocket > 0
      })
    });
    const j = await r.json();
    if (!j.success) {
      alert(`Registration failed: ${j.error || "unknown"}`);
    } else {
      alert("Domain registered! 🎉");
    }
  }

  return (
    <div className="max-w-2xl mx-auto space-y-4">
      <IncludedDomainBanner credit={credit} />
      <div className="flex gap-2">
        <input
          className="flex-1 border rounded-lg px-3 py-2"
          placeholder="Search a domain (e.g., brandrocket.com)"
          value={q}
          onChange={e => setQ(e.target.value)}
        />
        <button className="px-4 py-2 rounded-lg bg-black text-white" onClick={checkPrice}>Check</button>
      </div>
      {data && <DomainSearchResult data={data} onRegister={registerDomain} />}
    </div>
  );
}

Notes & defaults you can tweak quickly

Coverage math: credit covers up to min(wholesale, cap)—that ensures you never lose money; user pays the rest of retail.

Premium: currently excluded. You can allow and just cap coverage; keep clear UI copy.

Monthly plan unlock: add invoice counter in webhook if you want “after N paid invoices”.