> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrading & Downgrading a Subscription Plan

> How /subscription/change works end to end: what remainingAmount is and who computes it, the four change statuses, what happens to the old subscription and when, how to replace a change the customer never paid, and what A0048 / A0049 / A0050 / A0054 mean.

`/api/v1/subscription/update` changes **amounts** on the subscription the
customer already has, and can only lower them. Everything else — a different
cycle, a higher price, a different plan, a different currency — is a **plan
change** through `/api/v1/subscription/change`. This page is the plan-change
contract. Amount-only changes are in
[Subscription amounts & per-period scheduling](/docs/en/support/subscription-amount-scheduling-faq).

## What does a plan change actually do?

It does **not** edit the existing subscription. It creates a **new
subscription**, has the customer authorise it, and retires the old one.

1. You call `/api/v1/subscription/change` with the original subscription's
   `originSubscriptionRequest` plus a complete description of the new plan and a
   fresh `subscriptionRequest`.
2. Waffo creates the new subscription and returns a `subscriptionAction`
   containing a `webUrl`. You send the customer there to authorise the new plan.
3. When the customer authorises and the first charge of the new plan succeeds,
   the change commits: the new subscription becomes `ACTIVE` and the original is
   retired.
4. Waffo pushes `SUBSCRIPTION_CHANGE_NOTIFICATION` to the `notifyUrl` you sent
   on the change request. `/api/v1/subscription/change/inquiry` returns the same
   view on demand.

So the answer to "can I just edit the customer's existing plan" is **no** —
there is no endpoint that rewrites the cycle, the period count or the plan on a
live subscription. The customer has to re-authorise, because the mandate they
originally gave was for the old terms.

**`subscriptionId` changes.** Key your own records on the new `subscriptionId`
from the moment the change commits, and keep the old one only for history.

## Do I have to hand the customer a payment page, or can it be silent?

You have to hand them a page. The change is authorised by the customer at
checkout — that is what `subscriptionAction.webUrl` is for. There is no
server-to-server plan change that reuses the existing mandate.

Plan for the customer abandoning it: until they authorise, nothing has changed
and the original subscription keeps billing normally.

## What are the change statuses?

| `subscriptionChangeStatus` | Meaning |
| - | - |
| `IN_PROGRESS` | The payment request has been initiated. |
| `AUTHORIZATION_REQUIRED` | Waiting for the customer to authorise. `subscriptionAction` carries the URL. |
| `SUCCESS` | The change completed. Final. |
| `CLOSED` | The change did not complete. Final. |

`CLOSED` is deliberately **one bucket for every non-success ending** — the
customer's payment failed, the authorisation window ran out, or you replaced this
change with a newer one. It is not a distinct "timed out" versus "declined"
signal, so do not branch on it as though it were.

What to do when you see `CLOSED`: nothing automatic. The original subscription is
still there and still billing. If the customer still wants the new plan, raise a
**new** change request; do not retry the old one, and do not reuse its
`subscriptionRequest`.

## What is `remainingAmount`, and who works it out?

`remainingAmount` is **required** on every change request, and it is a value
**you** compute. Waffo does not calculate proration and has no proration field —
see the proration section of the
[amounts page](/docs/en/support/subscription-amount-scheduling-faq).

What Waffo does with it: it holds the figure as a **credit balance on the
change**, in the change request's `currency`, and draws it down against the new
subscription's charges. When a charge it was drawn against is closed rather than
collected, or draws down less than was reserved, the unused part is returned to
the balance rather than lost.

Practical rules:

* It is your number. If you want "the customer paid for 30 days and used 12",
  you do that arithmetic and send the result.
* Send it in the change request's `currency`. It is not converted for you.
* It is a credit against future charges, not a refund. If the customer is owed
  money back rather than credit forward, refund the original charge instead —
  see [Refunds](/docs/en/support/refund-faq).
* `remainingAmount` also comes back on `/api/v1/subscription/change/inquiry`, so
  you can read what is left rather than tracking it yourself.

## Can a plan change also switch the currency, or the payment method?

Yes to both — a change request describes the new subscription from scratch, so
these are just fields on it:

| What you want to switch | How |
| - | - |
| Pricing currency | `currency` on the change request. It is required, so you are always restating it. |
| The customer's charging currency | `userCurrency` on the change request. |
| Payment method | `paymentInfo.payMethodType` / `payMethodName` on the change request; the customer re-authorises on the checkout page, so a genuinely different method is fine. |
| Cycle, period count, trial, per-period amounts | The `productInfoList` entry — all of the creation fields are available. |
| Coupon or promotion | `promotionInfo`, with either `promotionCode` or `couponId` (mutually exclusive). |

Because `remainingAmount` is expressed in the change request's `currency`, a
currency switch means you must express the carry-over in the **new** currency.
Waffo will not convert an old-currency balance for you.

Nothing is inherited from the old subscription. Anything you do not restate is
gone, including the per-period amount plan — restate `scheduledAmounts` in the
change request if a price ramp should survive.

## Can one change request carry several plans?

`productInfoList` is a list, and each entry describes a plan. Treat it as the
shape for **one** plan the customer is moving to: the subscription that results
has one cycle, one period count and one amount, so a request describing two
different cycles has no coherent meaning. If a customer is genuinely buying two
things on different cycles, that is two subscriptions.

## Does the old subscription stop immediately, or at the end of the period?

Both exist, and **you choose with `startTime`**:

| `startTime` on the change request | New plan starts | Original subscription |
| - | - | - |
| Omitted | Immediately, on authorisation | Cancelled as soon as the change commits — `MERCHANT_CANCELLED` |
| A future time | On that date | Kept running, with a cancellation **scheduled for the end of its current period**, so the customer keeps the service they already paid for |

The second row is how you build "the upgrade takes effect next cycle" without
double-billing and without writing a scheduler. It is also the only end-of-period
retirement Waffo performs — `/api/v1/subscription/cancel` is always immediate,
which is covered on the
[hosted management page](/docs/en/support/subscription-hosted-management-faq).

Two constraints on `startTime` for a change:

* It must not be **earlier than the original subscription's next scheduled
  charge**. A start time inside the period the customer has already paid for is
  rejected.
* The general creation limits still apply: strictly in the future, at most 365
  days out, and inside the validity window of the agreement that will carry the
  new subscription.

While a change with a future start is pending, the original subscription's
renewal is held rather than allowed to fire, so the customer is not charged for
a period they are about to leave.

## The customer never paid the upgrade link and now wants a different plan

Call `/api/v1/subscription/change` again with the plan they actually want.

**Waffo settles the pending change for you** as part of that call: the unpaid
charge on the pending new subscription is cancelled, that new subscription is
closed as replaced, the pending change moves to `CLOSED`, and the original
subscription is released so the new change can be built on it. You do not need
support to clear it, and you do not need a separate cancel call — there is no
"cancel change" endpoint precisely because the next change does it.

The important caveat: settlement can fail, and when it does the new change is
**not** created. You get one of these instead, and the pending change is still
the live one:

| Code | Message | What it means | What to do |
| - | - | - | - |
| `A0048` | The previous plan change is still in payment | The customer's payment for the pending change is genuinely still running and cannot be cancelled. | Wait for that payment's result, then act on it. Do not loop on `change`. |
| `A0049` | The previous plan change is being cancelled, please retry later | Settlement did not complete cleanly and the outcome is not yet confirmed. | Retry the same change request after a short pause. This one is genuinely retryable. |
| `A0050` | The previous plan change has been paid successfully | The customer paid the pending change while you were replacing it. It is now the customer's live plan. | Re-read the subscription, then raise a **new** change based on the **new** active plan. |
| `A0054` | Another plan change request for this subscription is being processed | Two change calls for the same subscription raced. | Retry after a short pause. |

`A0048` and `A0049` are easy to confuse. `A0048` means *the money is moving,
stop and wait for the answer*; `A0049` means *we do not yet know, ask again*.
Retrying on `A0048` will not help, and treating `A0049` as fatal will leave a
customer stuck.

## Can I change amounts and the plan in the same breath?

No, and they are mutually exclusive while either is in flight. Waffo serialises
`update`, `change` and `cancel` per subscription: while one is being processed,
the others are rejected with `A0055` ("The subscription is being modified, please
retry later").

`A0055` is retryable. If you re-price in bulk, treat it as backpressure rather
than a failure — but do not fire an `update` and a `change` at the same
subscription concurrently and expect both to land.

## What happens to per-period amounts and trials on the new plan?

Nothing carries over. The new subscription's `amount`, `trialPeriodAmount`,
`numberOfTrialPeriod` and `scheduledAmounts` come **entirely from the change
request**. Omit `scheduledAmounts` and the new subscription bills every period at
the new `amount`.

One thing to think about before granting a trial on an upgrade: the trial period
numbering restarts, because this is a new subscription. A customer who already
had your trial gets it again if you configure one.

## Which record is authoritative?

`/api/v1/subscription/change/inquiry`, keyed on the new
`subscriptionRequest` plus `originSubscriptionRequest`. It returns the change
status, `subscriptionAction` while authorisation is still pending, the current
`remainingAmount`, and the full new-plan description you sent.

`SUBSCRIPTION_CHANGE_NOTIFICATION` tells you *when* to look; the inquiry tells
you *what is true*. Persist the new `subscriptionId` from it, and switch your
entitlement to the new subscription only once the change reads `SUCCESS`.

## Where do I look next?

* [Subscription creation & validation](/docs/en/support/subscription-creation-validation-faq)
  — the value ranges the new plan has to satisfy, and the `startTime` rules.
* [Subscription amounts & per-period scheduling](/docs/en/support/subscription-amount-scheduling-faq)
  — what `update` can do instead, and why there is no proration.
* [When does a subscription actually charge?](/docs/en/support/subscription-billing-schedule-faq)
  — how the new plan's dates are derived.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.