# End-to-End Walkthrough: Creating and Settling a Charge (Billing & Ledger) div strong Goal: br Create a realistic billing setup (rate + discount + allocation + subscription), generate a charge that references versioned configuration, and understand what happens as a billing cycle runs and charges become immutable. This walkthrough uses the **Billing & Ledger** API endpoints defined in the service OpenAPI spec, including: - `POST /rates`, `POST /rate-cards` - `POST /allocation-configurations` - `POST /subscriptions` - `POST /charges` - `PATCH /charges/{chargeId}` (mutable only) - `POST /charges/invoice` and `POST /charges/settle` (settlement entry points) - `GET /accounts/{id}/balance` (balance views) > **Auth:** All requests use a bearer token (JWT-derived). ## Before you start ### Identify your Profile IDs Billing & Ledger references Profile Service entities. You will need: - `billableEntityId` (who services were rendered to) - one or more `accountId` values (who pays) If you are using split allocations, ensure the billable entity is associated to the accounts you plan to reference (typically managed in Profile Service). ## Step 1 — Create a debit rate (what you charge) Create a rate that represents a debit (e.g., “Monthly Program Fee”). Rates are versioned and reusable across subscriptions and charges. ```bash curl -X POST "https://api.nelnetpay.com/billing/rates" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "description": "Monthly Program Fee", "type": "TUITION", "pricePerUnit": 15000, "tags": { "billing.category": "PROGRAM", "billing.plan": "STANDARD" } }' ``` ✅ Save the returned `id` as `rateId`. ## Step 2 — Create a discount rate (optional) Discounts are modeled as rates with `type=DISCOUNT`. Discount rates can be fixed-amount or percentage-based. ### Example: 10% discount ```bash curl -X POST "https://api.nelnetpay.com/billing/rates" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "description": "10% Loyalty Discount", "type": "DISCOUNT", "discountPercentage": 10, "tags": { "billing.discountType": "LOYALTY" } }' ``` ✅ Save the returned `id` as `discountRateId`. ## Step 3 — Create an allocation configuration (who pays, and how) Allocation configurations define financial responsibility routing. They contain ordered rules that resolve deterministically during settlement. Here’s a simple “single responsible party” example (100% to one account). ```bash curl -X POST "https://api.nelnetpay.com/billing/allocation-configurations" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "name": "Single Responsible Party", "description": "100% responsibility assigned to the primary account", "rules": [ { "ruleType": "RESPONSIBLE_PARTY", "accountId": "acc_123", "percent": 100 } ], "tags": { "billing.allocation": "PRIMARY_ONLY" } }' ``` ✅ Save the returned `id` as `allocationConfigurationId`. ### What allocation rules *mean* downstream When the charge is invoiced/settled: - Billing resolves these rules into **journal lines** - caps/transfers can yield remainder behavior such as **write-offs** - the resolved outcome is stored in **Settled Charges** and cannot be changed later ## Step 4 — (Optional) Create a rate card (catalog grouping) Rate cards make it easier for UIs and humans to browse “the menu” of rates. ```bash curl -X POST "https://api.nelnetpay.com/billing/rate-cards" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "name": "Program Catalog", "rateIds": [ { "rateId": "'"$rateId"'", "allocationConfigurationId": "'"$allocationConfigurationId"'" } ] }' ``` > Rate cards are for discovery only—you still pass explicit IDs when creating charges. ## Step 5 — Create a subscription (billing intent + schedule) Subscriptions express **intent** (what to bill and how often). They do not automatically create charges; a scheduler/orchestrator does that (often the Recurring Billing service). Example: a monthly subscription that bills on the 1st of the month, but the person starts mid-month. ```bash curl -X POST "https://api.nelnetpay.com/billing/subscriptions" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "billableEntityId": "be_123", "rateId": "'"$rateId"'", "allocationConfigurationId": "'"$allocationConfigurationId"'", "discountRateIds": ["'"$discountRateId"'"], "billingFrequency": "MONTHLY", "billingAnchorDay": 1, "billingTimeZone": "America/Chicago", "startDate": "2026-02-15", "status": "ACTIVE", "tags": { "billing.subscriptionSource": "ADMIN_UI" } }' ``` ✅ Save the returned `id` as `subscriptionId`. ### Proration example (why subscriptions matter) > A participant joins on the 15th, but billing runs on the 1st. In this case: - the subscription captures the intent and start date - billing logic computes the correct proration factor at charge creation or finalization time (depending on workflow) ## Step 6 — Create a charge (the invoice-ready instruction) Charges can be created: - **from a subscription** (recommended): pass `subscriptionId` and let the service discover rate/allocation/discount references - **directly**: pass explicit `rateId`, `allocationConfigurationId`, etc. ### 6A — Create a charge from a subscription ```bash curl -X POST "https://api.nelnetpay.com/billing/charges" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "subscriptionId": "'"$subscriptionId"'", "quantity": 1, "eventDate": "2026-02-15", "tags": { "billing.chargeReason": "MONTHLY_FEE" } }' ``` The service snapshots versions at creation time (rate, discount rates, allocation config, subscription) for audit safety. ### 6B — Create a direct account-level charge (no billable entity) Use `accountId` when billing directly to an account (for example, a one-time registration fee). ```bash curl -X POST "https://api.nelnetpay.com/billing/charges" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "accountId": "acc_123", "rateId": "'"$rateId"'", "allocationConfigurationId": "'"$allocationConfigurationId"'", "quantity": 1, "eventDate": "2026-02-15" }' ``` ## Step 7 — Update a charge (only while mutable) Charges can be patched only in mutable statuses (for example `PENDING` or `BILLED`). Once invoiced, updates return a conflict. ```bash curl -X PATCH "https://api.nelnetpay.com/billing/charges/$chargeId" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "quantity": 2, "tags": { "billing.updateReason": "CORRECTED_QUANTITY" } }' ``` ## Step 8 — Billing cycles and “finalization” Billing cycles control when you stop editing and start producing final financial outcomes. ### What changes when a cycle runs? - Before invoicing/settlement: charges represent editable instructions and may be updated/cancelled. - During invoicing or direct settlement: - allocation rules are resolved - resolved amounts and splits are persisted - ledger entries may be created (depending on the settlement flow) - charges become immutable and move to **Settled Charges** ## Step 9 — Settle charges (two common paths) ### 9A — Invoice settlement Invoices service typically retrieves charges and then calls the settlement endpoint to mark them invoiced and create ledger artifacts. ```bash curl -X POST "https://api.nps.com/v1/charges/invoice" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "chargeIds": ["'"$chargeId"'"], "invoiceId": "inv_123" }' ``` ### 9B — Direct settlement (account balance / widget flows) Payment Widget may support “pay account balance” by settling charges directly after payment. ```bash curl -X POST "https://api.nps.com/v1/charges/settle" -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" -d '{ "chargeIds": ["'"$chargeId"'"], "paymentId": "pay_123" }' ``` **Result:** the system records settlement and the charge becomes immutable (Settled Charge). ## Step 10 — Query balances (billing with or without invoices) Billing & Ledger can be used even without the invoices service. - **With invoices:** balances reflect invoices/settlements and ledger outcomes. - **Without invoices:** charges still accumulate. You can query outstanding charges and compute logical balances from charge state. Example balance lookup: ```bash curl -X GET "https://api.nelnetpay.com/billing/charges?accountId=acb123" -H "Authorization: Bearer $ACCESS_TOKEN" ``` ## Where tags show up in this flow Tags can be applied at multiple points: - rate tags (pricing categorization) - allocation tags (routing categorization) - subscription tags (intent and schedule metadata) - charge tags (event-specific context) Downstream records (charges, journal entries, exports, reporting) can carry **derived** tags and keep them nested for provenance (e.g., `billing.rate.*`, `profile.billableEntity.*`).