Stripe Invoices — Billing Documentation and Automation
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
Not finalizing draft invoices: Draft invoices are not visible to customers and do not attempt payment. Finalize them with finalize_invoice.
Misunderstanding collection_method: charge_automatically attempts payment. send_invoice emails the invoice for manual payment.
Forgetting to add line items before finalizing: Line items cannot be added to finalized invoices. Add them while the invoice is in draft.
Not handling auto_advance correctly: auto_advance=True will finalize and attempt collection. Set to False to review before finalizing.
Ignoring tax calculation: Enable Stripe Tax or add tax rates to line items for accurate tax collection.
Practice Questions
What are the possible invoice statuses? draft, open, paid, void, uncollectible.
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.
How do you add items to an invoice? Create InvoiceItems and associate them with a customer and optionally a specific invoice ID.
What is the difference between charge_automatically and send_invoice? charge_automatically attempts payment immediately. send_invoice emails the customer to pay manually.
Challenge: Create an invoice system that generates monthly invoices, adds usage-based line items, and handles automatic payment collection.
FAQ
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