This guide mirrors the checked-in examples/accrue_host story in package-facing
terms. Your Phoenix app owns MyApp.Billing, routing, auth, runtime config,
and verification choices. Accrue owns the billing engine behind those public
boundaries. Read-only processor queries such as saved payment methods use
Accrue.Billing.list_payment_methods/2 (and the host wrapper MyApp.Billing.list_payment_methods/2 after mix accrue.install); see guides/telemetry.md for the [:accrue, :billing, :payment_method, :list] span.
Server-side Stripe Checkout session creation uses Accrue.Billing.create_checkout_session/2
(and your host facade after install); telemetry is [:accrue, :billing, :checkout_session, :create]
— see guides/telemetry.md#billing-checkout-session-create.
Customer Portal session creation is the parallel server-side helper for Stripe-hosted billing self-service.
Server-side Stripe Customer Portal session creation uses Accrue.Billing.create_billing_portal_session/2
(and your host facade after install); telemetry is [:accrue, :billing, :billing_portal, :create]
— see guides/telemetry.md#billing-billing-portal-create.
How to enter this guide
This guide is one spine with three entry capsules — pick where you are starting, then follow the same ordered story (deps → install → runtime → migrations → Oban → webhooks → admin → proof). Public wording and step order stay aligned with examples/accrue_host/README.md; when the spine or command vocabulary changes, update that README in the same pull request (D-02). Maintainer checklist (INT-11): same-PR capsule discipline lives in the contributor map scripts/ci/README.md — search for First Hour + host README capsule parity.
For maintenance posture (when to stop speculative doc work, how friction is intake-gated), see Maturity and maintenance.
Capsule H — Hex consumer
You already have a Phoenix app. Add Accrue to mix.exs, run mix deps.get, then mix accrue.install … and continue from § 1. First run below (runtime config → migrations → Oban → webhook route → admin mount → subscription + proof).
Capsule M — Monorepo clone
From the repository root: cd examples/accrue_host, run mix setup, start mix phx.server, then follow the numbered host README story (subscription → signed webhook → admin → mix verify) — the same Fake-backed arc this guide describes in package terms. Sigra is wired in the checked-in demo for convenience and reproducibility, not because production Accrue apps must use it.
Capsule R — Evaluate / read-only
Shortest read-only path: clone the repo, cd examples/accrue_host, run mix verify or mix verify.full. For merge-blocking VERIFY-01 detail and Playwright entry points, use #proof-and-verification in the host README when you need more than the bounded proof commands.
Trust boundary (production vs demo)
Production apps integrate billing through host-owned Accrue.Auth; see Auth adapters for adapter choices and wiring contracts. Sigra is optional: the demo uses it for deterministic organization billing and CI, not as a blanket production requirement. When you are not on Sigra, follow Capsule H and Organization billing (non-Sigra) for org-scoped Stripe customers. Demo-specific mix.exs and setup commands stay in examples/accrue_host/README.md.
When you are preparing a real deploy (not the demo loop), walk Production readiness once — it links the same guides in ship order without duplicating them.
1. First run
The first hour should end with one Fake-backed subscription, one signed webhook proof, mounted admin inspection, and a focused verification pass.
Install the packages
Hex vs
main: The version pins below mirroraccrue/mix.exsandaccrue_admin/mix.exs@versionon the branch you are reading (usuallymainon GitHub). Hex.pm / Hex.pm/packages/accrue_admin reflect what is published; use HexDocs when you need docs tied to the resolved Hex version.
- The fenced
~>pins below track the Hex-published SemVer line for the@versionpair this branch ships with. path:/ monorepo installs must keepaccrueandaccrue_adminon the same three-part~>(lockstep trains).- Pre-1.0
~>minors may still ship breaking API changes—treatmix.lockas the production stability boundary, not semver intuition alone.
defp deps do
[
{:accrue, "~> 1.0.0"},
{:accrue_admin, "~> 1.0.0"}
]
endmix deps.get
mix accrue.install --billable MyApp.Accounts.User --billing-context MyApp.Billing
The checked-in host example is the canonical local evaluation loop:
cd examples/accrue_host
mix setup
mix phx.server
Keep runtime config host-owned
Accrue raises Accrue.ConfigError when required setup is missing. Keep secrets
and environment-specific values in config/runtime.exs:
import Config
config :accrue, :processor, Accrue.Processor.Fake
config :accrue, repo: MyApp.Repo
config :accrue, :webhook_signing_secrets, %{
stripe: System.get_env("STRIPE_WEBHOOK_SECRET", "whsec_test_host")
}Run your database setup before boot:
mix ecto.create
mix ecto.migrate
Start Oban with the app so webhook dispatch and replay work end to end:
children = [
MyApp.Repo,
{Oban, Application.fetch_env!(:my_app, Oban)},
MyAppWeb.Endpoint
]Mount the public billing boundaries
Add signed webhook ingest at /webhooks/stripe and keep the handler on the
public callback surface:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
import Accrue.Router
pipeline :accrue_webhook_raw_body do
plug Plug.Parsers,
parsers: [:json],
pass: ["*/*"],
json_decoder: Jason,
body_reader: {Accrue.Webhook.CachingBodyReader, :read_body, []}
end
scope "/webhooks" do
pipe_through :accrue_webhook_raw_body
accrue_webhook "/stripe", :stripe
end
endWhen this fails
- Raw body / parser order: Troubleshooting —
ACCRUE-DX-WEBHOOK-RAW-BODY- Missing signing secret: Troubleshooting —
ACCRUE-DX-WEBHOOK-SECRET-MISSING- Webhook behind the wrong pipeline: Troubleshooting —
ACCRUE-DX-WEBHOOK-PIPELINEmix accrue.installreruns / conflicts: Upgrade — installer rerun behavior
defmodule MyApp.BillingHandler do
use Accrue.Webhook.Handler
@impl Accrue.Webhook.Handler
def handle_event(type, event, ctx) do
MyApp.Billing.handle_webhook(type, event, ctx)
end
endMount accrue_admin "/billing" behind your host auth boundary.
AccrueAdmin.Router.accrue_admin/2 is the public router macro:
import AccrueAdmin.Router
scope "/" do
pipe_through [:browser, :require_authenticated_user]
accrue_admin "/billing",
session_keys: [:user_token],
on_mount: [{MyAppWeb.UserAuth, :mount_current_user}]
endProve the first subscription and webhook
Create the first subscription through the generated facade:
user = MyApp.Accounts.get_user!(user_id)
{:ok, subscription} =
MyApp.Billing.subscribe(user, "price_basic", trial_end: {:days, 14})For app-level tests, stay on supported helpers:
use Accrue.TestThen post one signed customer.subscription.created payload through
/webhooks/stripe, visit /billing, and confirm the mounted admin UI shows the
resulting billing state plus replay visibility.
Finish the guided path with the focused host proofs:
mix verify
mix verify is the focused tutorial proof suite. mix verify.full is the
CI-equivalent local gate that adds compile, assets, dev boot, regression, and
browser smoke after the first-run story is already clear. For the authoritative
merge-blocking command matrix, VERIFY-01, and Playwright entry points, see
Proof and verification in the host demo README.
2. Seeded history
Seeded history is for deterministic replay/history evaluation, not for the
main teaching path.
cd examples/accrue_host
mix setup
mix verify.full
Use it when you need replay-ready webhook states, browser smoke fixtures, or other evaluation setup that should not become public integration guidance.
3. Focused verification
mix verifyproves the host-owned tutorial arc: installer boundary, first subscription throughMyApp.Billing, signed webhook ingest, mounted/billinginspection, and replay visibility.mix verify.fullis the CI-equivalent local gate for maintainers.bash scripts/ci/accrue_host_uat.shis the repo-root wrapper around that same full contract.bash scripts/ci/accrue_host_hex_smoke.shis Hex smoke and stays separate from the checked-in host demo.mix accrue.installremains the production setup command for your own host app.
4. Rerunning mix accrue.install
Reruns refresh pristine generated files that still match the Accrue
fingerprint marker; user-edited generated files are skipped so local policy
changes are preserved. Unmarked existing files stay skipped unless you opt into
a narrow overwrite, and --write-conflicts writes reviewable artifacts under
.accrue/conflicts/ instead of patching live files blindly — the same contract
as the upgrade guide. See
Upgrade guide — Installer rerun behavior
for the full installer rerun semantics.