5 min read

What Is Revenue Reporting in Stripe

Revenue reporting in Stripe is straightforward in concept but scattered in practice. Stripe tracks every transaction, fee, and refund—but whether you're looking at gross revenue, net settlement, or something in between depends on where you're checking. This matters for financial accuracy, tax compliance, and forecasting.

What Stripe Actually Reports

Stripe tracks several revenue dimensions. Knowing which one answers your question saves time.

Charges: The Core Revenue Unit

A Charge is Stripe's fundamental revenue object. Every successful payment—card, ACH, ACM—creates a charge. Each charge has an amount, currency, status, and created timestamp. When you see revenue metrics in Stripe's Dashboard, they're aggregated from charges. Most integrations start by querying charges.

javascript
const stripe = require('stripe')('sk_test_...');

// List charges from the last 30 days
const charges = await stripe.charges.list({
  limit: 100,
  created: {
    gte: Math.floor(Date.now() / 1000) - 86400 * 30
  }
});

const totalRevenue = charges.data.reduce((sum, charge) => sum + charge.amount, 0);
console.log(`Total: $${totalRevenue / 100} (amount in cents)`);
Charges are where all revenue metrics start.

Gross vs. Net: Understanding the Gap

Stripe reports gross revenue (what customers paid) in the Dashboard. But your actual revenue is net (gross minus Stripe's fee). For card charges, Stripe takes 2.9% + $0.30. Your bank account only receives the net amount. This is the #1 reconciliation headache.

javascript
// Get what you actually received (net revenue)
const balance = await stripe.balance.retrieve();

const available = balance.available[0]; // ready to payout
const pending = balance.pending[0];     // settling in 1-2 days

console.log(`Available balance: $${available.amount / 100}`);
console.log(`Pending: $${pending.amount / 100}`);
console.log(`Total: $${(available.amount + pending.amount) / 100}`);
Balance shows net revenue—what actually hits your bank.

Status Matters: Succeeded vs. Failed vs. Refunded

Successful charges contribute to revenue. Failed charges don't. Refunded charges reduce revenue. Stripe webhooks and the Charges API both track status changes. If you don't filter for status: 'succeeded', you'll overcount revenue.

javascript
// Only count succeeded charges
const succeededCharges = await stripe.charges.list({
  status: 'succeeded',
  limit: 100
});

// Count refunded charges
const refundedCharges = await stripe.charges.list({
  limit: 100
});

const refundedAmount = refundedCharges.data
  .filter(c => c.refunded === true)
  .reduce((sum, c) => sum + c.amount_refunded, 0);

console.log(`Refunded: $${refundedAmount / 100}`);
Always filter by status to get accurate revenue.
Tip: Stripe's Dashboard shows gross; your bank shows net. Reconcile monthly against actual deposits, not Dashboard numbers.

Accessing Revenue Data

Three ways to get revenue data: the Dashboard, API queries, and webhooks. Each has trade-offs.

Use the Dashboard for Quick Snapshots

Go to ReportingPayments for transaction-level details or Balance for your current settlement. Payouts shows when money reaches your bank account. These views are fast but not suitable for automation or large-scale analysis.

javascript
// Automate Dashboard checks with API queries
const charges = await stripe.charges.list({
  limit: 100,
  created: {
    gte: Math.floor(Date.now() / 1000) - 86400 // last 24 hours
  }
});

console.log(`Charges in last 24h: ${charges.data.length}`);
Automate Dashboard checks with API queries.

Query Charges for Custom Reports

The Charges API lets you filter by date, customer, status, amount range, and payment method. Build revenue reports, reconciliation tools, or customer lifetime value calculations by querying charges. This is how most finance integrations work.

javascript
// Revenue by customer in last quarter
const charges = await stripe.charges.list({
  limit: 100,
  status: 'succeeded',
  created: {
    gte: Math.floor(new Date('2025-01-01').getTime() / 1000),
    lte: Math.floor(new Date('2025-03-31').getTime() / 1000)
  }
});

const revenueByCustomer = {};
charges.data.forEach(charge => {
  const customerId = charge.customer;
  revenueByCustomer[customerId] = (revenueByCustomer[customerId] || 0) + charge.amount;
});

console.log('Customer revenue:', revenueByCustomer);
Build custom financial reports by querying charges.

Listen to Webhooks for Real-Time Tracking

Webhooks fire when charges succeed, fail, or are refunded. Use webhooks to update your own database or send data to accounting software in real time, rather than batch-querying charges. This is essential for accuracy-critical systems.

javascript
// Webhook endpoint to track revenue in real time
const express = require('express');
const app = express();

app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const event = stripe.webhooks.constructEvent(
    req.body,
    sig,
    'whsec_test_...'
  );

  if (event.type === 'charge.succeeded') {
    const charge = event.data.object;
    console.log(`New revenue: $${charge.amount / 100}`);
  }

  if (event.type === 'charge.refunded') {
    const charge = event.data.object;
    console.log(`Refund: $${charge.amount_refunded / 100}`);
  }

  res.json({received: true});
});
Webhooks enable real-time financial tracking.
Watch out: Webhooks can arrive out of order or duplicate. Stripe's idempotency handles API retries, but your webhook handler should be idempotent too.

Common Pitfalls

  • Confusing gross and net. Dashboard shows gross; your bank gets net. This is the #1 reconciliation mistake.
  • Not filtering by status. stripe.charges.list() returns all charges. You need status: 'succeeded' to see actual revenue.
  • Assuming batch queries catch everything. Stripe's list API paginates at 100 items; use starting_after to iterate through all charges.
  • Relying on webhooks alone. Webhooks can fail or be delayed. Always verify with periodic API queries.

Wrapping Up

Revenue reporting in Stripe comes down to three tools: the Dashboard for quick checks, the Charges API for custom reporting, and webhooks for real-time tracking. Most robust systems use all three. If you want to track revenue automatically across multiple tools and create unified financial dashboards, Product Analyst can help.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free