Skip to content

Stripe Invoices — Billing Documentation and Automation

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Stripe Invoices. We cover key concepts, practical examples, and best practices to help you master this topic.

Stripe invoices document charges and payments for subscriptions and one-time purchases, with automatic generation, email delivery, PDF download, and customizable branding.

What You'll Learn

By the end of this lesson you will understand how invoices work in Stripe, how to create and send invoices, handle automatic collection, customize appearance, and manage billing cycles.

Why It Matters

Proper invoicing is essential for business Compliance, customer transparency, and tax reporting. Stripe automates invoice generation, sending, and payment collection while providing professional PDF invoices.

Real-World Use

DodaZIP generates invoices automatically for all subscription payments. Customers receive email notifications with PDF invoices itemizing charges, taxes, and payment details.

flowchart LR
    S[Subscription] -->|Billing Cycle| I[Invoice Created]
    I -->|Auto-collect| A[Attempt Payment]
    A -->|Success| P[Paid Invoice]
    A -->|Fail| F[Finalize Invoice]
    F -->|Retry| R[Retry Payment]
    P -->|Email| E[Send Invoice PDF]
    style I fill:#6772e5,color:#fff

Invoice Lifecycle

Invoices progress through draft, open, paid, void, or uncollectible statuses.

def invoice_lifecycle():
    statuses = [
        ("draft", "Invoice being prepared, not yet finalized"),
        ("open", "Finalized, awaiting payment"),
        ("paid", "Successfully paid"),
        ("void", "Canceled before payment"),
        ("uncollectible", "Payment failed and marked as bad debt"),
    ]
    
    print("Invoice Statuses:")
    for status, description in statuses:
        print(f"  {status:15s} | {description}")

def simulate_invoice_flow():
    print("\nInvoice Flow:")
    print("  Draft -> Finalize -> Open -> Pay -> Paid")
    print("  Draft -> Finalize -> Open -> (fail) -> Uncollectible")
    print("  Draft -> Void")

invoice_lifecycle()
simulate_invoice_flow()

Creating Invoices

Create invoices manually for one-time charges or let Stripe generate them automatically for subscriptions.

import stripe
import os

stripe.api_key = os.environ.get("STRIPE_SECRET_KEY", "sk_test_placeholder")

def create_invoice(customer_id, days_until_due=30):
    invoice = stripe.Invoice.create(
        customer=customer_id,
        collection_method="charge_automatically",
        days_until_due=days_until_due,
        auto_advance=True,
    )
    return invoice

stripe.api_key = "sk_test_placeholder"

class MockInvoice:
    id = "in_test_abc"
    number = "INV-001"
    status = "draft"
    total = 2999

stripe.Invoice.create = lambda **kwargs: MockInvoice()

invoice = create_invoice("cus_abc")
print(f"Invoice created: {invoice.id}")
print(f"Number: {invoice.number}")
print(f"Status: {invoice.status}")
print(f"Amount: ${invoice.total/100:.2f}")

Expected output:

Invoice created: in_test_abc
Number: INV-001
Status: draft
Amount: $29.99

Adding Line Items

Add individual line items to an invoice before finalizing.

# line_items.py
# Adding items to invoices

def add_invoice_item(customer_id, amount_cents, description, invoice_id=None):
    print(f"Adding line item to invoice {invoice_id or 'next invoice'}")
    print(f"  Description: {description}")
    print(f"  Amount: ${amount_cents/100:.2f}")
    print(f"  -> Line item added")

def create_full_invoice(customer_id):
    print(f"Creating invoice for {customer_id}")
    
    items = [
        (999, "Monthly subscription - Basic Plan"),
        (500, "Additional storage (50GB)"),
        (0, "Promotional discount - FIRSTMONTH"),
    ]
    
    total = 0
    for amount, desc in items:
        add_invoice_item(customer_id, amount, desc)
        total += amount
    
    print(f"\nTotal: ${total/100:.2f}")
    print("Invoice finalized and sent to customer")
    return total

create_full_invoice("cus_abc")

Common Mistakes

  1. Not finalizing draft invoices: Draft invoices are not visible to customers and do not attempt payment. Finalize them with finalize_invoice.

  2. Misunderstanding collection_method: charge_automatically attempts payment. send_invoice emails the invoice for manual payment.

  3. Forgetting to add line items before finalizing: Line items cannot be added to finalized invoices. Add them while the invoice is in draft.

  4. Not handling auto_advance correctly: auto_advance=True will finalize and attempt collection. Set to False to review before finalizing.

  5. Ignoring tax calculation: Enable Stripe Tax or add tax rates to line items for accurate tax collection.

Practice Questions

  1. What are the possible invoice statuses? draft, open, paid, void, uncollectible.

  2. What happens when an invoice is finalized? It moves from draft to open status, becomes visible to the customer, and payment is attempted if charge_automatically is set.

  3. How do you add items to an invoice? Create InvoiceItems and associate them with a customer and optionally a specific invoice ID.

  4. What is the difference between charge_automatically and send_invoice? charge_automatically attempts payment immediately. send_invoice emails the customer to pay manually.

  5. Challenge: Create an invoice system that generates monthly invoices, adds usage-based line items, and handles automatic payment collection.

FAQ

Can I customize invoice appearance?

Yes. Configure business name, logo, and branding in the Stripe dashboard under Settings > Branding.

Does Stripe send invoice emails automatically?

Yes. Stripe sends invoice emails when status changes to open if send_invoice is configured.

Can I download invoices as PDF?

Yes. Each invoice has a downloadable PDF URL accessible via the API or Dashboard.

How do voided invoices affect revenue?

Voided invoices do not appear as revenue. They are removed from the customer's outstanding balance.

Can I create invoices in different currencies?

Yes. Set the currency parameter when creating the invoice. Line items must use the same currency.

Mini Project

Create an invoicing endpoint that generates an invoice with line items, finalizes it, and returns the invoice URL.

import json

def lambda_handler(event, context):
    body = json.loads(event.get("body", "{}"))
    customer_id = body.get("customer_id", "cus_test")
    items = body.get("items", [{"amount": 1999, "description": "Monthly plan"}])
    
    invoice_id = f"in_{hash(customer_id)}"
    total = sum(item["amount"] for item in items)
    
    print(f"Created invoice {invoice_id} for {customer_id}")
    for item in items:
        print(f"  ${item['amount']/100:.2f} - {item['description']}")
    print(f"  Total: ${total/100:.2f}")
    
    return {
        "statusCode": 200,
        "body": json.dumps({
            "invoice_id": invoice_id,
            "total": total,
            "invoice_pdf": f"https://invoice.stripe.com/in_test_{invoice_id}.pdf",
            "status": "open"
        })
    }

print(lambda_handler({"body": json.dumps({"customer_id": "cus_abc", "items": [{"amount": 2999, "description": "Pro Plan"}]})}, None)["body"])

What's Next

Next: Pricing and Products for setting up your product catalog.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro