Skip to main content

larzsubscription

Subscription billing as a state machine. Pure Python, zero dependencies.

The lifecycle of a paid subscription is deceptively fiddly — trials that convert, renewals that charge, failed payments that go past due and retry, plan changes that need proration, cancellations now or at period end. larzsubscription models all of it as one small, testable object with exact Decimal money.

from larzsubscription import Plan, Subscription
from datetime import datetime

pro = Plan("pro", price="9.99", interval_days=30, trial_days=14)
sub = Subscription(pro, start=datetime(2026, 1, 1))

sub.status                                   # "trialing"
sub.renew(now=datetime(2026, 1, 15))         # trial converts -> "active", charges 9.99
credit = sub.change_plan(Plan("basic", "4.99"), now=datetime(2026, 1, 20))  # prorated
sub.cancel()                                 # at period end

Why

  • The whole lifecycle, correctly. trialing → active → past_due → canceled → expired, with trials converting, dunning attempts counted, and cancel-now vs cancel-at-period-end both handled.
  • Real proration. change_plan() returns the exact net amount to charge (or credit) for the remainder of the current period.
  • Exact money. Decimal throughout; floats rejected. Every charge is recorded as an invoice; total_billed() reconciles.
  • Deterministic. Pass now= everywhere, so billing is testable without waiting or mocking the clock — and it composes with larzmoney / larzledger.

Install

pip install larzsubscription

API

Plan(name, price, interval_days=30, trial_days=0)

sub = Subscription(plan, start=now, on_event=cb)
sub.renew(now)                 # bill next period (trial converts); returns amount
sub.fail_payment(now)          # -> past_due, dunning_attempts += 1
sub.recover(now)               # past_due -> active (payment recovered)
sub.cancel(immediately=False)  # at period end, or now
sub.change_plan(new_plan, now) # -> prorated net (charge>0 / credit<0)
sub.has_access(now); sub.next_renewal; sub.total_billed(); sub.invoices; sub.status

Tests

python -m unittest discover -s tests -v   # 16 tests, zero deps

The Larz stack

One of 30+ pure-Python, zero-dependency libraries at github.com/larz-scripter — pairs with larzmoney, larzledger, and larzpdf.

License

MIT © larz-scripter

Metadata

Release files for larzsubscription 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for larzsubscription 0.1.0
File Size Uploaded Provenance
larzsubscription-0.1.0.tar.gz 6.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for larzsubscription 0.1.0
File Interpreter ABI Platform
larzsubscription-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 12.8 kB

Release files / larzsubscription-0.1.0.tar.gz

Download URL larzsubscription-0.1.0.tar.gz
Size 6.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a87c9958918e2d17cb445eefcc1bae34535ea43ae4f2ff37c9fc14faeb93287c
BLAKE2b-256 checksum
How to use checksums
3346a37a925d22519802f34d41881364977bf060736fe3f9d3275c9006348ed4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / larzsubscription-0.1.0-py3-none-any.whl

Download URL larzsubscription-0.1.0-py3-none-any.whl
Size 6.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
facc3f6d4f8f3bbf52e7df1011e5bd6cd4d63c9dac3f0ff163b36741abacbdcc
BLAKE2b-256 checksum
How to use checksums
6bcc17d5e10a78852d9fbed7b1a16aeaeb6fbf960b059d441bd7cf5933098d3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Security for larzsubscription 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page