diff --git a/html5/_main_toc.html.slim b/html5/_main_toc.html.slim index ada6fb59b..cfa9e7ef7 100644 --- a/html5/_main_toc.html.slim +++ b/html5/_main_toc.html.slim @@ -333,6 +333,12 @@ nav.sidebar-nav li a.nav-link href="/latest/aviate-coupons.html" | Coupons + li + a.nav-link href="/latest/aviate-subscription-lifecycle.html" + | Subscription Lifecycle + li + a.nav-link href="/latest/aviate-credits-refunds.html" + | Credits & Refunds li a.nav-link href="/latest/aviate-tax.html" | Tax diff --git a/userguide/aviate/aviate-credits-refunds.adoc b/userguide/aviate/aviate-credits-refunds.adoc new file mode 100644 index 000000000..6f9b2b721 --- /dev/null +++ b/userguide/aviate/aviate-credits-refunds.adoc @@ -0,0 +1,253 @@ += Credits, Refunds & Invoice Adjustments + +:card-badge: Premium +:card-title: Aviate +:card-link: https://aviate.killbill.io/ +:card-description: +include::{sourcedir}/includes/premium-card.adoc[] + +== Overview + +Billing corrections are a routine part of operations — handling disputes, issuing goodwill credits, correcting invoice errors, and processing refunds. This guide covers Kill Bill account credits, invoice adjustments, invoice voiding, and payment refunds, with Aviate-specific context on how these operations interact with wallets, tax, and revenue recognition. + +NOTE: These operations use *Kill Bill core APIs* (base path `/1.0/kb/`) with *Basic authentication* (`-u admin:password`) and tenant headers. Aviate's wallet credits (`CREDIT_FREE`, `CREDIT_PAID`) are *separate* from Kill Bill's native account credits. For Aviate wallet credits, see https://docs.killbill.io/latest/aviate-wallet.html[Wallet Guide]. + +== Prerequisites + +* A running Kill Bill instance with the https://docs.killbill.io/latest/how-to-install-the-aviate-plugin.html[Aviate plugin installed]. +* Kill Bill admin credentials (Basic auth — `admin:password` by default). +* Tenant API key and secret (`X-Killbill-ApiKey` / `X-Killbill-ApiSecret`). +* The target account ID, invoice ID, or payment ID depending on the operation. + +== Aviate Wallet Credits vs. Kill Bill Account Credits + +Aviate wallet credits and Kill Bill account credits are two distinct mechanisms. Understanding the difference is important to avoid confusion. + +[cols="2,3,3", options="header"] +|=== +| Feature | Aviate Wallet Credits | Kill Bill Account Credits + +| Created via +| `POST /plugins/aviate-plugin/v1/wallet/\{id}/credit` (Bearer token) +| `POST /1.0/kb/credits` (Basic auth) + +| Scope +| Wallet-level (tied to a specific wallet) +| Account-level (applied to any invoice) + +| Expiration +| Configurable per credit +| No expiration + +| Top-off +| Automatic (via `TOP_OFF_FIXED` or `TOP_OFF_TARGET`) +| Manual only + +| Invoicing +| Paid credits trigger an invoice; free credits do not +| Generates a credit memo (CBA) invoice + +| Usage +| Consumed by usage-based invoicing against the wallet +| Applied as credit-balance-adjustment to the next invoice + +| Authentication +| Bearer token (Aviate plugin API) +| Basic auth (Kill Bill core API) +|=== + +IMPORTANT: Adding a Kill Bill account credit does *not* increase the Aviate wallet balance, and vice versa. They are independent systems. + +== Add Account Credit + +Account credits create a credit-balance-adjustment (CBA) that is automatically applied to the next invoice. Use this for goodwill credits, billing error corrections, or service outage compensation. + +[source,bash] +---- +curl -v -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "accountId": "{accountId}", + "creditAmount": 15.00, + "currency": "USD", + "description": "Customer goodwill credit" + }' \ + "http://127.0.0.1:8080/1.0/kb/credits" +---- + +*Expected result:* A credit memo invoice is generated for the specified amount. The credit is automatically applied to offset the next invoice balance on the account. + +== Adjust an Invoice Item + +Invoice item adjustments reduce the amount of a specific line item on an existing invoice. This is useful for partial credits — for example, compensating a customer for a partial service outage without voiding the entire invoice. + +[source,bash] +---- +curl -v -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "accountId": "{accountId}", + "invoiceItemId": "{invoiceItemId}", + "amount": 5.00, + "description": "Partial service outage credit" + }' \ + "http://127.0.0.1:8080/1.0/kb/invoices/{invoiceId}" +---- + +*Expected result:* An adjustment item is added to the invoice, reducing the balance by the specified amount. The invoice total is updated accordingly. If the invoice was already paid, the adjustment creates an account credit (CBA) for the adjusted amount. + +== Void an Invoice + +Voiding an invoice reverses all its items and sets the invoice status to `VOID`. This is a full reversal — use it when the entire invoice was generated in error. + +[source,bash] +---- +curl -v -X PUT \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + "http://127.0.0.1:8080/1.0/kb/invoices/{invoiceId}/voidInvoice" +---- + +*Expected result:* All invoice items are reversed. The account balance is adjusted accordingly. + +IMPORTANT: You *cannot* void an invoice that has payments associated with it. You must refund the payments first, then void the invoice. + +== Refund a Payment + +Refunds return money to the customer for a previous payment. Kill Bill supports two refund modes depending on the `isAdjusted` flag. + +=== Refund with Invoice Adjustment + +When `isAdjusted` is `true`, the refund also adjusts the invoice — the invoice balance is reduced by the refund amount. Use this when you want to both return money and correct the invoice. + +[source,bash] +---- +curl -v -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 29.99, + "isAdjusted": true + }' \ + "http://127.0.0.1:8080/1.0/kb/payments/{paymentId}/refunds" +---- + +*Expected result:* The payment gateway processes the refund. Kill Bill adjusts the corresponding invoice by the refund amount. + +=== Refund without Invoice Adjustment + +When `isAdjusted` is `false`, only the payment is refunded — the invoice balance remains unchanged. This creates an account balance that needs to be settled. + +[source,bash] +---- +curl -v -X POST \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "amount": 29.99, + "isAdjusted": false + }' \ + "http://127.0.0.1:8080/1.0/kb/payments/{paymentId}/refunds" +---- + +*Expected result:* The payment gateway processes the refund. The invoice balance is not changed — the account shows an outstanding balance equal to the refund amount. + +== Impact on Aviate Features + +=== Tax + +Refunded or voided invoice items may require tax adjustment. If the original invoice included tax items generated by Aviate Tax, the adjustment or void reverses the charge but does *not* automatically reverse the tax item. Check your tax logic configuration to determine whether a separate tax adjustment is needed. See https://docs.killbill.io/latest/aviate-tax.html[Tax Configuration]. + +=== Revenue Recognition + +Credits and refunds trigger revenue reversals in the recognition schedule. If Aviate revenue recognition is enabled, invoice adjustments and voids will create corresponding reversal entries in the revenue recognition report. + +=== Wallet + +Kill Bill account credits are *completely separate* from Aviate wallet credits: + +* Adding a KB account credit does *not* increase the wallet balance. +* Refunding a payment does *not* restore wallet credits that were consumed. +* Voiding an invoice that consumed wallet credits does *not* automatically return credits to the wallet. + +If you need to restore wallet credits, you must manually add credits back to the wallet via the Aviate wallet API. See https://docs.killbill.io/latest/aviate-wallet.html[Wallet Guide]. + +=== Metering + +Adjusting a `USAGE` invoice item corrects the financial record but does *not* reverse the underlying metering events. The usage events remain in Kill Bill's usage store. If you need to correct metering data, use the Aviate metering APIs separately. See https://docs.killbill.io/latest/aviate-metering.html[Metering]. + +== What to Verify + +After performing credit, refund, or adjustment operations, verify the results: + +=== Check Account Balance + +[source,bash] +---- +curl -v \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + "http://127.0.0.1:8080/1.0/kb/accounts/{accountId}" +---- + +Look for the `accountBalance` and `accountCBA` (credit balance adjustment) fields. + +=== Check Invoice Details + +[source,bash] +---- +curl -v \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + "http://127.0.0.1:8080/1.0/kb/invoices/{invoiceId}?withItems=true" +---- + +Verify that adjustment items appear and the invoice balance is correct. + +=== Check Payment Status + +[source,bash] +---- +curl -v \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + "http://127.0.0.1:8080/1.0/kb/payments/{paymentId}" +---- + +Verify that the refund transaction appears in the payment's transaction list with status `SUCCESS`. + +== Common Pitfalls + +1. **Confusing Aviate wallet credits with KB account credits** — they are independent systems with different APIs, authentication methods, and behaviors. Review the comparison table above. +2. **Refunding more than the payment amount** — returns an error. The refund amount cannot exceed the original payment amount minus any previous refunds. +3. **Voiding an invoice that has payments** — returns an error. You must refund all payments on the invoice before voiding it. +4. **Adjusting a USAGE invoice item** — corrects the financial record but does not reverse the underlying metering events. Usage data must be corrected separately via Aviate metering APIs. +5. **Credit applied to wrong currency** — returns a `400 Bad Request` error. The credit currency must match the account's currency. +6. **Large credit amounts without approval** — there is no built-in approval workflow for credits. Consider implementing business process controls outside Kill Bill for large credit amounts. + +== Related + +* https://docs.killbill.io/latest/aviate-wallet.html[Wallet] — Aviate-managed prepaid credits +* https://docs.killbill.io/latest/aviate-subscription-lifecycle.html[Subscription Lifecycle] — plan changes, cancellations, pauses +* https://docs.killbill.io/latest/aviate-tax.html[Tax Configuration] — tax implications of adjustments +* https://docs.killbill.io/latest/aviate-metering.html[Metering] — usage event handling and corrections +* https://docs.killbill.io/latest/aviate-coupons.html[Coupons] — discount behavior with credits diff --git a/userguide/aviate/aviate-subscription-lifecycle.adoc b/userguide/aviate/aviate-subscription-lifecycle.adoc new file mode 100644 index 000000000..5775c3563 --- /dev/null +++ b/userguide/aviate/aviate-subscription-lifecycle.adoc @@ -0,0 +1,239 @@ += Subscription Lifecycle Management + +:card-badge: Premium +:card-title: Aviate +:card-link: https://aviate.killbill.io/ +:card-description: +include::{sourcedir}/includes/premium-card.adoc[] + +== Overview + +After a subscription is created — either manually via the Kill Bill API, through the Aviate UI, or through a https://docs.killbill.io/latest/aviate-quotes-orders.html[quote/order] — billing administrators need to manage ongoing changes: plan upgrades and downgrades, cancellations, and pauses. This guide covers these operations with Aviate-specific context. + +NOTE: The operations in this guide use *Kill Bill core APIs* (base path `/1.0/kb/`). They use *Basic authentication* (`-u admin:password`) with tenant headers, not Bearer tokens. This guide adds Aviate-specific context around how these changes interact with the Aviate catalog, wallets, coupons, tax, and metering. + +== Prerequisites + +* A running Kill Bill instance with the https://docs.killbill.io/latest/how-to-install-the-aviate-plugin.html[Aviate plugin installed]. +* An active subscription exists on the target account. +* Kill Bill admin credentials (Basic auth — `admin:password` by default). +* Tenant API key and secret (`X-Killbill-ApiKey` / `X-Killbill-ApiSecret`). +* For Aviate-specific behavior (coupon carryover, tax recalculation, wallet interaction), the relevant Aviate feature flags must be enabled. See the individual feature guides for details. + +== Change Plan (Upgrade / Downgrade) + +Changing a subscription's plan lets you move a customer between tiers — for example from a `standard-monthly` plan to a `premium-monthly` plan. Kill Bill supports two timing policies: *immediate* and *end-of-term*. + +=== Immediate Change + +An immediate change takes effect right away. Kill Bill generates a prorated credit for the unused portion of the current plan and a new charge for the remaining portion of the billing period on the new plan. + +[source,bash] +---- +curl -v -X PUT \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "planName": "premium-monthly", + "billingPolicy": "IMMEDIATE", + "priceList": "DEFAULT" + }' \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}" +---- + +*Expected result:* A new invoice is generated containing a credit item for the unused portion of the old plan and a charge item for the prorated amount of the new plan. + +=== Change at End of Billing Period + +An end-of-term change delays the switch until the current billing period ends. No proration is involved — the customer continues on the old plan until the period boundary, then transitions to the new plan. + +[source,bash] +---- +curl -v -X PUT \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{ + "planName": "premium-monthly", + "billingPolicy": "END_OF_TERM", + "priceList": "DEFAULT" + }' \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}" +---- + +*Expected result:* No immediate invoice change. The next invoice at the billing period boundary reflects the new plan pricing. + +=== Aviate Considerations for Plan Changes + +* **Coupons** — if a coupon was applied to the subscription, the discount carries over to the new plan _unless_ the new plan is not included in the coupon's `planList`. In that case the coupon stops applying. See https://docs.killbill.io/latest/aviate-coupons.html[Coupons]. +* **Tax** — tax is recalculated based on the new plan's product tax code. If the new product has a different tax code (or no tax code), the tax amount on subsequent invoices changes accordingly. See https://docs.killbill.io/latest/aviate-tax.html[Tax Configuration]. +* **Wallet** — wallet credits continue to apply to the new plan. The wallet balance is unaffected by the plan change itself. See https://docs.killbill.io/latest/aviate-wallet.html[Wallet]. +* **Quotes & Orders** — if the subscription was originally created via a quote or order, the plan change is independent of the original order. No updates are made to the order record. +* **Metering** — usage events continue to be recorded against the same subscription ID. If the new plan has a different usage unit or billing meter, ensure the metering configuration matches. + +== Cancel Subscription + +Cancellation ends a subscription. Like plan changes, cancellation supports *immediate* and *end-of-term* policies. + +=== Cancel Immediately + +[source,bash] +---- +curl -v -X DELETE \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}?billingPolicy=IMMEDIATE" +---- + +*Expected result:* The entitlement ends immediately. A prorated credit is issued for the unused portion of the current billing period. + +=== Cancel at End of Term + +[source,bash] +---- +curl -v -X DELETE \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}?billingPolicy=END_OF_TERM" +---- + +*Expected result:* The customer retains service until the end of the current billing period. No prorated credit is generated. No further invoices are created after the effective cancellation date. + +=== What Happens After Cancellation + +* *Entitlement ends* — the customer loses access to the service as of the effective cancellation date. +* *Prorated credit* — if `IMMEDIATE`, a credit is issued for the unused portion of the billing period. If `END_OF_TERM`, no proration occurs. +* *No future invoices* — Kill Bill stops generating invoices for this subscription after the effective date. +* *Wallet balance is not automatically refunded* — any remaining wallet credits stay on the wallet. They can be consumed by other subscriptions on the same account or manually adjusted. See https://docs.killbill.io/latest/aviate-wallet.html[Wallet]. +* *Metering stops* — Aviate metering no longer records usage events against the cancelled subscription. Any events submitted after cancellation for this subscription are rejected. +* *Coupons* — active coupon redemptions linked to the cancelled subscription become inactive. + +== Pause / Resume + +Pausing suspends billing and entitlement for all subscriptions in a bundle. This is useful for temporary service holds (e.g., customer vacation, seasonal business). + +NOTE: Pause and resume operate at the *bundle* level, not the individual subscription level. All subscriptions within the bundle are affected. + +=== Pause a Subscription Bundle + +[source,bash] +---- +curl -v -X PUT \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{}' \ + "http://127.0.0.1:8080/1.0/kb/bundles/{bundleId}/pause" +---- + +*Expected result:* Billing and entitlement are suspended. No invoices are generated for the paused bundle until it is resumed. + +=== Resume a Subscription Bundle + +[source,bash] +---- +curl -v -X PUT \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + -H "Content-Type: application/json" \ + -d '{}' \ + "http://127.0.0.1:8080/1.0/kb/bundles/{bundleId}/resume" +---- + +*Expected result:* Billing and entitlement restart. The next invoice covers the period from the resume date forward. + +=== Aviate Considerations for Pause/Resume + +* *Wallet* — wallet credits are not consumed during the pause period. Automatic top-off is not triggered while the bundle is paused. +* *Metering* — usage events submitted during the pause period are not invoiced. Depending on your configuration, they may be queued or rejected. +* *Coupons* — coupon duration continues to tick during a pause. If a coupon expires while the bundle is paused, it will not apply when the bundle resumes. + +== Entitlement vs. Billing Dates + +Kill Bill distinguishes between two dates for every subscription lifecycle event: + +* **Entitlement date** — when service access starts or stops for the customer. +* **Billing date** — when charges start or stop being generated. + +These dates can differ. For example, you can cancel a customer's entitlement today (they lose access immediately) but continue billing through the end of the month: + +[source,bash] +---- +curl -v -X DELETE \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + -H "X-Killbill-CreatedBy: admin" \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}?entitlementPolicy=IMMEDIATE&billingPolicy=END_OF_TERM" +---- + +This combination is useful when you want to cut off service right away but avoid proration complexity, or when contractual terms require billing through the end of the period. + +== What to Verify + +After performing lifecycle operations, verify the results using these API calls: + +=== Check Subscription Status + +[source,bash] +---- +curl -v \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + "http://127.0.0.1:8080/1.0/kb/subscriptions/{subscriptionId}" +---- + +Look for: + +* `state` — should be `ACTIVE`, `CANCELLED`, or `BLOCKED` (paused). +* `planName` — should reflect the new plan after a change. +* `billingStartDate` / `billingEndDate` — confirm the effective dates. + +=== Check Invoices + +[source,bash] +---- +curl -v \ + -u admin:password \ + -H "X-Killbill-ApiKey: my-tenant" \ + -H "X-Killbill-ApiSecret: my-secret" \ + "http://127.0.0.1:8080/1.0/kb/accounts/{accountId}/invoices?withItems=true" +---- + +* After a plan change: the latest invoice should contain a credit for the old plan and a charge for the new plan. +* After cancellation: no invoices should appear after the effective cancellation date. +* After pause: no invoices should be generated during the pause window. +* After resume: the next invoice should cover the period starting from the resume date. + +== Common Pitfalls + +1. **Cancelling with `IMMEDIATE` on day 1 of a billing period** — generates a large credit for the nearly unused period. Consider `END_OF_TERM` if this is undesirable. +2. **Pausing without resuming** — the bundle remains permanently paused with no billing and no alerts by default. Set a reminder or use Kill Bill's tag system to track paused bundles. +3. **Changing to a plan not in the catalog** — returns a `400 Bad Request` error. Verify the plan exists in the Aviate catalog first. See https://docs.killbill.io/latest/aviate-catalog-guide.html[Catalog Guide]. +4. **Cancelling a subscription managed by a contract** — may conflict with contract terms. Check whether the subscription is governed by an Aviate contract before cancelling. See https://docs.killbill.io/latest/aviate-contracts.html[Contracts]. +5. **Mixing entitlement and billing policies incorrectly** — setting `entitlementPolicy=END_OF_TERM` and `billingPolicy=IMMEDIATE` stops charges before the customer loses access, which can be confusing. Ensure the policy combination matches your business intent. +6. **Ignoring coupon plan restrictions during plan change** — if the new plan is not in the coupon's `planList`, the discount silently stops applying. Verify coupon compatibility before changing plans. + +== Related + +* https://docs.killbill.io/latest/aviate-catalog-guide.html[Catalog Guide] — available plans and plan management +* https://docs.killbill.io/latest/aviate-quotes-orders.html[Quotes & Orders] — creating subscriptions via orders +* https://docs.killbill.io/latest/aviate-coupons.html[Coupons] — coupon behavior during plan changes +* https://docs.killbill.io/latest/aviate-wallet.html[Wallet] — credit behavior after cancellation +* https://docs.killbill.io/latest/aviate-tax.html[Tax Configuration] — tax recalculation on plan changes +* https://docs.killbill.io/latest/aviate-metering.html[Metering] — usage event handling during lifecycle changes +* https://docs.killbill.io/latest/aviate-credits-refunds.html[Credits, Refunds & Invoice Adjustments] — financial corrections