DEV Community

Golden Alien
Golden Alien

Posted on

🛠️ merge_payment_csvs: Merge Gumroad, Stripe, and PayPal CSVs into a unified ledger for easy

Merge Payment CSVs Tool

This simple Python utility merges transaction data from Gumroad, Stripe, and PayPal CSV export files into a single, consistent ledger format. It's designed for creators and small businesses that use multiple payment platforms and want a unified view of their income for bookkeeping, tax reporting, or financial analysis.

Features

  • Automatically detects and parses CSVs from Gumroad, Stripe, and PayPal
  • Normalizes columns into a uniform schema: Date, Description, Amount (USD), Fee, Net, and Source
  • Handles different date formats and currency representations
  • Filters out non-transaction rows (refunds, transfers, fees-only entries) where needed
  • Outputs a clean CSV file sorted by date

Usage

Run the script from the command line:

python merge_payment_csvs.py --gumroad gumroad.csv --stripe stripe.csv --paypal paypal.csv --output ledger.csv
Enter fullscreen mode Exit fullscreen mode

If a platform wasn’t used in the period, omit its argument. The script will only process provided files.

Output

The resulting ledger.csv contains all sales transactions with consistent formatting, making it easy to import into Excel, Google Sheets, or accounting software.

Requirements

  • Python 3.6+
  • Standard library only — no external dependencies

Why This Exists

Managing multiple platforms means scattered records. Manual merging is error-prone and time-consuming. This tool automates the consolidation, saving hours each month and reducing mistakes in financial reporting.

Notes

  • Always back up original files before processing
  • Review output for edge cases (e.g. currency conversions, promotional discounts)
  • Designed for read-only transaction exports — no API keys or live data access

Maintained for simplicity and privacy: no data leaves your machine.

import csv
import argparse
from datetime import datetime
from pathlib import Path


def parse_gumroad_row(row):
    return {
        'Date': row['Created at'].split(' ')[0],
        'Description': f"Gumroad: {row['Product']}",
        'Amount': row['Sale price'],
        'Fee': row['Gumroad fee'],
        'Net': row['Amount sent to you'],
        'Source': 'Gumroad'
    }


def parse_stripe_row(row):
    if row['Type'] != 'charge':
        return None
    return {
        'Date': row['Created'].split(' ')[0],
        'Description': row['Description'],
        'Amount': row['Gross'],
        'Fee': row['Fee'],
        'Net': row['Net'],
        'Source': 'Stripe'
    }


def parse_paypal_row(row):
    if 'Sale' not in row.get('Type', ''):
        return None
    return {
        'Date': row['Date'],
        'Description': row['Name'],
        'Amount': row['Gross'],
        'Fee': row['Fee'],
        'Net': row['Net'],
        'Source': 'PayPal'
    }


def merge_ledger(gumroad_path, stripe_path, paypal_path, output_path):
    records = []
    sources = [
        (gumroad_path, parse_gumroad_row, 'Gumroad'),
        (stripe_path, parse_stripe_row, 'Stripe'),
        (paypal_path, parse_paypal_row, 'PayPal')
    ]

    for file_path, parser, name in sources:
        if not file_path:
            continue
        if not Path(file_path).exists():
            print(f"Warning: {name} file not found: {file_path}")
            continue
        with open(file_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                parsed = parser(row)
                if parsed:
                    records.append(parsed)

    # Sort by date
    records.sort(key=lambda x: datetime.strptime(x['Date'], '%Y-%m-%d'))

    # Write merged output
    with open(output_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['Date', 'Description', 'Amount', 'Fee', 'Net', 'Source'])
        writer.writeheader()
        writer.writerows(records)

    print(f"Merged {len(records)} transactions into {output_path}")


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Merge Gumroad, Stripe, and PayPal CSVs into one ledger.')
    parser.add_argument('--gumroad', help='Path to Gumroad CSV export')
    parser.add_argument('--stripe', help='Path to Stripe CSV export')
    parser.add_argument('--paypal', help='Path to PayPal CSV export')
    parser.add_argument('--output', required=True, help='Output CSV file path')
    args = parser.parse_args()

    merge_ledger(args.gumroad, args.stripe, args.paypal, args.output)

Enter fullscreen mode Exit fullscreen mode

Top comments (0)