Card Vaulting
Save a card once and charge it again: capture it in the browser with EPD Elements (epd.js), then attach it to an EPD Commerce customer with your secret key.
What this is and who needs it
To bill a customer more than once, or to charge them later without making them re-enter their card, you have to store the card somewhere safe. Storing card data yourself means PCI compliance, audits, and risk you do not want.
EPD handles this for you, and the payoff is immediate: raw card data never reaches your servers, which keeps your PCI compliance at its lightest tier: PCI SAQ A (a short self-check, not a full audit). Under the hood, the card is captured in a secure field and stored in EPD’s PCI-scoped vault; EPD Commerce keeps a display copy (last four digits, brand, expiry) that points back to the stored card.
If you charge customers more than once (subscriptions, top-ups, win-backs, saved-card checkout), this is the flow you need.
The recommended way to capture a card for vaulting is EPD Elements, the epd.js browser SDK. No browser in the loop (phone/MOTO orders where an agent keys the card in by hand, a back-office tool, or a migration)? Use Inbound Card Capture instead. Both produce the same vaulted payment method.
How it works
Vaulting uses two keys with two different jobs. Keep them straight.
epd_live_pk_… / epd_test_pk_…. Lives in the browser. Capture-only: it tokenizes a card and nothing else. Safe to ship in client-side code.
epd_live_sk_… / epd_test_sk_…. Server-side only. Attaches the captured card to a customer, then charges it. Never expose it in the browser.
EPD Commerce does not accept a raw PAN (the card number itself), CVV, or expiry over its API. Raw card data reaches EPD only through a PCI-scoped capture layer that tokenizes it first: the EPD Elements secure field in the browser, or the server-to-server Inbound Card Capture proxy. The API itself only ever receives the resulting token.
The flow
Client-side
Your checkout page loads epd.js and mounts a secure card field with your publishable key. The shopper enters their card; your pages and servers never see the raw PAN. See EPD Elements for the full SDK reference (combined and split fields, styling, events).
Client-side
On submit, the SDK tokenizes the card and returns a single-use card_token (cct_…).
A card_token is single-use and expires 15 minutes after capture. Attach it promptly; if the attach fails, capture a fresh one. Never resend a spent token.
Client-side → your server
Your frontend POSTs the card_token to your own server. It is bound to your merchant and useless to anyone else, but route it only to your backend.
Server-side → EPD Commerce
Your backend attaches the token to a customer with your secret key. EPD vaults the card, stores the mirror, and returns a UUID id for the payment method.
curl https://api.epd.com/v1/customers/$CUSTOMER_ID/payment_methods \
-H "Authorization: Bearer $EPD_KEY" \
-H "epd-version: 2026-02-11" \
-H "X-EPD-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"card_token": "cct_3f8a1c9e7b2d4a6f0e1c3b5d7f9a1c3e5b7d9f0a2c4e6b8d",
"set_as_default": true
}'
Don’t have the customer yet? Create one first with POST /v1/customers, then attach. Some processors require a billing address to vault: if yours does, pass an optional billing_details object on the same request. See Add Payment Method.
Pass payment_method_id: "<uuid>" on POST /v1/orders or POST /v1/subscriptions. The card never has to be entered again.
Key identifiers
A few identifiers move through this flow. Keep them straight.
| Identifier | Source | Lifetime | What it identifies |
|---|---|---|---|
card_token | EPD Elements | 15 minutes | One-time, single-use token representing the captured card |
payment_method_id | EPD Commerce | Permanent | The vaulted payment method on an EPD Commerce customer |
payment_method_id is the only id that must be a bare UUID on input; don’t prefix it with pm_.
Sandbox
A test publishable key (epd_test_pk_…) tokenizes against your sandbox; the SDK behaves identically; only the key’s environment differs. Type a test card (e.g. 4111 1111 1111 1111 for a Visa success) into the field and the resulting card_token works against the sandbox attach endpoint. Sandbox is keyed off your API key: same base URL, no separate environment.
Why this matters
| Without a vault | With EPD’s vault |
|---|---|
| Card data flows through your servers | Card data never reaches your servers |
| Full PCI DSS audit (SAQ D) | Reduced PCI scope (SAQ A) |
| You store sensitive card data | EPD stores it; you store a token only |
| Re-entry required for each charge | Charge stored cards on demand |
Security
Never log, store, or transmit raw card numbers. Capture cards only through the EPD Elements secure field; never build your own input that touches the PAN.
- Keep your secret key server-side only; never ship it to the browser. Only the publishable key belongs in client code.
- Use HTTPS for every API call.
- Rotate keys periodically from the EPD Commerce dashboard.