Process Bank Payments
Storing your customers' bank accounts 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 bank account information to any third-party API endpoint, with minimal configuration requirements. The steps below take into consideration NACHA's sensitive financial information requirements related to Data Security, Data Transmission, Audit Trails and Access Controls.
If you are not yet storing your customers' bank accounts with Basis Theory, here are a few guides you can explore:
- Collect Bank Accounts - capture bank accounts in the frontend;
- Collect Inbound Sensitive Data via API - receive data in API requests;
- Import from a Database - migrate to Basis Theory.
Getting Started
To get started, you will need to create a Basis Theory Account and a TEST Tenant.
Creating a Private Application
Next, you will need a Private Application using our NACHA-compliant template Use Bank Tokens
. Click here to create one.
This will create an application with the following Access Controls:
- Permissions:
token:use
- Containers:
/bank/
- Transform:
reveal
Send the Data
We will use Basis Theory Ephemeral Proxy, a tool that transparently performs detokenization, to share the sensitive banking data with 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 account information in the expected payload contents with detokenization expressions that contain token identifiers.
For example, given you have a previously stored bank token with the following identifier:
{
"id": "f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b",
"type": "bank",
"tenantId": "4aee08b9-5557-474b-a120-252e01fc7b0f",
"data": {
"routing_number": "021000021",
"account_number": "XXXXX3123"
},
"createdBy": "f5c44560-8433-4dcc-b67f-53594c397a5e",
"createdAt": "2023-10-26T14:27:10.6126956+00:00",
"mask": {
"routingNumber": "{{ data.routing_number }}",
"accountNumber": "{{ data.account_number | reveal_last: 4 }}"
},
"searchIndexes": [],
"containers": [
"/bank/high/"
]
}
Here are a few integration examples of how to send the detokenized banking information to third party APIs.
- Stripe
- Adyen
- Dwolla
- Finix
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <API_KEY>' \
-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=us_bank_account' \
--data-urlencode 'us_bank_account[account_holder_type]=individual' \
--data-urlencode 'us_bank_account[routing_number]={{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: "$.routing_number" }}' \
--data-urlencode 'us_bank_account[account_number]={{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: "$.account_number" }}' \
--data-urlencode 'billing_details[name]=John Doe'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <API_KEY>' \
-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 '{
"merchantAccount": "BasisTheoryECOM",
"amount": {
"value": 100,
"currency": "USD"
},
"reference": "YOUR_ORDER_NUMBER",
"paymentMethod": {
"type": "ach",
"bankLocationId": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.routing_number\" }}",
"bankAccountNumber": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.account_number\" }}",
"ownerName": "John Smith"
},
"billingAddress":{
"houseNumberOrName":"50",
"street":"Test Street",
"city":"Amsterdam",
"stateOrProvince":"NY",
"postalCode":"12010",
"country":"US"
}
}'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <API_KEY>' \
-H 'BT-PROXY-URL: https://api.dwolla.com/funding-sources' \
-H 'Authorization: Bearer connect.eyJraWQiOiJPNVVOUVR0VHdDSXBcL0lsYnNlWld...M7YDfoXxH9MqsABvcoRJaQoSMjDCgheu1h0clQ' \
-H 'Content-Type: application/json' \
-d '{
"_links": {
"treasury-account": {
"href": "https://api.dwolla.com/treasury-accounts/ea5f9468-4374-410f-b52e-5461d8cbca98"
}
},
"name": "My Savings Bank",
"bankAccountType": "savings",
"routingNumber": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.routing_number\" }}",
"accountNumber": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.account_number\" }}"
}'
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <API_KEY>' \
-H 'BT-PROXY-URL: https://finix.sandbox-payments-api.com/payment_instruments' \
-u 'USsRhsHYZGBPnQw8CByJyEQW:8a14c2f9-d94b-4c72-8f5c-a62908e5b30e'
-H 'Content-Type: application/json' \
-d '{
"account_number": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.account_number\" }}",
"account_type": "BUSINESS_CHECKING",
"bank_code": "{{ f910b9aa-a4a6-4f24-9ec4-2de1a5731d0b | json: \"$.routing_number\" }}",
"identity": "IDpYDM7J9n57q849o9E9yNrG",
"name": "Smith & Associates Consulting",
"type": "BANK_ACCOUNT"
}'
Key Considerations
You are not restricted to the Payment Processors listed above. As long as your partner can accept banking information through an API endpoint, you can invoke it using the Ephemeral Proxy.
If you find that the HTTPS response from your processor contains sensitive 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 bank accounts documented by your payment processor, following the desired test scenario. Passing incorrect data to test/sandbox endpoints may lead to hard-to-debug rejected transactions.
Conclusion
By using our Ephemeral Proxy, you can confidently transmit banking data to Payment Processors via API requests without ever touching the account 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.