Yes, we can include 1 domain with a subscription. The clean way is to add a “domain credit” system tied to Stripe subscriptions.

Plan (simple + robust)
1) Define the offer

Eligibility: 1 free new registration or transfer (1-yr) for eligible TLDs (e.g., .com .net .org .co), exclude premium TLDs (e.g., .ai) and premium-priced domains.

Cap: Cover up to $15 wholesale (you can change this). If wholesale > cap → user pays the difference.

Timing:

Yearly plan: credit minted immediately, expires in 60 days if unused.

Monthly plan: (option A) after 2 paid months, or (option B) immediately—your call.

Renewal: Not included. Renewals billed at your normal retail. (You can add a small loyalty discount if you like.)

Abuse guard: 1 credit per 12 months per account; no premium names, no refunds if they cancel the subscription after redeeming (or charge a clawback fee).

2) Data model

Create a domain_credits table:

id, user_id, status (available|used|expired|revoked), cap_cents, eligible_tlds (json), issued_at, expires_at, used_domain, used_at, source_subscription_id
Create a domains table:

id, user_id, domain, tld, registered_at, expires_at, autorenew (bool), registrar="OpenSRS", premium (bool), subscription_linked (bool)

3) Stripe webhook → mint credit

Handle:

checkout.session.completed (new subs)

customer.subscription.created|updated (status becomes active)

Optional: invoice.paid (for monthly waiting period)

Logic:

If plan is eligible and user hasn’t received a credit in last 12 months → insert domain_credits (available, cap, eligible TLD list).

If subscription canceled within your “grace period” and credit used → optionally bill clawback.

4) Checkout flow for domains (frontend UX)

If user has available credit:

Show “Free with Pro (up to $15)” badge on eligible TLDs.

On selection, call backend /api/domain/price?domain=…

Backend checks OpenSRS availability + premium flag + wholesale.

If premium==true or wholesale > cap → show “Not fully covered: you pay $X”.

In cart:

Apply a line-item discount equal to min(retail_price, credit_cap).

Remaining (if any) billed via Stripe.

On success, redeem the credit.

5) Register via OpenSRS

Flow on successful payment (or $0):

Lookup (confirm available + non-premium).

registerDomain (1 year) with your contact + nameservers (or your DNS).

Save domain in domains table, set autorenew=true if you want.

Mark domain_credits.status = used, store used_domain.

6) Renewal & notifications (post-purchase)

30/7/3-day emails: “Your domain renews on …”

Add a Stripe upcoming invoice item for renewal (your retail).

If they cancel subscription later → domain remains theirs; renewals billed normally.

7) Transfers (optional but nice)

Allow transfer-in as the free domain:

Use credit to cover transfer + 1-yr extension (standard).

Same eligibility (no premium).

Require auth code + unlock + disable WHOIS privacy temporarily.

8) Fraud & limits

Require verified email + card.

One credit per 12 months per user (enforce in DB).

Prevent multiple redemptions by the same user/org.

Quick implementation checklist

Backend

 domain_credits + domains tables

 Stripe webhook handler to mint credits

 /api/domain/price → OpenSRS lookup, detect premium, return wholesale + retail + coverage

 /api/domain/register → verifies credit, charges delta (if any), calls OpenSRS, redeems credit

 Cron/queue worker for renewal reminders

Frontend

 “Included domain” banner when credit exists

 Cart shows discount from credit

 Clear copy: “Covers up to $15 on eligible TLDs. Premium domains excluded.”

Config you can set now

FREE_DOMAIN_TLDS=.com,.net,.org,.co

FREE_DOMAIN_WHOLESALE_CAP=1500 (=$15)

FREE_DOMAIN_CREDIT_EXPIRY_DAYS=60

FREE_DOMAIN_MONTHLY_WAITING_INVOICES=2 (if you pick the wait option)