Offering a free trial (e.g., 14-day trial or 50 free credits) is one of the most effective conversion strategies for B2B SaaS. However, bad actors and resource hoarders exploit these offers by generating hundreds of throwaway accounts to continuously farm free computational resources (GPU hours, LLM tokens, or email sends).
In this guide, we will implement a production pipeline that pairs Stripe Billing with MailCheck API to block disposable trial abusers before they can provision resources.
1. The Cost of Unchecked Free Trial Abuse
When a single user automates 50 fake trial accounts:
- Direct Infrastructure Losses: Hundreds of dollars in third-party API calls (e.g. OpenAI GPT-4, ElevenLabs, Claude 3.5 Sonnet).
- Stripe Authorization Overhead: Increased card validation and customer creation clutter.
- Skewed Product Analytics: Inflated signup numbers leading to false conversion metrics.
2. Common Evasion Tactics Used by Farmers
- Burner Domains: Registering via
@tempmail.ninja,@dropmail.me, or@guerrillamail.com. - Subaddressing (Plus Addressing): Appending tags to standard inboxes (e.g.
john+trial1@gmail.com,john+trial2@gmail.com). - Catch-All Custom Domains: Buying a $2
.xyzdomain and using wildcard inboxes.
MailCheck API automatically flags subaddressing tricks, detects catch-all configurations, and matches against 40M+ active temporary domains.
3. The Pre-Checkout Verification Pipeline
Rather than checking the email after Stripe creates the customer, check the email synchronously before generating the Stripe Checkout Session:
graph LR
A["User Requests Free Trial"] --> B["MailCheck API Verification"]
B -->|Disposable / Burner| C["Reject: 'Permanent Email Required'"]
B -->|Legitimate Domain| D["Create Stripe Customer & Session"]
D --> E["Redirect User to Stripe Checkout"]
4. Production Node.js / Express / Next.js Implementation
Here is a complete checkout route handler:
import Stripe from 'stripe';
import { NextResponse } from 'next/server';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
});
export async function POST(req: Request) {
try {
const { email, planId } = await req.json();
if (!email) {
return NextResponse.json({ error: 'Email is required' }, { status: 400 });
}
// 1. Validate Email with MailCheck API (Sub-50ms Edge Check)
const validationRes = await fetch('https://fadsync-email-validation.p.rapidapi.com/v1/check', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-RapidAPI-Key': process.env.RAPIDAPI_KEY!,
'X-RapidAPI-Host': 'fadsync-email-validation.p.rapidapi.com'
},
body: JSON.stringify({ email })
});
const valResult = await validationRes.json();
// 2. Reject disposable addresses and low-reputation domains
if (valResult.is_disposable || valResult.recommendation === 'BLOCK') {
return NextResponse.json({
error: 'Free trials require a valid business or personal email address. Disposable email services are not allowed.',
suggestion: valResult.suggestion || null
}, { status: 422 });
}
// 3. Create Stripe Checkout Session with Free Trial Period
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
customer_email: email,
line_items: [
{
price: planId, // e.g. price_pro_monthly
quantity: 1,
},
],
mode: 'subscription',
subscription_data: {
trial_period_days: 14, // 14-Day Free Trial
},
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
});
return NextResponse.json({ url: session.url });
} catch (error: any) {
console.error('Stripe Trial Creation Error:', error);
return NextResponse.json({ error: error.message || 'Internal Server Error' }, { status: 500 });
}
}
5. Multi-Layer Defense Best Practices
- Enforce Card Pre-Authorization ($0 Auth): Require entering credit card details for trial activation, preventing pure bot networks.
- Combine with IP & Device Fingerprinting: Flag registrations sharing identical device hashes or VPN IP subnets.
- Use Domain-Level Lookups: If a single domain provisions more than 5 accounts in an hour, route them to your sales team instead of automated free trials.
