Back to Docs

Recipe: Payment card tokenization (PCI scope reduction)

Replace raw PANs with provider tokens so your servers never touch cardholder data. This recipe shrinks your PCI DSS footprint from SAQ-D to SAQ-A.

Architecture

The browser collects card details inside an iframe hosted by your tokenization provider (Stripe Elements, Braintree Hosted Fields, or Spreedly). The provider returns a single-use token. Your backend forwards that token to the provider's API, receives a permanent multi-use token, and stores only that opaque reference.

Step 1 — Client-side iframe

<div id="card-element">
  <!-- Provider mounts iframe here -->
</div>
<script>
  const stripe = Stripe('pk_live_xxx');
  const elements = stripe.elements();
  const card = elements.create('card');
  card.mount('#card-element');
</script>

Step 2 — Token creation

const {token, error} = await stripe.createToken(card);
if (error) { /* handle */ }
// POST token.id to your backend

Step 3 — Server-side exchange

POST /v1/payment_methods
Authorization: Bearer sk_live_xxx
Content-Type: application/x-www-form-urlencoded

card=tok_visa&customer=cus_xxx

Store the returned pm_xxx ID. Never log or persist the raw token beyond the exchange window.

PCI scope impact

  • SAQ-A eligibility (22 controls vs SAQ-D's 329)
  • No cardholder data traverses your network
  • Provider handles key management and rotation
  • Chargebacks and refunds use the stored token
Meridian note: Combine with the “Secrets rotation” recipe to rotate your provider API keys automatically. Tokenization alone does not remove the need for transport-layer encryption — always enforce TLS 1.3.