ASAASA Standard
Active Phase 1Production Foundation

PCI Raw Card Data Safety

Billing Safety · BIL-17 · Priority: P0

Why It Matters

If your application collects, transmits, or stores raw credit card numbers — card number, CVV, expiration date — instead of using Stripe Elements or Checkout, you are in violation of PCI DSS and expose yourself to significant legal and financial liability.

PCI compliance requires that cardholder data never touches your servers. Stripe Elements and Checkout handle this by tokenizing card data in an iframe hosted by Stripe — your server never sees the actual card number.

AI code generators sometimes create custom payment forms with plain <input> fields for card data, bypassing Stripe's tokenization entirely.

Priority: P0 — PCI violation. Legal liability. Potential fines of $5,000–$100,000/month.

Affected Stack: Any framework with Stripe integration


The Problem

<!-- ❌ What AI tools sometimes generate -->
<form onSubmit={handlePayment}>
  <input name="cardNumber" placeholder="Card number" />
  <input name="cvv" placeholder="CVV" />
  <input name="expiry" placeholder="MM/YY" />
  <button type="submit">Pay</button>
</form>

This sends raw card data to your server. Even if you immediately forward it to Stripe, your server has handled PCI-sensitive data — making you liable for full PCI DSS compliance (SAQ D), which requires annual audits costing $50,000+.


The Fix

Use Stripe Elements or Stripe Checkout. Card data stays in Stripe's iframe — your server only receives a token.

// ✅ Stripe Elements — card data never touches your server
import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

function PaymentForm() {
  const stripe = useStripe();
  const elements = useElements();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const { paymentMethod } = await stripe!.createPaymentMethod({
      type: 'card',
      card: elements!.getElement(CardElement)!,
    });
    // Send paymentMethod.id to your server — not card data
  };

  return (
    <form onSubmit={handleSubmit}>
      <CardElement /> {/* Stripe-hosted iframe */}
      <button type="submit">Pay</button>
    </form>
  );
}

Even better: Use Stripe Checkout (server-initiated) which handles the entire payment UI on Stripe's domain.

References


Related Checks


Is your app safe? Run Free Scan →