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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

larzsubscription-0.1.0.tar.gz (6.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

larzsubscription-0.1.0-py3-none-any.whl (6.5 kB view details)

Uploaded Python 3

File details

Details for the file larzsubscription-0.1.0.tar.gz.

File metadata

  • Download URL: larzsubscription-0.1.0.tar.gz
  • Upload date:
  • Size: 6.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.13

File hashes

Hashes for larzsubscription-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a87c9958918e2d17cb445eefcc1bae34535ea43ae4f2ff37c9fc14faeb93287c
MD5 aee52f481d404fa15766b62d1e0645c0
BLAKE2b-256 3346a37a925d22519802f34d41881364977bf060736fe3f9d3275c9006348ed4

See more details on using hashes here.

File details

Details for the file larzsubscription-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for larzsubscription-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 facc3f6d4f8f3bbf52e7df1011e5bd6cd4d63c9dac3f0ff163b36741abacbdcc
MD5 2c842187a420e94986b50d43c98c8b6d
BLAKE2b-256 6bcc17d5e10a78852d9fbed7b1a16aeaeb6fbf960b059d441bd7cf5933098d3a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

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