abono v0.1: Stripe memberships for rastrillo apps
abono sends readers to Stripe's hosted Checkout, records the subscriptions Stripe reports, lets the owner give memberships away, and tells the app which plan a person holds. It works on the site's own Stripe key, by card, with a 30-day grace after a failed renewal. The app keeps roles and accounts through two callbacks (Holder, OnChange). It ships partials, an approved English catalog, a starter-page generator, a fake Stripe for tests (abonotest), a development sandbox and an example app. Extracted from Patrix's paid memberships. Design and plan: docs/superpowers/specs/2026-10-21-abono-design.md and docs/superpowers/plans/2026-10-22-abono-v0.1.md in rastrillo. Squashed from v0.1 into main.
.amadan/ci+6 −0.amadan/ci.d/10-vet+3 −0.amadan/ci.d/20-fmt+3 −0.amadan/ci.d/30-test+3 −0.amadan/ci.d/40-race+3 −0.amadan/ci.d/50-browser+3 −0.gitignore+6 −0Makefile+45 −0README.md+3 −1SKILL.md+77 −0abono.go+187 −0abono_test.go+183 −0abonotest/abonotest.go+159 −0abonotest/abonotest_test.go+513 −0adopt.go+159 −0adopt_test.go+238 −0catalog.go+115 −0catalog_test.go+49 −0checkout.go+340 −0checkout_test.go+279 −0cmd/abono/main.go+164 −0cmd/abono/main_test.go+318 −0cmd/abono/pages/abono_pages.go.tmpl+849 −0cmd/abono/pages/account.html.tmpl+33 −0cmd/abono/pages/done.html.tmpl+35 −0cmd/abono/pages/owner-end.html.tmpl+18 −0cmd/abono/pages/owner-members.html.tmpl+80 −0cmd/abono/pages/owner-offers.html.tmpl+66 −0cmd/abono/pages/owner-stripe.html.tmpl+24 −0cmd/abono/pages/plan.html.tmpl+30 −0cmd/abono/pages/pricing.html.tmpl+20 −0comps.go+78 −0comps_test.go+101 −0describe.go+103 −0describe_test.go+97 −0doc.go+10 −0example/README.md+53 −0example/abono_pages.go+849 −0example/app.go+356 −0example/app_test.go+821 −0example/browser_test.go+85 −0example/catalog.go+57 −0example/catalog_test.go+108 −0example/go.mod+38 −0example/go.sum+97 −0example/locales/en.toml+5 −0example/main.go+72 −0example/migrations/0001_init.sql+3 −0example/models.go+109 −0example/templates/abono/account.html+33 −0example/templates/abono/done.html+35 −0example/templates/abono/owner-end.html+18 −0example/templates/abono/owner-members.html+80 −0example/templates/abono/owner-offers.html+66 −0example/templates/abono/owner-stripe.html+24 −0example/templates/abono/plan.html+30 −0example/templates/abono/pricing.html+20 −0example/templates/account.html+4 −0example/templates/layout.html+27 −0example/templates/signin.html+3 −0export_test.go+13 −0go.mod+26 −0go.sum+77 −0holdings.go+273 −0holdings_test.go+282 −0internal/fakestripe/fake.go+977 −0internal/fakestripe/fake_test.go+724 −0internal/fakestripe/pages.go+298 −0internal/stripeapi/client.go+324 −0internal/stripeapi/client_test.go+232 −0internal/stripeapi/signature.go+57 −0internal/stripeapi/signature_test.go+87 −0locales/en.toml+143 −0locks.go+58 −0migrations.go+16 −0migrations/0001_init.sql+87 −0offers.go+306 −0offers_test.go+311 −0owner.go+61 −0owner_test.go+114 −0reconcile.go+118 −0reconcile_test.go+226 −0sandbox.go+101 −0sandbox_test.go+397 −0schema_test.go+159 −0skillmd_test.go+45 −0stripe_double_test.go+174 −0sync.go+389 −0sync_test.go+774 −0templates.go+40 −0templates/abono-billing-button.html+13 −0templates/abono-paywall.html+17 −0templates/abono-plan-card.html+18 −0templates/abono-plan-picker.html+15 −0templates/abono-price.html+8 −0templates/abono-status.html+23 −0templates/abono-subscribe-button.html+16 −0templates_test.go+735 −0webhook.go+244 −0webhook_test.go+411 −0
CI passed — run details
CI log
=== vet === go vet ./... === fmt === === test === go test ./... -count=1 ok amadan.net/rastrillo/abono 3.854s ok amadan.net/rastrillo/abono/abonotest 0.148s ok amadan.net/rastrillo/abono/cmd/abono 2.574s ok amadan.net/rastrillo/abono/internal/fakestripe 0.083s ok amadan.net/rastrillo/abono/internal/stripeapi 0.020s ok example 0.506s === race === CGO_ENABLED=1 go test ./... -race -count=1 ok amadan.net/rastrillo/abono 17.003s ok amadan.net/rastrillo/abono/abonotest 1.972s ok amadan.net/rastrillo/abono/cmd/abono 4.528s ok amadan.net/rastrillo/abono/internal/fakestripe 1.166s ok amadan.net/rastrillo/abono/internal/stripeapi 1.045s ok example 4.268s === browser === ok example 2.131s
.amadan/ciLoading diff…
.amadan/ci.d/10-vetLoading diff…
.amadan/ci.d/20-fmtLoading diff…
.amadan/ci.d/30-testLoading diff…
.amadan/ci.d/40-raceLoading diff…
.amadan/ci.d/50-browserLoading diff…
.gitignoreLoading diff…
MakefileLoading diff…
README.mdLoading diff…
SKILL.mdLoading diff…
abono.goLoading diff…
abono_test.goLoading diff…
abonotest/abonotest.goLoading diff…
abonotest/abonotest_test.goLoading diff…
adopt.goLoading diff…
adopt_test.goLoading diff…
catalog.goLoading diff…
catalog_test.goLoading diff…
checkout.goLoading diff…
checkout_test.goLoading diff…
cmd/abono/main.goLoading diff…
cmd/abono/main_test.goLoading diff…
cmd/abono/pages/abono_pages.go.tmplLoading diff…
cmd/abono/pages/account.html.tmplLoading diff…
cmd/abono/pages/done.html.tmplLoading diff…
cmd/abono/pages/owner-end.html.tmplLoading diff…
cmd/abono/pages/owner-members.html.tmplLoading diff…
cmd/abono/pages/owner-offers.html.tmplLoading diff…
cmd/abono/pages/owner-stripe.html.tmplLoading diff…
cmd/abono/pages/plan.html.tmplLoading diff…
cmd/abono/pages/pricing.html.tmplLoading diff…
comps.goLoading diff…
comps_test.goLoading diff…
describe.goLoading diff…
describe_test.goLoading diff…
doc.goLoading diff…
example/README.mdLoading diff…
example/abono_pages.goLoading diff…
example/app.goLoading diff…
example/app_test.goLoading diff…
example/browser_test.goLoading diff…
example/catalog.goLoading diff…
example/catalog_test.goLoading diff…
example/go.modLoading diff…
example/go.sumLoading diff…
example/locales/en.tomlLoading diff…
example/main.goLoading diff…
example/migrations/0001_init.sqlLoading diff…
example/models.goLoading diff…
example/templates/abono/account.htmlLoading diff…
example/templates/abono/done.htmlLoading diff…
example/templates/abono/owner-end.htmlLoading diff…
example/templates/abono/owner-members.htmlLoading diff…
example/templates/abono/owner-offers.htmlLoading diff…
example/templates/abono/owner-stripe.htmlLoading diff…
example/templates/abono/plan.htmlLoading diff…
example/templates/abono/pricing.htmlLoading diff…
example/templates/account.htmlLoading diff…
example/templates/layout.htmlLoading diff…
example/templates/signin.htmlLoading diff…
export_test.goLoading diff…
go.modLoading diff…
go.sumLoading diff…
holdings.goLoading diff…
holdings_test.goLoading diff…
internal/fakestripe/fake.goLoading diff…
internal/fakestripe/fake_test.goLoading diff…
internal/fakestripe/pages.goLoading diff…
internal/stripeapi/client.goLoading diff…
internal/stripeapi/client_test.goLoading diff…
internal/stripeapi/signature.goLoading diff…
internal/stripeapi/signature_test.goLoading diff…
locales/en.tomlLoading diff…
locks.goLoading diff…
migrations.goLoading diff…
migrations/0001_init.sqlLoading diff…
offers.goLoading diff…
offers_test.goLoading diff…
owner.goLoading diff…
owner_test.goLoading diff…
reconcile.goLoading diff…
reconcile_test.goLoading diff…
sandbox.goLoading diff…
sandbox_test.goLoading diff…
schema_test.goLoading diff…
skillmd_test.goLoading diff…
stripe_double_test.goLoading diff…
sync.goLoading diff…
sync_test.goLoading diff…
templates.goLoading diff…
templates/abono-billing-button.htmlLoading diff…
templates/abono-paywall.htmlLoading diff…
templates/abono-plan-card.htmlLoading diff…
templates/abono-plan-picker.htmlLoading diff…
templates/abono-price.htmlLoading diff…
templates/abono-status.htmlLoading diff…
templates/abono-subscribe-button.htmlLoading diff…
templates_test.goLoading diff…
webhook.goLoading diff…
webhook_test.goLoading diff…