Charge a Card
Streamline card transactions with a secure, proxy-powered solution.Charging a card involves finalizing the transaction by requesting approval from the card networks and issuers to transfer funds. In checkout flows, this operation typically constitutes a Customer-Initiated Transaction (CIT), where the cardholder actively authorizes the payment. Properly flagged CITs help issuers distinguish these transactions from merchant-initiated ones, often leading to higher approval rates and a smoother payment experience.
This guide will show you how to use the Proxy to charge a card with your chosen Payment Service Provider (PSP) while offloading PCI compliance scope with ease. We’ll explore a variety of processor-specific standards and their unique verification quirks, offering practical examples you can adapt to your selected PSP.
If you are not yet collecting your customers' cards with Basis Theory, here are a few guides you can explore:
- Replace Processor iFrames - capture cards in the frontend;
- Receive Cards via API / Webhooks - receive cards in API requests;
Getting Started
To get started, you will need to create a Basis Theory Account and a TEST Tenant.
Private Application
You will need a Private Application to allow your backend to call Basis Theory APIs. Click here to create one using the Basis Theory Customer Portal.
This will create an application with the following Access Controls:
- Permissions:
token:use
,token:create
First Charge
Payment service providers (PSPs) may differ in how they handle this operation, offering workflows like "authorization and capture" or "direct charge." Understanding these variations allows merchants to optimize the process for efficiency and reliability, ensuring seamless processing in multi-psp scenarios and consistent fund settlement.
Given you have previously created a Token Intent:
{
"id": "d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4",
"type": "card",
"tenant_id": "4aee08b9-5557-474b-a120-252e01fc7b0f",
"fingerprint": "BKJYqf2tcvhTHSXN7EvBJLviN3PBYRgwoJgce8VAfnSr",
"card": {
"bin": "424242",
"last4": "4242",
"expiration_month": 10,
"expiration_year": 2028,
"brand": "visa",
"funding": "credit",
"authentication": "sca_required",
"issuer_country": {
"alpha2": "GB",
"name": "UNITED KINGDOM OF GREAT BRITAIN AND NORTHERN IRELAND",
"numeric": "826"
}
},
"created_by": "0bc89db5-fadd-4d57-af93-10472e35ebd3",
"created_at": "2025-03-10T14:23:56.5580574+00:00",
"expires_at": "2025-03-11T14:23:56.5580575+00:00"
}
Here is how you can leverage the Ephemeral Proxy, a tool that transparently performs detokenization, to share the sensitive cardholder data to the Payments Processor or Acquirer API.
- Checkout.com
- Stripe
In Checkout.com, you can charge a card by calling the /payments
API.
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://api.sandbox.checkout.com/payments' \
-H 'Authorization: Bearer <CHECKOUT_SECRET_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "card",
"number": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data.number\" }}",
"expiry_month": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data\" | card_exp: \"MM\" }}",
"expiry_year": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data\" | card_exp: \"YYYY\" }}",
"cvv": "{{ token_intent: d96b9925-85d7-495e-88fe-a9c3ecb23b61 | json: \"$.data.cvc\" }}",
"store_for_future_use": true
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "pc_svyx12xvaaduraq3ckb2gwnvzq"
}'
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "card",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}
In Stripe, you can charge a card by creating a Payment Intent.
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://api.stripe.com/v1/payment_intents' \
-H 'Authorization: Bearer <STRIPE_SECRET_KEY>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'amount=1000' \
--data-urlencode 'currency=USD' \
--data-urlencode 'customer=cus_RTEOvM5dSx9RCS' \
--data-urlencode 'confirm=true' \
--data-urlencode 'payment_method_data[type]=card' \
--data-urlencode 'payment_method_data[card][number]={{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.data.number" }}' \
--data-urlencode 'payment_method_data[card][exp_month]={{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.data" | card_exp: "MM" }}' \
--data-urlencode 'payment_method_data[card][exp_year]={{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.data" | card_exp: "YYYY" }}' \
--data-urlencode 'payment_method_data[card][cvc]={{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: "$.data.cvc" }}'
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"status": "succeeded",
"object": "payment_intent",
"payment_method": "pm_1QaKJmG9VRQ2700S5JcsJv4Q",
"latest_charge": {
"payment_method_details": {
"card": {
"network_transaction_id": "8723dfab9c4e56a1b8cd0291ef67ac43"
...
}
...
}
...
}
...
}
We are working to add more full-featured processor-specific examples. Please refer to our previous generic proxy guide for more request samples.
Don't hesitate to contact us if you need assistance with performing card transactions against a specific processor.
Save a Card
Once a card has been processed for the first time, merchants can store details in the long-term for future use and enable express checkouts, repeat transactions for subscription services, recurring payments and consumption-based charges. Merchants are responsible for obtaining explicit customer consent before saving card details, ensuring compliance with data protection regulations and fostering trust.
To do that, we can convert the Token Intent into a Token, by calling the Create Token API and passing the token_intent_id
.
curl "https://api.basistheory.com/tokens" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"token_intent_id": "d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4"
}'
This operation returns a new or existing persistent Token depending on your fingerprinting and deduplication options:
{
"id": "1cd9c8bf-99fd-4784-ba69-68b15b301019",
"tenant_id": "74eca6ab-f94d-49bc-9f3c-6144eb3415ac",
"type": "card",
"card": {
"bin": "42424242",
"brand": "visa",
"last4": "4242",
"expiration_month": 12,
"expiration_year": 2025,
"funding": "credit",
"authentication": "sca_required"
},
"fingerprint": "BKJYqf2tcvhTHSXN7EvBJLviN3PBYRgwoJgce8VAfnSr",
"_extras": {
"deduplicated": false
},
...
}
What you should store in your database:
- (Required) The token
id
, associating it with the rightful customer account. - The card details (
brand
,last4
,expiration_month
, andexpiration_year
) if your plan to use it for things like rendering a stored card to your customer.- Alternatively, you can later retrieve the token to get that information from the API.
- The Processor Token if you plan to directly charge that card with the Processor (without going through Basis Theory Proxy).
- The Network Transaction identifier returned by the processor during the initial transaction;
- This allows you to seamlessly charge the card with new processors or acquirers in the future and achieve the best approval rates.
- Refer to your current processor’s documentation or support for guidance on accessing this value.
Subsequent Charges
Once you've securely saved a card in the vault and generated a persistent token, you are ready to perform subsequent charges. Whether you have returning customers purchasing with a saved card, or you are performing recurring billing, the token allows you to process payments seamlessly without storing sensitive card details directly in your system.
Using the PAN
If you charged or verified a card used a different acquirer than the one required for subsequent charges, we recommend using this method, which relies on the Proxy to securely route tokenized cardholder data to the payment processor.
- Checkout.com
- Stripe
In Checkout.com, you can charge a saved card by calling the /payments
API.
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://api.sandbox.checkout.com/payments' \
-H 'Authorization: Bearer <CHECKOUT_SECRET_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "card",
"number": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data.number\" }}",
"expiry_month": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data\" | card_exp: \"MM\" }}",
"expiry_year": "{{ token_intent: d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4 | json: \"$.data\" | card_exp: \"YYYY\" }}",
"stored": true
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "pc_svyx12xvaaduraq3ckb2gwnvzq"
}'
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "card",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}
In Stripe, you can charge a saved card by creating a Payment Intent.
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://api.stripe.com/v1/payment_intents' \
-H 'Authorization: Bearer <STRIPE_SECRET_KEY>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'amount=1000' \
--data-urlencode 'currency=USD' \
--data-urlencode 'customer=cus_RTEOvM5dSx9RCS' \
--data-urlencode 'confirm=true' \
--data-urlencode 'payment_method_data[type]=card' \
--data-urlencode 'payment_method_data[card][number]={{ token: 1cd9c8bf-99fd-4784-ba69-68b15b301019 | json: "$.data.number" }}' \
--data-urlencode 'payment_method_data[card][exp_month]={{ token: 1cd9c8bf-99fd-4784-ba69-68b15b301019 | json: "$.data" | card_exp: "MM" }}' \
--data-urlencode 'payment_method_data[card][exp_year]={{ token: 1cd9c8bf-99fd-4784-ba69-68b15b301019 | json: "$.data" | card_exp: "YYYY" }}' \
--data-urlencode 'payment_method_data[card][cvc]={{ token: 1cd9c8bf-99fd-4784-ba69-68b15b301019 | json: "$.data.cvc" }}'
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"status": "succeeded",
"object": "payment_intent",
"payment_method": "pm_1QaKJmG9VRQ2700S5JcsJv4Q",
"latest_charge": {
"payment_method_details": {
"card": {
"network_transaction_id": "8723dfab9c4e56a1b8cd0291ef67ac43"
...
}
...
}
...
}
...
}
We are working to add more full-featured processor-specific examples. Please refer to our previous generic proxy guide for more request samples.
Don't hesitate to contact us if you need assistance with performing card transactions against a specific processor.
Using the Processor Token
If you have already charged or verified a card using a payments processor and saved its token, we recommend using this method, which doesn't require going through Basis Theory.
- Checkout.com
- Stripe
In Checkout.com, you can charge a saved card by calling the /payments
API.
curl 'https://api.sandbox.checkout.com/payments' \
-X 'POST' \
-H 'Authorization: Bearer <CHECKOUT_SECRET_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "id",
"id": "src_gguer4xlawke3ctljczft7573a",
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "pc_svyx12xvaaduraq3ckb2gwnvzq"
}'
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "card",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}
In Stripe, you can charge a card by creating a Payment Intent.
curl 'https://api.stripe.com/v1/payment_intents' \
-X 'POST' \
-H 'Authorization: Bearer <STRIPE_SECRET_KEY>' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'amount=1000' \
--data-urlencode 'currency=USD' \
--data-urlencode 'customer=cus_RTEOvM5dSx9RCS' \
--data-urlencode 'confirm=true' \
--data-urlencode 'payment_method=pm_1QaKJmG9VRQ2700S5JcsJv4Q'
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"status": "succeeded",
"object": "payment_intent",
"payment_method": "pm_1QaKJmG9VRQ2700S5JcsJv4Q",
"latest_charge": {
"payment_method_details": {
"card": {
"network_transaction_id": "8723dfab9c4e56a1b8cd0291ef67ac43"
...
}
...
}
...
}
...
}