Process Card Payments
Storing your customers' cards with Basis Theory platform grants you the ability to seamlessly shift between payment processors, giving you competitive advantages including payment flexibility, reliability, cost savings, increased acceptance rates, and future-proofing your business against processor downtime or shutdown.
This guide will explore how to forward stored cardholder data to any third-party API endpoint, with minimal configuration requirements. By following the steps presented below, you are substantially de-scoping your servers and database from PCI DSS compliance. If you want to learn more about how we can help you meet up to 95% of the PCI requirements or if you need help filling out your SAQ, reach out to our team!
If you are not yet storing your customers' cards with Basis Theory, here are a few guides you can explore:
- Collect Cards - capture cards in the frontend;
- Receive Cards via API - receive cards in API requests;
- Issue Cards - create brand-new cards using an Issuer API;
- Import from a Database - migrate to Basis Theory.
Getting Started
To get started, you will need a Basis Theory account and a Tenant.
Creating a Private Application
Next, you will need a Private Application using our PCI-compliant template Use PCI Tokens
. Click here to create one.
This will create an application with the following Access Controls:
- Permissions:
token:use
- Containers:
/pci/
- Transform:
reveal
Send the Data
We will use Basis Theory Ephemeral Proxy, a tool that transparently performs detokenization, to share the sensitive cardholder data to the Payments Processor or Acquirer API. To do this, we will formulate our HTTPS request as if we were directly connecting to the target endpoint, with the following variations:
- Use the previously-created Private Application Key as the value of the
BT-API-KEY
header; - Specify the target API endpoint as the value for the
BT-PROXY-URL
header; - Pass any additional headers you need the target API to receive, for example
Authorization
,X-API-KEY
, etc.; - Replace the plaintext sensitive cardholder data in the expected payload contents with detokenization expressions that contains token identifiers.
For example, given you have a previously stored card token with the following identifier:
{
"id": "d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4",
"type": "card",
"tenant_id": "15f48eb5-8b52-4cdd-a396-608f7cf001d0",
"data": {
"number": "XXXXXXXXXXXX4242",
"expiration_month": 12,
"expiration_year": 2025
},
"created_by": "4a6ae2a6-79f8-4640-968f-88db913743df",
"created_at": "2023-04-17T12:54:44.8320458+00:00",
"mask": {
"number": "{{ data.number | reveal_last: 4 }}",
"expiration_month": "{{ data.expiration_month }}",
"expiration_year": "{{ data.expiration_year }}"
},
"privacy": {
"classification": "pci",
"impact_level": "high",
"restriction_policy": "mask"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
Here are a few integration examples of how to send the detokenized card information to third party APIs.
- Stripe
- Adyen
- Chase
- TabaPay
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: test_1234567890' \
-H 'BT-PROXY-URL: https://api.stripe.com/v1/payment_methods' \
-H 'Authorization: Bearer sk_test_51KMGNYGuvJF9SIWEW0y4rKcaQwLVLck2rGB8UEPHzSp1utx7gXKAfZ3DVgjMfAuvBIT42pQhg0sIx2PepEJkXv9g00yIrUwhI4' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'type=card' \
--data-urlencode 'card[number]={{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.number" }}' \
--data-urlencode 'card[exp_month]={{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.expiration_month" }}' \
--data-urlencode 'card[exp_year]={{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.expiration_year" }}' \
--data-urlencode 'card[cvc]={{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.cvc" }}'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: test_1234567890' \
-H 'BT-PROXY-URL: https://checkout-test.adyen.com/v69/payments' \
-H 'X-API-KEY: Vt(JJ5U5xuVECtg59fm9hBM+cZMWhw+ms2edxM%Rwmu0=Z2n3rGiQjQr-YEYfAq((It-Ocb03Jfob1JqGhogg:J/skGLIwerM=uAuHQDFHZBh+75pKgznYB3QeL7mrnBSeh34YAxLjdGEJQAhKdaU2' \
-H 'Content-Type: application/json' \
-d '{
"amount": {
"value": 100,
"currency": "USD"
},
"paymentMethod": {
"type": "scheme",
"number": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.number\" }}",
"expiryMonth": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_month\" | pad_left: 2, \"0\" }}",
"expiryYear": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_year\" }}",
"cvc": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.cvc\" }}",
"holderName": "John Smith"
},
"reference": "YOUR_ORDER_NUMBER",
"shopperInteraction": "Ecommerce",
"recurringProcessingModel": "CardOnFile",
"storePaymentMethod": "true",
"merchantAccount": "BasisTheoryECOM",
"shopperReference": "YOUR_UNIQUE_SHOPPER_ID",
"returnUrl": "https://your-company.com/..."
}'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: test_1234567890' \
-H 'BT-PROXY-URL: https://orbitalvar1.chasepaymentech.com/profile' \
-H 'orbitalConnectionUsername: TESTMYCOMPANY1234' \
-H 'orbitalConnectionPassword: f1nwbj32VwW36tV16s55' \
-H 'merchantID: MERCHANTID1234' \
-H 'Content-Type: application/json' \
-d '{
"version": "4.3",
"merchant": {
"bin": "000001"
},
"order": {
"customerProfileFromOrderInd": "A",
"customerProfileOrderOverideInd": "NO"
},
"paymentInstrument": {
"customerAccountType": "CC",
"card": {
"ccAccountNum": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.number\" }}",
"ccExp": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_year\" }}{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_month\" | pad_left: 2, \"0\" }}"
}
},
"profile": {
"customerName": "John Smith",
"customerAddress1": "123 Main St",
"customerCity": "Tampa",
"customerState": "FL",
"customerZIP": "33601"
}
}'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: test_1234567890' \
-H 'BT-PROXY-URL: https://api.tabapay.net/v1/clients/MyClientID/accounts' \
-H 'Authorization: Bearer 123' \
-H 'Content-Type: application/json' \
-d '{
"card": {
"accountNumber": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.number\" }}",
"expirationDate": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_year\" }}{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.expiration_month\" | pad_left: 2, \"0\" }}",
"securityCode": "{{ d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.cvc\" }}"
},
"owner": {
"name": {
"first": "John",
"last": "Smith"
},
"address": {
"line1": "123 Main St",
"city": "Tampa",
"state": "FL",
"zipcode": "33601"
}
},
"referenceID": "reference_123"
}'
Key Considerations
You are not restricted to the Payment Processors listed above. As long as your partner can accept cards through an API endpoint, you can invoke it using the Ephemeral Proxy.
If you find that the HTTPS response from your processor contains cardholder data, you can use a Pre-Configured Proxy to redact or tokenize the sensitive data points using a Response Transform.
It's important to note that for some acquirers, inbound connections are only accepted from whitelisted IP addresses provided by the client. To help with this, here you can find a compiled list of our IP addresses that you can send to your acquirer. In cases of more restrictive integrations, Basis Theory can provide dedicated IPs upon request. If you're interested in this option, please don't hesitate to contact us.
During testing phase, make sure to create tokens using test cards documented by your payment processor, following the desired test scenario. Passing incorrect cards to test/sandbox endpoints may lead to hard-to-debug rejected transactions.
Conclusion
By using our Ephemeral Proxy, you can confidently transmit cardholder data to Payment Processors via API requests without ever touching the card details yourself. This approach not only improves security and reduces compliance risks but also provides the flexibility to establish your own relationships with Payment Processors and the latest payment technologies.