Issue Cards
Whether you're building a fintech platform, corporate expense management system, digital wallet solution, or any application requiring card issuance capabilities, you may face hurdles while integrating with card issuers or BaaS (Banking as a Service) providers, including limited customization options, user experience constraints, and complex PCI-DSS compliance requirements. It is also fair to mention that truly owning the cardholder data is rarely possible without building your own Cardholder Data Environment (CDE) to perform simple tasks, like shipping the card information to a card switcher service or rewards program.
In this guide, we will leverage the Basis Theory Proxy API to set up a thin layer for handling the Issuer API responses and storing the newly issued cardholder data in a secure vault using tokenization. By following a small series of simple steps, we can unlock possibilities for using this sensitive data in various scenarios without taking on any unnecessary compliance scope.
Getting Started
To get started, you will need to create a Basis Theory Account and a TEST Tenant.
Provisioning Resources
In this section, we will explore the bare minimum resources necessary to call the Issuer API and securely store the card details.
Management Application
To create all subsequent resources, you will need a Management Application.
Click here to create a Management Application or login to your Basis Theory account and create a new application with the following settings:
- Name - Resource Creator
- Application Type - Management
- Permissions:
application:create
,proxy:create
Private Application
Next you will need a Private Application to create tokens within the Proxy, and invoke it from your servers. This application represents your Backend.
Using the Management Application key to authorize the request, call Basis Theory API to create a new Private Application:
curl "https://api.basistheory.com/applications" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Backend",
"type": "private",
"permissions": [ "token:create", "token:read", "token:use" ]
}'
<API_KEY>
with the Management API Key you created previously.key
and id
from the created Private Application as it will be used later in this guide.Pre-Configured Proxy
Now we will create a Proxy that intercepts the HTTP response from the Issuer API, which contains sensitive cardholder data that we need to store in our Basis Theory Tenant. To achieve that, we will leverage a Response Transform code that handles the response body to tokenize and redact the cardholder data, and include the new token:
- Lithic
- Marqeta
- Stripe
- Galileo
- Treasury Prime
- Mastercard
- Adyen
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const { token: lithicToken, pan, exp_month, exp_year, cvv, ...rest } = body;
const token = await bt.tokens.create({
id: lithicToken, // (optional) use the Lithic card token as Basis Theory card token
type: 'card',
data: {
number: pan,
expiration_month: exp_month,
expiration_year: exp_year,
cvc: cvv
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
...rest,
token, // replaces the Lithic token in the response to the Basis Theory token object
},
}
}
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const { token: marqetaToken, pan, expiration, cvv_number, ...rest } = body;
const token = await bt.tokens.create({
id: marqetaToken, // (optional) uses the Marqeta card token as Basis Theory card token
type: 'card',
data: {
number: pan,
expiration_month: expiration.slice(0, 2),
expiration_year: `20${expiration.slice(-2)}`,
cvc: cvv_number
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
...rest,
token, // replaces the Marqeta token in the response to the Basis Theory token object
},
}
}
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const { id, number, exp_month, exp_year, cvc, ...rest } = body;
const token = await bt.tokens.create({
id, // (optional) uses the Stripe card id as Basis Theory card token id
type: 'card',
data: {
number,
expiration_month: exp_month,
expiration_year: exp_year,
cvc
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
...rest,
token, // the Basis Theory token is added to the response
},
}
}
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const {
response_data: [{
card_id,
card_number,
expiry_date,
card_security_code,
...response_data
}],
...rest
} = body;
const expiration = new Date(expiry_date);
const token = await bt.tokens.create({
id: card_id, // (optional) uses the Galileo card id as Basis Theory card token id
type: 'card',
data: {
number: card_number,
expiration_month: expiration.getMonth() + 1,
expiration_year: expiration.getFullYear(),
cvc: card_security_code,
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
...rest,
response_data: [{
...response_data,
token, // the Basis Theory token is added to the response
}],
},
};
};
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const { id, pan, expiration, cvv, ...rest } = body;
const token = await bt.tokens.create({
id, // (optional) use the TP card id as Basis Theory card token
type: 'card',
data: {
number: pan,
expiration_month: expiration.slice(0, 2),
expiration_year: `20${expiration.slice(-2)}`,
cvc: cvv
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
...rest,
token, // the Basis Theory token is added to the response
},
}
}
const { create } = require('xmlbuilder2');
const WHITE_PIXEL =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=';
module.exports = async function (req) {
const { bt, args } = req;
const { body, headers } = args;
const doc = create(body);
const obj = doc.end({ format: 'object' });
const purchaseRequestDetail =
obj['S:Envelope']?.['S:Body']?.['ns2:getPurchaseRequestDetail'];
const vcnInformation = purchaseRequestDetail?.['ns2:vcnInformation'];
const cardImage = purchaseRequestDetail?.['ns2:cardImage'];
if (vcnInformation) {
const {
'ns2:Pan': number,
'ns2:Expiry': expiry,
'ns2:Avv': cvc,
} = vcnInformation;
const expirationMonth = expiry.slice(-2);
const expirationYear = `20${expiry.slice(0, 2)}`;
const token = await bt.tokens.create({
type: 'card',
data: {
number,
expirationMonth,
expirationYear,
cvc,
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
vcnInformation['ns2:Pan'] = token.id;
vcnInformation['ns2:Avv'] = '000';
}
if (cardImage) {
// redacts card image from response
purchaseRequestDetail['ns2:cardImage'] = cardImage.replace(
/base64,.+?"/,
`base64,${WHITE_PIXEL}"`
);
}
const transformed = create(obj).end();
return {
body: transformed,
headers,
};
};
module.exports = async function (req) {
const { bt, args, configuration } = req;
const { body, headers } = args;
const { pan, expiration: { month, year }, cvc } = body;
const token = await bt.tokens.create({
type: 'card',
data: {
number: pan,
expiration_month: month,
expiration_year: year,
cvc
},
fingerprintExpression: '{{ data.number }}',
deduplicateToken: true,
});
return {
headers,
body: {
token, // the Basis Theory token is added to the response
},
}
}
Notice how we are using fingerprintExpression
and deduplicateToken
properties to make sure we only create one card token per unique each card number, also known as Primary Account Number (PAN). Click here to learn more about deduplication.
Let's store the contents of the responseTransform.js
file into a variable:
response_transform_code=$(cat responseTransform.js)
And call Basis Theory API to create the Proxy:
- Lithic
- Marqeta
- Stripe
- Galileo
- Treasury Prime
- Mastercard
- Adyen
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://sandbox.lithic.com/v1/cards",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://sandbox-api.marqeta.com/v3/cards?show_cvv_number=true&show_pan=true",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://api.stripe.com/v1/issuing/cards",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://api-sandbox.cv.gpsrv.com/intserv/4.0/createVirtualCard",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://api.sandbox.treasuryprime.com/card",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://sandbox.api.mastercard.com/iccp/financial",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
curl "https://api.basistheory.com/proxies" \
-X "POST" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"name": "Issuer Proxy",
"destination_url": "https://balanceplatform-api-test.adyen.com/bcl/v2/paymentInstruments",
"response_transform": {
"code": '"$(echo $response_transform_code | jq -Rsa .)"'
},
"application": {
"id": "45c124e7-6ab2-4899-b4d9-1388b0ba9d04"
}
}'
Important things to notice in the request above:
<API_KEY>
is the Management Application Key, used to authenticate the request;45c124e7-6ab2-4899-b4d9-1388b0ba9d04
is the id of the Private Application, which is used to initialize thebt
instance injected in thereq
parameter;response_transform_code
is passed in plaintext form.
key
from the created Proxy as it will be used later to invoke it.Done! These are all the resources necessary. Let's see how to use them.
Calling the Issuer API
This is the last step of this guide, and it's also the one that your services will likely perform repeatedly based on your business requirements. Let's call the Issuer API through the Proxy, passing the Private Application key to authenticate the request, and the payload the endpoint expects:
- Lithic
- Marqeta
- Stripe
- Galileo
- Treasury Prime
- Mastercard
- Adyen
curl 'https://api.basistheory.com/proxy' \
-X "POST" \
-H "Content-Type: application/json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-H "Authorization: 5c2150b7-1c86-4f34-9d27-7f4465837e8d" \
-d '{
"type": "VIRTUAL"
}'
The Authorization
header includes Lithic API Key.
{
"created": "2023-05-25T13:15:15Z",
"last_four": "3671",
"hostname": "",
"memo": "VIRTUAL card",
"type": "VIRTUAL",
"spend_limit": 0,
"spend_limit_duration": "TRANSACTION",
"state": "OPEN",
"funding": {
"created": "2021-10-06T21:25:13Z",
"token": "2b191150-1a75-451a-a5fa-52e3cba2a5f6",
"type": "DEPOSITORY_CHECKING",
"state": "ENABLED",
"nickname": "",
"account_name": "Sandbox",
"last_four": "1883"
},
"auth_rule_tokens": [],
"token": {
"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 }}"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
}
curl 'https://api.basistheory.com/proxy' \
-X "POST" \
-H "Content-Type: application/json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-H "Authorization: Basic OGIyZjM3ZTUtYWU0My00YTM2LTljZjYtOTFjYmI5N2E4ZjkxOmY1YzllZDQ3LTNhODEtNDlkZi1hN2MyLWI5ODcyYjU2M2MxNw==" \
-d '{
"card_product_token": "69b58c85-60a2-4c36-88f4-2f8e78f5a9b6",
"user_token": "d4ae5e81-7f9d-45b4-8f17-56e4abdafd18"
}'
The Authorization
header includes Marqeta's Application token and Admin access token in the form of Basic HTTP Auth.
{
"created_time": "2023-05-25T13:33:22Z",
"last_modified_time": "2023-05-25T13:33:22Z",
"user_token": "fc601fb6-4b8b-4670-bad6-c1875cee7657",
"card_product_token": "45d0d874-2b89-487a-9296-ea92a9c6dc68",
"last_four": "3181",
"expiration_time": "2023-06-04T23:59:59Z",
"barcode": "19938128247771351193",
"pin_is_set": false,
"state": "ACTIVE",
"state_reason": "Digitally presented the card",
"fulfillment_status": "DIGITALLY_PRESENTED",
"instrument_type": "VIRTUAL_PAN",
"expedite": false,
"metadata": {},
"token": {
"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 }}"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
}
curl 'https://api.basistheory.com/proxy/<CARD_ID>?expand[]=number&expand[]=cvc' \
-X "GET" \
-H "Content-Type: application/json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-H "Authorization: Bearer sk_test_51KMGNYGuvJF9SIWEW0y4rKcaQwLVLck2rGB8UEPHzSp1utx7gXKAfZ3DVgjMfAuvBIT42pQhg0sIx2PepEJkXv9g00yIrUwhI4"
The Authorization
header includes the Stripe API Key, and <CARD_ID>
is added to the the URL. Notice that we also must instruct Stripe to expand the card number
and cvc
using the URL.
{
"id": "ic_1ND8RgGuvJF9SIWEmUMy57D9",
"object": "issuing.card",
"brand": "Visa",
"cancellation_reason": null,
"cardholder": {
"id": "ich_1ND8OKGuvJF9SIWEEqzwegKr",
"object": "issuing.cardholder",
"billing": {
"address": {
"city": "San Francisco",
"country": "US",
"line1": "1234 Main Street",
"line2": null,
"postal_code": "94111",
"state": "CA"
}
},
"company": null,
"created": 1685375632,
"email": "jenny.rosen@example.com",
"individual": null,
"livemode": false,
"metadata": {},
"name": "Jenny Rosen",
"phone_number": "+18888675309",
"requirements": {
"disabled_reason": "requirements.past_due",
"past_due": [
"individual.card_issuing.user_terms_acceptance.ip",
"individual.card_issuing.user_terms_acceptance.date",
"individual.first_name",
"individual.last_name"
]
},
"spending_controls": {
"allowed_categories": [],
"blocked_categories": [],
"spending_limits": [],
"spending_limits_currency": null
},
"status": "active",
"type": "individual"
},
"created": 1685375840,
"currency": "usd",
"last4": "0005",
"livemode": false,
"metadata": {},
"pin": null,
"replaced_by": null,
"replacement_for": null,
"replacement_reason": null,
"shipping": null,
"spending_controls": {
"allowed_categories": null,
"blocked_categories": null,
"spending_limits": [
{
"amount": 50000,
"categories": [],
"interval": "daily"
}
],
"spending_limits_currency": "usd"
},
"status": "inactive",
"type": "virtual",
"wallets": {
"apple_pay": {
"eligible": true,
"ineligible_reason": null
},
"google_pay": {
"eligible": true,
"ineligible_reason": null
},
"primary_account_identifier": null
},
"token": {
"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 }}"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
}
Stripe only returns number
and cvc
when retrieving an existing card. So you first must create the card directly, and then retrieve it using the Proxy.
curl 'https://api.basistheory.com/proxy' \
-X "POST" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "response-content-type: json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-d "apiLogin=A6Dyok-0439" \
-d "apiTransKey=oDSCeZmIF5YyiGqKtos4GFZ3" \
-d "providerId=78569" \
-d "transactionId=9e9d8aa5-63a2-5072-8d66-38424e0c3527" \
-d "prodId=6595"
The data parameters apiLogin
, apiTransKey
providerId
and transactionId
are all part of Galileo authentication. Notice that we instruct Galileo to return JSON content using the response-content-type
header.
{
"status_code": 0,
"status": "Success",
"processing_time": 0.916,
"response_data": [
{
"pmt_ref_no": "001109456010",
"product_id": "1665",
"galileo_account_number": "462881",
"cip": [
{
"status": "Pass",
"model_results": [
{
"model_name": "BancorpPIDC",
"model_version": "3",
"code": "Pass A",
"text": "Pass A"
}
],
"date_time": "2020-07-13 10:37:52"
}
],
"card_id": "850238",
"token": {
"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 }}"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
}
],
"echo": {
"provider_transaction_id": "",
"provider_timestamp": null,
"transaction_id": "RSNU68AN366U7I1UT8JQ"
},
"rtoken": "8cc16de0-5eda-4e2a-968e-3b08fce6f778",
"system_timestamp": "2020-07-13 10:37:52"
}
curl 'https://api.basistheory.com/proxy/<CARD_ID>?show_pan=true&show_cvv=true' \
-X "POST" \
-H "Content-Type: application/json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-H "Authorization: Basic OGIyZjM3ZTUtYWU0My00YTM2LTljZjYtOTFjYmI5N2E4ZjkxOmY1YzllZDQ3LTNhODEtNDlkZi1hN2MyLWI5ODcyYjU2M2MxNw=="
The Authorization
header includes Treasury Prime's API Key ID and API Key Value in the form of Basic HTTP Auth, while the <CARD_ID>
is added to the the URL. Notice that we also must instruct Treasury Prime to show pan and cvc using query parameters.
{
"account_id": "acct_291u96075mva",
"card_controls": null,
"card_product_id": "cdpt_w10r2sebv0nl",
"created_at": "2021-02-19T20:42:40Z",
"fulfillment": {
"status": "issued",
"shipping_method": "USPS Mail (5-7 business days)",
"tracking_number": null
},
"id": "card_zuhqnmz7e085",
"last4": "3385",
"person_id": "psn_5vyzn6sveupa",
"pin_is_set": false,
"status": "unactivated",
"updated_at": "2021-02-19T20:42:41Z",
"userdata": null
"token": {
"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/"
]
}
}
Treasury Prime only returns pan
and cvv
when retrieving an existing card. So you first must create the card directly, and then retrieve it using the Proxy.
curl 'https://api.basistheory.com/proxy?bt-proxy-key=TDEyQmkhQMpGiZd13FSRQ9' \
-X "POST" \
-H "Content-Type: text/xml" \
-H "BT-API-KEY: <API_KEY>" \
-H 'Authorization: OAuth oauth_body_hash="..."' \
-d '<?xml version="1.0"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header/>
<S:Body>
<submitPurchaseRequestRequest xmlns="http://mastercard.com/sd/pc2/service" xmlns:ns2="http://mastercard.com/sd/pc/service">
<SubmitPurchaseRequest>
<RCNData>
<ns2:rcnId>123456</ns2:rcnId>
<ns2:rcnAlias>Test RCN</ns2:rcnAlias>
</RCNData>
<companyId>123456</companyId>
<description>Purchase description</description>
<TemplateDetails2>
<templateId>12345</templateId>
<fullTemplateRuleDetails>
<ruleName>My Rule</ruleName>
<ruleType>A</ruleType>
<templateControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="transactionLimitControl">
<amount>100.0</amount>
</templateControl>
</fullTemplateRuleDetails>
</TemplateDetails2>
<supplierDetails>
<supplierId>12345</supplierId>
</supplierDetails>
</SubmitPurchaseRequest>
</submitPurchaseRequestRequest>
</S:Body>
</S:Envelope>'
The Authorization
header is created by your application to authenticate against Mastercard API. It is likely that you will use a SOAP client to create the request and send to Mastercard.
Remember to change the URL to target the Proxy, and add the BT-API-KEY
header.
<?xml version="1.0"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getPurchaseRequestDetail xmlns="http://mastercard.com/sd/pc/service" xmlns:ns2="http://mastercard.com/sd/pc2/service">
<ns2:purchaseRequestId>123456789</ns2:purchaseRequestId>
<ns2:requestStatus>Approved</ns2:requestStatus>
<ns2:PurchaseRequestTemplateDetails>
<ns2:templateId>12345</ns2:templateId>
<ns2:fullTemplateRuleDetails>
<ns2:ruleName>My Rule</ns2:ruleName>
<ns2:ruleType>A</ns2:ruleType>
<ns2:templateControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:transactionLimitControl">
<ns2:amount>100.0</ns2:amount>
<ns2:negate>false</ns2:negate>
</ns2:templateControl>
</ns2:fullTemplateRuleDetails>
</ns2:PurchaseRequestTemplateDetails>
<ns2:vcnInformation>
<ns2:Id>18015011</ns2:Id>
<ns2:Pan>d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4</ns2:Pan>
<ns2:Expiry>2512</ns2:Expiry>
<ns2:Avv>000</ns2:Avv>
<ns2:Status>S</ns2:Status>
<ns2:EVCNIndicator>false</ns2:EVCNIndicator>
</ns2:vcnInformation>
<ns2:addenda>
<tripLeg/>
<railDetail/>
<travelAgency/>
</ns2:addenda>
<ns2:cardImage><?xml version="1.0" encoding="UTF-8"?><html><head/> <body style="margin:0; padding:0; width:100%"> <div style="position: relative;"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=" style="height: 250px; width: 800px;"/>
</div></body></html></ns2:cardImage>
<ns2:supplierDetails>
<ns2:supplierId>12345</ns2:supplierId>
<ns2:notifySupplier>false</ns2:notifySupplier>
</ns2:supplierDetails>
</ns2:getPurchaseRequestDetail>
</S:Body>
</S:Envelope>
curl 'https://api.basistheory.com/proxy/<PAYMENT_INSTRUMENT>/reveal' \
-X "GET" \
-H "Content-Type: application/json" \
-H "BT-PROXY-KEY: TDEyQmkhQMpGiZd13FSRQ9" \
-H "BT-API-KEY: <API_KEY>" \
-H 'X-API-KEY: Vt(JJ5U5xuVECtg59fm9hBM+cZMWhw+ms2edxM%Rwmu0=Z2n3rGiQjQr-YEYfAq((It-Ocb03Jfob1JqGhogg:J/skGLIwerM=uAuHQDFHZBh+75pKgznYB3QeL7mrnBSeh34YAxLjdGEJQAhKdaU2'
The Authorization
header includes the Adyen API Key, and <PAYMENT_INSTRUMENT>
is added to the the URL.
{
"token": {
"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 }}"
},
"search_indexes": [],
"containers": [
"/pci/high/"
]
}
}
Adyen only returns pan
and cvc
when calling the special /reveal
endpoint for a payment instrument an existing card. So you first must create the payment instrument directly, and then reveal it using the Proxy.
<API_KEY>
with the Private Application Key and TDEyQmkhQMpGiZd13FSRQ9
with the Proxy Key you created previously.Key Considerations
Flexibility
You are not restricted to the Card Issuers listed above. As long as your partner can send you the card information through an API response, you can redact and tokenize it using the Pre-Configured Proxy.
CVC Retention
Card tokens retain verification code for 1 hour. This limitation is enforced by default to comply with PCI-DSS guidelines. However, depending on your business case, extending this period or implementing technical workarounds may be possible upon analysis. Please, reach out to our support team for assistance with this or other subjects.
Issuer Requirements
If you find that the Issuer API expects any form of Personal Identifiable Information (PII) in the Request Payload, you can also use the Proxy to detokenize sensitive data before hitting the downstream endpoint.
It's important to note that for some Issuers, 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.
Integrations
A major benefit of vaulting issued cards is the ability to work with their data and send it anywhere. You may find useful to offer value-added services to your users, like a rewards program, the ability to monitor transactions, manage expenses or update payment methods in 3rd party services.
Check out this guide to learn how to route stored cards to 3rd party APIs. While it is mainly geared towards payments processing, the concepts are virtually the same.
Display Card to End User
By vaulting your issued cards with Basis Theory you automatically unlock the ability to display them to your end users using our highly customizable and developer-friendly Elements.
Check out this guide to learn how to securely display cards in your Web or Mobile application.
Conclusion
By using our Pre-Configured Proxy, you can confidently receive cardholder data from a Card Issuer 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 Issuers and decide what to do with the issued cards' data.