← Back to Blog
Stripe Integration Fraud Prevention · 10 min read

Stop Fake Signups Before They Hit Stripe

Every fake account that reaches Stripe becomes your problem. Card-testing attacks cost $15-25 per chargeback. Trial abuse bleeds revenue. Disputed charges tank your Stripe risk score. The fix is not better Stripe settings — it is stopping fraudulent signups before they create an account at all.

The Problem: Fraud Gets Past Signup, Hits Your Payment System

Here is the typical signup flow for a SaaS application:

  1. User enters email, password, and phone number
  2. SMS OTP is sent and verified
  3. Account is created
  4. User enters payment information (Stripe Checkout or Elements)
  5. Stripe creates a customer, charges the card, starts the subscription

The fraud happens at step 1. A bot or human fraudster enters a disposable email (or a real person's email obtained from a breach), a TextNow phone number, and completes the SMS verification. By step 3, they have a fully authenticated account. By step 4, they are interacting with Stripe.

Stripe has its own fraud detection (Stripe Radar) that operates at the payment level. Radar is excellent at identifying stolen credit cards and suspicious payment patterns. But Radar cannot see what happened during signup. It does not know the account was created with a VOIP number. It does not know the signup IP is a known VPN exit node. It does not know three other accounts were created from the same device fingerprint in the last hour.

The Blind Spot

Stripe Radar evaluates the payment. Auth1 evaluates the person. Fraud prevention requires both, but the identity layer has to come first. By the time a fraudulent payment reaches Radar, the damage is already happening: the account exists, the free trial is consumed, the API is being abused, and your metrics are polluted.

What Card Testing Looks Like

Card testing (also called card cracking or BIN attacks) is when a fraudster uses your payment form to validate stolen credit card numbers. The process works like this:

  1. Fraudster obtains a batch of stolen card numbers from the dark web ($1-10 per card)
  2. They create an account on your platform (using a VOIP number to pass phone verification)
  3. They attempt small charges ($0.50-1.00) through your Stripe integration to test if cards are live
  4. Cards that succeed are confirmed as valid and sold at a premium ($25-100 per card)
  5. You are left with the failed charges, disputes, and a degraded Stripe risk score

The economics are simple. If a fraudster tests 100 cards through your platform, even at a 10% success rate, they profit. You absorb the costs:

Cost Item Per Incident 100 Card Test
Stripe processing fee (failed) $0.30 $30.00
Chargeback fee (successful tests) $15.00 $150.00
SMS verification cost $0.0075 $0.75
Support time (investigating) $5-15 $50-150
Stripe risk score degradation Cumulative Increased decline rates for real customers
Total $230-330

A single card-testing session costs you $230-330. Persistent attackers can run multiple sessions per week. Over a month, card-testing attacks can cost $1,000-5,000 for a mid-size SaaS.

The Free Trial Problem

Free trial abuse is simpler but equally damaging. The pattern is:

The financial impact depends on your product. For a SaaS charging $49/month, each trial-abusing account represents $49 in lost potential revenue. If 200 of your 2,000 monthly trials are abusers, that is $9,800/month in revenue that never converts.

The Architecture: Auth1 Sits Between Signup and Stripe

The solution is to insert a fraud-evaluation layer between your signup form and your payment system. Auth1 acts as a gatekeeper: only verified, low-risk users reach Stripe.

1
User submits signup form
Email, password, phone number collected in your frontend
2
Auth1 verifies phone + evaluates risk
Carrier lookup, VOIP detection, device fingerprint, IP reputation, velocity checks. Risk score computed in <50ms. If VOIP or high risk, signup is rejected before OTP is sent.
3
User completes OTP verification
SMS sent only to verified non-VOIP numbers. No wasted SMS costs.
4
Account created with fraud metadata
Auth1 returns a verified user token with risk score, carrier info, and device fingerprint attached.
5
Stripe customer created
Only verified, low-risk users reach Stripe Checkout. Fraud metadata passed as Stripe customer metadata for Radar to use.

Integration Guide: Auth1 + Stripe

Here is the complete server-side integration for a Node.js/Express application using Auth1 for signup verification and Stripe for payments.

JavaScript signup-with-stripe.js
const express = require('express');
const Stripe = require('stripe');

const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
const AUTH1_KEY = process.env.AUTH1_API_KEY;
const AUTH1_BASE = 'https://auth-api.z101.ai/api';

const app = express();
app.use(express.json());

// Step 1: Verify phone number (called when user submits signup form)
app.post('/api/signup/verify', async (req, res) => {
  const { phone, email } = req.body;

  // Auth1 handles VOIP detection + OTP delivery in one call
  const auth1Res = await fetch(`${AUTH1_BASE}/auth/sms/request`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': AUTH1_KEY,
    },
    body: JSON.stringify({
      phone,
      email,
      voipPolicy: 'block',
    }),
  });

  const data = await auth1Res.json();

  if (data.isVoip || data.riskScore > 60) {
    return res.status(400).json({
      error: 'verification_failed',
      message: 'Unable to verify this phone number. Please use a mobile number.',
    });
  }

  // OTP sent to verified number
  res.json({ success: true, riskScore: data.riskScore });
});

// Step 2: Complete signup + create Stripe customer
app.post('/api/signup/complete', async (req, res) => {
  const { phone, email, name, otp } = req.body;

  // Verify OTP with Auth1
  const verifyRes = await fetch(`${AUTH1_BASE}/auth/sms/verify`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': AUTH1_KEY,
    },
    body: JSON.stringify({ phone, code: otp }),
  });

  const verifyData = await verifyRes.json();

  if (!verifyData.verified) {
    return res.status(400).json({ error: 'Invalid verification code' });
  }

  // Create Stripe customer with fraud metadata
  const customer = await stripe.customers.create({
    email,
    name,
    phone,
    metadata: {
      auth1_risk_score: String(verifyData.riskScore),
      auth1_carrier: verifyData.carrierInfo?.name || 'unknown',
      auth1_line_type: verifyData.carrierInfo?.lineType || 'unknown',
      auth1_verified: 'true',
    },
  });

  // Create your user in your database
  const user = await db.users.create({
    email, name, phone,
    stripeCustomerId: customer.id,
    auth1RiskScore: verifyData.riskScore,
  });

  // Create Stripe Checkout session for payment
  const session = await stripe.checkout.sessions.create({
    customer: customer.id,
    mode: 'subscription',
    line_items: [{ price: 'price_xxx', quantity: 1 }],
    success_url: 'https://app.example.com/welcome',
    cancel_url: 'https://example.com/pricing',
  });

  res.json({ checkoutUrl: session.url });
});

What Happens at Each Step

Before Auth1 (No Protection)

After Auth1 (Protected)

Stripe Radar Integration

When you pass Auth1's risk score as Stripe customer metadata, Stripe Radar can use it in custom rules. For example, you can create a Radar rule that blocks payments from customers with auth1_risk_score > 50. This gives Radar additional signal it does not have on its own, making it significantly more effective.

Advanced Patterns

Pattern 1: Tiered Access Based on Risk Score

Instead of binary allow/block, use the risk score to gate access levels:

Pattern 2: Webhook-Based Monitoring

Auth1 sends webhooks for every verification event. Combine these with Stripe webhooks to build a real-time fraud dashboard:

JavaScript webhooks.js
// Auth1 webhook: verification completed
app.post('/webhooks/auth1', (req, res) => {
  const { event, data } = req.body;

  if (event === 'verification.completed') {
    // Log for analytics
    analytics.track('signup_verified', {
      phone: data.phone,
      riskScore: data.riskScore,
      isVoip: data.isVoip,
      carrier: data.carrierInfo.name,
    });
  }

  if (event === 'verification.blocked') {
    // Alert on blocked attempts
    slack.notify(`Blocked signup: ${data.phone} (${data.carrierInfo.name}, score: ${data.riskScore})`);
  }

  res.sendStatus(200);
});

// Stripe webhook: dispute created
app.post('/webhooks/stripe', (req, res) => {
  const event = req.body;

  if (event.type === 'charge.dispute.created') {
    const customer = event.data.object.customer;
    // Cross-reference with Auth1 risk score
    // Automatically ban accounts with disputes + high risk
  }

  res.sendStatus(200);
});

Pattern 3: Pre-Stripe Verification Charge

For high-risk but not blocked signups (risk score 50-70), use Stripe's Setup Intents to verify the payment method before granting access:

Results: Before and After

Companies that implement Auth1 as a pre-Stripe verification layer typically see these changes within 30 days:

Metric Before Auth1 After Auth1 Change
Fake account rate 15-25% 2-4% -85%
Card-testing incidents/month 3-8 0-1 -90%
Chargebacks/month $300-1,200 $0-75 -92%
Wasted SMS costs/month $15-45 $0-3 -95%
Trial-to-paid conversion 8-12% 14-22% +75%
Stripe dispute rate 0.8-1.5% 0.1-0.3% -80%

The trial-to-paid conversion increase is particularly significant. When you remove fake accounts from your funnel, your conversion metrics reflect actual user behavior, and they are almost always better than the fraud-polluted numbers suggest.

Getting Started

Auth1 integrates with Stripe in under an hour. Sign up at auth1.ai/signup, get your API key, and add the verification call before your Stripe customer creation. The free tier includes 1,000 verifications/month, enough to test the integration and measure the impact on your signup quality.