
Monetize Without Tebex: Self-Hosted Store with Stripe & Webhooks
Read this before you start. If you run a FiveM/RedM server and plan to sell in‑game perks, you must use Tebex. Cfx.re’s docs and PLA state that “the use of any other platform or payment provider is prohibited” for server monetization. Self‑hosting Stripe is fine for off‑server sales (standalone scripts, MLOs, UI packs, services, courses). Don’t attach those sales to in‑game perks on live servers.
This guide is part of our complete FiveM server management hub, covering everything from initial setup to scaling your community.
What you can monetize without Tebex
Allowed (outside the server):
- Standalone scripts/MLOs you license for self‑install.
- Texture packs, UI kits, sound packs.
- Consulting, custom dev work, installation services.
- Documentation, courses, templates.
Avoid (requires Tebex or breaks policy):
- In‑game currency, pay‑to‑win, loot boxes, NFTs.
- Priority queue, VIP roles, whitelist boosts tied to your live server.
Architecture at a glance
Goal: a self‑hosted store that sells digital products (scripts/MLOs) and delivers licenses automatically.
Stack:
- Frontend: Next.js (App Router), React.
- Payments: Stripe Checkout (simple) or Payment Element + Payment Intents (custom flow).
- Webhooks: Receive events, verify signatures, update orders, deliver files/keys.
- Storage/Delivery: S3/R2 + signed URLs; or license server with rate‑limited downloads.
- Tax/Invoices: Stripe Tax + Billing invoices for B2B.
Data flow:
- Create Product + Price in Stripe.
- Start Checkout Session with metadata (productId, buyerId).
- On
checkout.session.completedorpayment_intent.succeeded, verify signature. - Mark order paid; generate license; create signed download link; email the receipt and license.
Step‑by‑step setup
1) Stripe account hygiene
- Complete onboarding and verify business (KYC).
- Set statement descriptor, support email, refund policy page URL.
- Enable Apple Pay / Google Pay; add bank account.
- Turn on Radar rules for basic fraud checks.
2) Products and pricing
- Create one Stripe Product per script/MLO.
- Use one‑time Prices in your buyer currency (EUR/USD).
- Add metadata keys you will need later:
sku,product_slug,license_policy.
3) Choose the payment flow
- Stripe Checkout: fastest and SCA‑ready. Handles wallets, 3DS, receipts. Recommended for most stores.
- Payment Element + Payment Intents: more control; use if you need custom UI or extra methods.
4) Secure webhooks
- Create a
POST /api/stripe/webhookendpoint. - Read the raw body; don’t parse JSON before verification.
- Verify with the endpoint secret; reject if signature fails.
- Use idempotency for outbound tasks you trigger from webhooks (e.g., license creation) so retries don’t duplicate orders.
5) Order + license fulfillment
- After verified success:
- Create
ordersrow withstatus='paid'. - Generate a license key (UUID v4) or buyer‑bound unlock token.
- Store an immutable build of the zip (hash it).
- Issue a time‑limited signed URL (e.g., 24 hours, 3 downloads).
- Email the buyer: invoice link, license, download button.
- Add a customer portal page with order history and re‑downloads.
- Create
6) Taxes, invoices, and receipts
- Turn on Stripe Tax; mark items as digital goods.
- Collect buyer location (IP + billing address) and tax numbers (e.g., EU VAT ID).
- For EU B2C, charge destination VAT; for valid EU B2B VAT IDs, zero‑rate and record the ID.
- Store invoice PDFs with the order record.
7) Compliance and policies
- Terms of Sale (license scope, no resale, anti‑cheat clause, DMCA takedown).
- Refunds: EU distance selling rules apply, but digital downloads lose the 14‑day right if the buyer expressly consents and acknowledges this before download. Show a checkbox and log consent.
- Privacy: keep a GDPR page; log webhook payloads without storing full card data.
- Security: rotate webhook secrets; least‑privilege API keys; encrypt license table at rest.
Minimal implementation (Next.js + Stripe Checkout)
Server action (create session):
// app/api/checkout/route.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-06-20' });
export async function POST(req: Request) {
const { sku, successUrl, cancelUrl } = await req.json();
const priceId = await mapSkuToPriceId(sku);
const session = await stripe.checkout.sessions.create({
mode: 'payment',
line_items: [{ price: priceId, quantity: 1 }],
metadata: { sku },
success_url: successUrl,
cancel_url: cancelUrl,
});
return Response.json({ url: session.url });
}
Webhook route:
// app/api/stripe/webhook/route.ts
import Stripe from 'stripe';
import { headers } from 'next/headers';
export const config = { api: { bodyParser: false } } as any; // keep raw body
function buffer(body: ReadableStream<Uint8Array>) {
const reader = body.getReader();
const chunks: Uint8Array[] = [];
return reader.read().then(function process({ done, value }): any {
if (done) return Buffer.concat(chunks);
chunks.push(Buffer.from(value));
return reader.read().then(process);
});
}
export async function POST(req: Request) {
const sig = headers().get('stripe-signature')!;
const buf = await buffer(req.body!);
let event: Stripe.Event;
try {
event = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2024-06-20' })
.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response('Bad signature', { status: 400 });
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session;
await fulfill(session); // create order, license, email
}
return new Response('OK', { status: 200 });
}
Idempotency tip: when your webhook triggers external work (e.g., emailing, license creation), wrap those calls with your own Idempotency-Key to prevent duplicates.
License and file delivery
- Bind each license to buyer email + product SKU.
- Sign download URLs; limit by time + attempts; log IPs for abuse control.
- Include a small license validator snippet in your resource loader to check the key against your API (rate‑limited).
Risk and fraud controls
- Enable 3DS/SCA via Checkout or Payment Intents.
- Block disposable emails and mismatched billing country for high‑risk orders.
- Keep dispute evidence templates: product description, license logs, download logs, consent to digital‑content waiver.
Don’t break Cfx.re rules
- Use Tebex for any perk that touches your running server (priority queue, VIP kits, cars, skins).
- Keep Stripe for off‑server digital products and services only.
Internal links (FiveMX)
- Monetization policy overview → https://vertexmods.com/en/shop/monetization-policy
- Refund & Returns for digital goods → https://vertexmods.com/en/shop/refund-policy-digital
- Script licensing template → https://vertexmods.com/en/shop/script-license-template
- DMCA takedown → https://vertexmods.com/dmca
- Performance & anti‑cheat notes → https://vertexmods.com/performance
External references
- Cfx.re PLA + Tebex‑only monetization and docs.
- Stripe webhooks and signature verification.
- Stripe idempotency keys and SCA.
- EU consumer law: digital content right of withdrawal exceptions.
- Stripe Tax: digital goods and OSS.
Ready‑to‑ship checklist
- Stripe account verified; business details complete.
- Products + Prices created; metadata set.
- Checkout session API live; success/cancel routes.
- Webhook endpoint live; signature verified; retries tested.
- Fulfillment: license generator, signed download URL, email.
- Taxes on; invoice PDFs stored; VAT validation.
- Legal pages: Terms, Refunds, Privacy, License.
- Logs + dashboards: orders, errors, retries, disputes.
- Audit: no server‑perk sales outside Tebex.
FAQ (short)
- Can I sell VIP roles with Stripe? Yes, if you use Discord.
- Can I sell my MLOs with Stripe? Yes, if delivery is off‑server and not tied to live perks.
- Do buyers get 14‑day refunds? For EU, not after they consent to immediate digital delivery and start the download. Log consent.
- Which Stripe flow is best? Checkout for speed; Payment Intents if you need heavy customization.
Stay in the Loop
Get the latest FiveM tutorials, mod releases, and exclusive updates delivered to your inbox.
No spam. Unsubscribe anytime.