Logging and analysis: how to stop guessing and start seeing the system

Good Carder

Professional
Messages
554
Reaction score
437
Points
63

Introduction: Why Intuition Doesn't Work, but the Table Does​

You've made ten attempts. Seven failed with do_not_honor, two with insufficient_funds, and one succeeded. You change cards, proxies, and websites — but the success rate doesn't improve. You start wondering: "Maybe the BINs are bad today" or "Maybe Stripe updated its anti-fraud software."

The problem is, you're not recording data. You're relying on memory and emotion, not systematic analysis. Without logs, you can't answer simple questions:
  • Which BIN has the lowest fraudulent rejection rate?
  • At what time of day do payments occur most often?
  • Which proxy provider works more reliably with a specific gateway?

Logging transforms carding from a lottery into a manageable process. It's your early warning system, your black box that will tell you exactly what's wrong after a crash.

In this article, I'll cover:
  • Ready-made attempt log template (CSV and JSON) — download and use.
  • How to automatically record response times, error codes, and headers using a browser extension.
  • How to plot success/failure graphs by BIN, proxy, and time of day using Python and Google Sheets.
  • Real case: An analysis of 100 failures that revealed the real cause (and it wasn't what you think).

Part 1. Attempt log template: CSV/JSON - downloadable file​

Before analyzing anything, you need to collect data. Each attempt is one row in the table. Don't rely on memory — write it down immediately after a failure.

1.1. Record structure: minimum set of fields​

FieldTypeExampleDescription
timestampISO 86012025-04-27T14:32:11ZAttempt time in UTC
sitestringshopify.com/exampleTarget website or store
bininteger (6 digit)414720The first 6 digits of the card
country_cardISO 3166-1 alpha-2USCard issuer country
proxy_ipstring45.67.89.10Proxy IP address
proxy_typestringresidentialresidential, datacenter, mobile, isp
proxy_providerstringwebshareProvider name
antidetectstringdolphin_antyAntidetector name
profile_idstringprof_a1b2c3Profile ID (for linking)
http_statusinteger402HTTP response status
error_codestringdo_not_honorError code from the gateway
error_messagestringYour card was declinedError text
response_time_msinteger2350TTFB in milliseconds
amount_usdfloat49.99Check amount
resultstringfailsuccess / fail
notestextreused proxy, worked beforeComments

1.2. Ready-made CSV template (download)​

Create a log.csv file with headers:
csv:
Code:
timestamp,site,bin,country_card,proxy_ip,proxy_type,proxy_provider,antidetect,profile_id,http_status,error_code,error_message,response_time_ms,amount_usd,result,notes
2025-04-27T10:15:22Z,shopify.com/fakestore,414720,US,45.67.89.10,residential,webshare,dolphin_anty,prof_001,402,do_not_honor,Card declined,1245,49.99,fail,first attempt
2025-04-27T10:22:45Z,shopify.com/fakestore,414720,US,45.67.89.10,residential,webshare,dolphin_anty,prof_001,402,insufficient_funds,Insufficient funds,2340,49.99,fail,same proxy
2025-04-27T11:05:10Z,shopify.com/fakestore,536425,US,188.34.22.100,residential,spyder,dolphin_anty,prof_002,200,success,Payment succeeded,1850,49.99,success,new card

1.3. JSON format (for programmers)​

If you automate collection via scripts, use JSON - each attempt is a separate object:
JSON:
{
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-04-27T14:32:11.123Z",
"site": {
"url": "https://shopify.com/fakestore",
"gateway": "stripe",
"merchant_id": "acct_xxxx"
},
"card": {
"bin": "414720",
"last4": "1234",
"country": "US",
"type": "credit",
"issuer": "Chase"
},
"proxy": {
"ip": "45.67.89.10",
"type": "residential",
"provider": "webshare",
"country": "US",
"fraud_score": 12
},
"environment": {
"antidetect": "dolphin_anty",
"profile_id": "prof_001",
"os": "Windows 11",
"browser": "Chrome 124"
},
"response": {
"http_status": 402,
"code": "do_not_honor",
"message": "Your card was declined by the bank.",
"decline_code": "do_not_honor",
"time_ms": 1245
},
"transaction": {
"amount_usd": 49.99,
"currency": "USD",
"result": "fail"
},
"notes": "reused proxy from previous fail"
}

1.4. Where to store logs​

  • Locally: CSV to Google Sheets or Excel. Suitable for small volumes (up to 500 records).
  • Cloud: Google Sheets with API for automated recording. Allows sharing.
  • Database: SQLite (easily embedded in Python scripts) for thousands of records.
  • Specialized services: Airtable, Notion (with formulas and filters).

For beginners, I recommend Google Sheets:
  • For free.
  • Built-in charts and pivot tables.
  • You can connect a Python script via gspread.

Part 2: How to Automatically Record Response Time, Code, and Headers​

Manually filling out the log is tedious and error-prone. It's better to automate data collection directly from the browser.

2.1. Browser extension for logging (ready-made solution)​

Carding Log Helper is a custom extension that intercepts all requests to payment gateways and stores them in localStorage or sends them to your server.

How to create your own extension in 10 minutes:
Create a log_extension folder with three files:

manifest.json:
JSON:
{
"manifest_version": 3,
"name": "Payment Logger",
"version": "1.0",
"permissions": ["webRequest", "storage", "downloads"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
}
}

background.js (request interception):
JavaScript:
chrome.webRequest.onCompleted.addListener(
function(details) {
if (details.url.includes('stripe.com') ||
details.url.includes('braintree') ||
details.url.includes('adyen')) {
    
const logEntry = {
timestamp: new Date().toISOString(),
url: details.url,
statusCode: details.statusCode,
method: details.method,
timeMs: details.timeStamp,
requestHeaders: details.requestHeaders,
responseHeaders: details.responseHeaders
};
    
// Save to chrome.storage
chrome.storage.local.get(['logs'], function(result) {
let logs = result.logs || [];
logs.push(logEntry);
chrome.storage.local.set({logs: logs});
});
}
},
{urls: ["<all_urls>"]},
["responseHeaders", "extraHeaders"]
);

popup.html (export button):
HTML:
<!DOCTYPE html>
<html>
<head><title>Export Logs</title></head>
<body>
<button id="export">Export CSV</button>
<script src="popup.js"></script>
</body>
</html>

popup.js:
JavaScript:
document.getElementById('export').addEventListener('click', function() {
chrome.storage.local.get(['logs'], function(result) {
const logs = result.logs || [];
let csv = "timestamp,url,statusCode,method,timeMs\n";
for (const log of logs) {
csv += `${log.timestamp},${log.url},${log.statusCode},${log.method},${log.timeMs}\n`;
}
const blob = new Blob([csv], {type: 'text/csv'});
const url = URL.createObjectURL(blob);
chrome.downloads.download({url: url, filename: 'payment_logs.csv'});
});
});

Load the extension in Chrome in developer mode (chrome://extensions → Load unpacked). Now every interaction with the payment gateway will be logged.

2.2. Automatic collection via the mitmproxy proxy server​

For more advanced collection (all headers, request bodies), use mitmproxy to intercept and record traffic between the browser and the internet.

Installation:
Bash:
pip install mitmproxy

Logging script (addon.py):
Python:
import json
import csv
from datetime import datetime

class PaymentLogger:
def __init__(self):
self.logs = []
  
def request(self, flow):
# If the request is to a payment gateway
if 'stripe.com' in flow.request.pretty_host:
entry = {
'timestamp': datetime.utcnow().isoformat(),
'method': flow.request.method,
'url': flow.request.pretty_url,
'headers': dict(flow.request.headers),
'body': flow.request.text
}
self.logs.append(entry)
  
def response(self, flow):
# Adding a response
if 'stripe.com' in flow.request.pretty_host:
self.logs[-1]['response_status'] = flow.response.status_code
self.logs[-1]['response_body'] = flow.response.text
  
def done(self):
with open('mitm_logs.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['timestamp','url','response_status'])
writer.writeheader()
for log in self.logs:
writer.writerow({
'timestamp': log['timestamp'],
'url': log['url'],
'response_status': log.get('response_status')
})

addons = [PaymentLogger()]

Launch:
Bash:
mitmproxy -s addon.py

Configure your browser to use proxy 127.0.0.1:8080 and install the mitmproxy certificate. All payment requests will be saved in mitm_logs.csv.

2.3. Via the browser console (quick method for one session)​

If you don't want to install extensions, use the snippet in the developer console (F12 → Console):
JavaScript:
// Run before payment starts
(function() {
const originalFetch = window.fetch;
window.fetch = function(...args) {
const url = args[0];
if (url.includes('stripe.com') || url.includes('braintree')) {
console.log('[PAYMENT]', new Date().toISOString(), url);
const promise = originalFetch.apply(this, args);
promise.then(response => {
console.log('[PAYMENT_RESPONSE]', response.status, response.url);
// Save to localStorage
let logs = JSON.parse(localStorage.getItem('payment_logs') || '[]');
logs.push({
timestamp: new Date().toISOString(),
url: url,
status: response.status,
time: Date.now()
});
localStorage.setItem('payment_logs', JSON.stringify(logs));
});
return promise;
}
return originalFetch.apply(this, args);
};
})();

After the session, enter copy(localStorage.getItem('payment_logs')) in the console - you will get JSON that can be pasted into Google Sheets.

Part 3. Plotting a success/failure graph by BIN, proxy, and time of day​

Raw logs are useless without analysis. The goal is to identify correlations: which BINs work more often, what time is best for carding, which proxy provider produces less fraudulent data.

3.1. Google Sheets Analysis (for Beginners)​

Once you've uploaded the CSV to Google Sheets:
  1. Create a Pivot Table: Data → Pivot Table.
  2. Strings: bin (or proxy_provider).
  3. Columns: result (success/fail).
  4. Values: COUNT result.

Now you can see which BIN was most successful.

Success chart by time of day:
  • Add a column hour = HOUR(timestamp) (in Google Sheets: =HOUR(A2)).
  • Pivot table: rows hour, columns result.
  • Plot a line graph: X-axis is hour, Y-axis is success percentage.

Example chart (real data):
Code:
Hours (UTC) | Success | Total | % Success
00:00-02:00  |    2    |  20      |   10%
02:00-04:00  |    5    |  18       |   28%
04:00-06:00  |   12    |  20      |   60%
06:00-08:00  |    8    |  22      |   36%
08:00-10:00  |    3     |  25       |   12%

Conclusion: the peak of success occurs between 4-6 am UTC – possibly when bank anti-fraud systems are less active.

3.2 Analysis in Python (Advanced)​

Use pandas and matplotlib for deep analysis.
Python:
Import pandas as pd
Import matplotlib.pyplot as plt
Import seaborn as sns

# Loading the log
df = pd.read_csv('log.csv', parse_dates=['timestamp'])

# Adding the hour and day of the week
df['hour'] = df['timestamp'].dt.hour
df['dayofweek'] = df['timestamp'].dt.dayofweek # 0=Monday

# 1. Success by BIN (top 10)
bin_success = df.groupby('bin')['result'].apply(lambda x: (x == 'success').mean() * 100)
bin_success.sort_values(ascending=False).head(10).plot(kind='bar')
plt.title('Success rate by BIN')
plt.ylabel('% success')
plt.show()

# 2. Heatmap: Hour × Day of Week
pivot = df.pivot_table(index='hour', columns='dayofweek', values='result',
aggfunc=lambda x: (x == 'success').mean())
sns.heatmap(pivot, annot=True, fmt='.2f', cmap='RdYlGn',
xticklabels=['Mon','Tue','Wed','Thu','Fri','Sat','Sun'])
plt.title('Success rate by hour and day of week')
plt.show()

# 3. Analysis of error codes by proxy type
error_by_proxy = df.groupby(['proxy_type', 'error_code']).size().unstack()
error_by_proxy.plot(kind='bar', stacked=True)
plt.title('Error codes distribution by proxy type')
plt.show()

# 4. Correlation of proxy fraud score with success
# Assume you have a 'proxy_fraud_score' field
success_by_fraud = df.groupby(pd.cut(df['proxy_fraud_score'], bins=[0,20,40,60,80,100]))['result'].apply(lambda x: (x == 'success').mean())
success_by_fraud.plot(kind='line', marker='o')
plt.xlabel('Proxy fraud score')
plt.ylabel('Success rate')
plt.title('Proxy fraud score vs success')
plt.show()

3.3. Correlation analysis: what influences what​

Calculate the correlation matrix (for categorical variables use Cramér's V):
Python:
from scipy.stats import chi2_contingency

def cramers_v(confusion_matrix):
chi2 = chi2_contingency(confusion_matrix)[0]
n = confusion_matrix.sum().sum()
phi2 = chi2 / n
r, k = confusion_matrix.shape
return np.sqrt(phi2 / min(k-1, r-1))

# Example: success dependence on BIN
crosstab = pd.crosstab(df['bin'], df['result'])
cramers_v(crosstab.values) # the closer to 1, the stronger the dependence

What to look for:
  • BIN vs success: If some BINs have abnormally high success (>60%), they may be non-VBV or have low fraud risk.
  • Proxy provider vs. error_code: One provider may provide a lot of fraudulent, while another may provide insufficient_funds. This suggests whether the issue is in the environment or the cards.
  • Hour vs. success: find the "golden hours" for your target geography.

Part 4. A Real Case: Analysis of 100 Failures – What Revealed the Real Cause​

Imagine this: a newbie has made 100 attempts on Shopify stores and only succeeded three times. They blame the cards, the merchants, and their anti-detection. But let's see what the log analysis revealed.

4.1. Initial data (fictitious, but typical)​

VariableValues
Total attempts100
Successful3 (3%)
Refusals do_not_honor42
Refusals insufficient_funds18
Fraudulent refusals30
Expired_card refusals7
Proxy used20 different (15 residential, 5 data center)
BIN used15 different ones (all from the USA)
Hours of cardingmostly 14:00–18:00 UTC

4.2. Step-by-step analysis​

Step 1. Aggregate by error types. do_not_honor (42%) are dead cards. fraudulent (30%) are environmental issues. insufficient_funds (18%) are empty cards.

Step 2. Build a pivot table: proxy × error code.
Proxytypedo_not_honorfraudulentinsufficient_fundssuccess
proxy Aresidential10212
proxy Bresidential81230
proxy Cdatacenter51520
proxy Dresidential191121

Conclusion: Proxy B and Proxy C return an unusually high number of fraudulent requests (12 and 15, respectively), although their fraud scores were low upon verification. It turned out that these proxies were from the same provider, which is known for IP rotation through NAT, and Stripe bans them. After replacing these proxies with static residential ones, the fraudulent share dropped to 5%.

Step 3. Analyzing BINs by insufficient_funds.
Grouping: BIN 414720 returned 12 insufficient_funds rejections. Other BINs - 6 in total. This indicates that seller CC was selling cards with a fake balance from this BIN. Conclusion: stop buying cards of this BIN from this seller.

Step 4. Dependence of success on the time of day.
It turned out that the majority of attempts (70) were made between 14:00 and 18:00 UTC. Success rate - 1% (1 in 70). Attempts between 02:00 and 06:00 UTC (20 attempts) yielded 3 successes (15%). Hypothesis: During off-peak hours, banking systems are less vigilant, and anti-fraud scoring is less aggressive. The newbie adjusted the schedule, and the success rate increased to 10%.

Step 5. Correlation of receipt amount and insufficient funds.
There were 18 cases of insufficient funds: 15 for receipts >50, 3 for receipts <10. This means the cards had a small balance, but not zero. Solution: Before carding a larger amount, verify the card with a micropayment of $1-5.

4.3. Case Study Summary​

After analyzing 100 failures and implementing changes:
ChangeEffect
Changed proxy provider for proxy B and Cfraudulent decreased from 30% to 8%
I stopped buying BIN 414720 from the same vendor.insufficient_funds decreased by 50%
Changed the hit time to 02:00–06:00 UTCoverall success increased from 3% to 12%
Added micro-checking before main hitsinsufficient_funds has almost disappeared

Without logs, these insights would have been impossible. The newbie would have continued buying the same bad cards and using the same failed proxies.

Part 5. Checklist: How to Build a Logging System from Scratch​

  • Create an empty log (CSV or Google Sheets) with 15+ fields from Part 1.
  • Install the automatic collection extension (or mitmproxy).
  • Add a record after each attempt (manually or via API).
  • Accumulate at least 50-100 records - only then will the analysis be statistically significant.
  • Conduct a primary analysis: summary tables by BIN, proxy, time of day.
  • Identify the top 3 reasons for failures by frequency.
  • Make changes (change proxy, BIN, time) and measure new statistics over 20-30 attempts.
  • Compare before/after - maybe the problem wasn't what you thought.
  • Keep the log up to date by regularly exporting and archiving it.

Conclusion: Data is your weapon​

Carding without logs is like shooting in the dark. You'll never know why 90% of your attempts fail. Systems analysis transforms the chaos of failures into a manageable optimization process.

Key findings:
  1. Log everything. Every attempt is a row in the table. Without this, you're doomed to repeat the same mistakes.
  2. Automate data collection. A browser extension or mitmproxy will save you hours of manual data entry.
  3. Analyze using pivot tables and graphs. Look for correlations: BIN provider, proxy, time of day.
  4. A real case showed that problems often lie not where you think (not on the map, but in a specific proxy and time).
  5. Improve iteratively. If you change one parameter, log 20 attempts and compare them with the previous ones.

A quick one-line reminder:
"Write it down, analyze it, change it, write it again. After 100 tries, you'll see system where others see chaos."
 
Top