> ## 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.

# Cross-Currency Subscriptions & Portal FAQ

> How FX works on a subscription priced in one currency but charged in another, where the converted amount and rate are visible, and how to find subscriptions, charges and refunds in the merchant Portal.

Subscriptions priced in one currency and charged in another, and how to find any
of it again in the merchant Portal.

Amount configuration lives in
[Subscription Amounts & Per-Period Scheduling FAQ](/docs/en/support/subscription-amount-scheduling-faq);
creation rules in
[Subscription Creation & Validation FAQ](/docs/en/support/subscription-creation-validation-faq).
General FX behavior for one-off orders is in
[Exchange Rates FAQ](/docs/en/support/exchange-rates-faq).

## Is the exchange rate on a cross-currency subscription locked when I create it?

**No.** A cross-currency subscription is **converted separately for every
period**. Nothing is locked at creation, and no single rate applies for the life
of the subscription.

What this means in practice:

| | Behavior |
| - | - |
| Rate scope | One conversion per billing period |
| When the rate is determined | When that period's charge is **initiated** by Waffo |
| Relationship to the charge date | The rate belongs to the moment the charge is initiated, which is **not** necessarily the calendar date the charge settles |
| Rate reused across periods | No |
| Merchant-side amount | Unchanged — you keep billing the same amount in your pricing currency |
| User-side amount | Varies period to period as the rate moves |

So a subscription priced at a fixed amount in your currency will show a
**different local-currency amount in different periods**, and that is correct
behavior, not a pricing error. Do not build reconciliation that assumes the
user-side amount is constant, and do not quote the user a fixed local-currency
price for future periods.

If a rate cannot be obtained for the currency pair when a period's charge is
initiated, that period is not charged with a stale rate — the attempt fails
rather than converting at an unknown rate.

## What does the user actually pay in a cross-currency period?

The amount charged in the user's currency is the converted amount **plus any user
fee that applies**, rounded to the precision of the user's currency. Both parts
are recalculated per period along with the rate, so the user-side total is a
per-period figure.

Your own side of the ledger is unaffected: the subscription's `amount` (or the
per-period scheduled amount) stays in your pricing currency.

## Where do I see the converted amount and the rate that was used?

Per charge, not per subscription. The subscription query response reports
`userCurrency` — the currency the user is charged in — but it does not carry a
converted amount, a rate, or a fee breakdown.

To get those, go to the individual charge:

| What you want | Where to get it |
| - | - |
| The period's charge IDs | `/api/v1/subscription/inquiry` with `paymentDetails=1` — each entry has `acquiringOrderId` and `period`, plus a `topupDetails` list when the period had additional charges |
| The amount the user actually paid | Order inquiry on that `acquiringOrderId`: `finalDealAmount`, together with `userCurrency` |
| Your own billed amount for that period | Order inquiry: `orderAmount` with `orderCurrency` |
| The rate and user fee shown to the user at payment time | Order inquiry `orderAction.actionData` while the order is awaiting authorization — it carries `paymentAmount`, `paymentCurrency`, `userFeeAmount`, `userFeeCurrency` and `exchangeRate`. Intended for merchants rendering their own checkout. |

`finalDealAmount` differs from `orderAmount` whenever the currencies differ or a
user fee applies — that difference is the point of the field, and it is the
number to reconcile the user's bank statement against.

In the Portal, the same information is on the individual **payment detail** page
(reachable from the subscription's payment history), not on the subscription
detail page.

## Can I find which subscription an Order ID belongs to?

Yes. In **Subscription → Agreements**, the search box has a dimension selector,
and **Order ID** is one of the options. Enter the order ID and the subscription
that charge belongs to is returned.

The full set of search dimensions on that list:

| Dimension | Notes |
| - | - |
| Subscription Id | The default selection |
| User Id | Your `merchantUserId` |
| User Email | |
| Card Last 4 Digits | Must be exactly 4 digits, otherwise the search returns nothing |
| Order ID | Reverse lookup — finds the subscription a charge belongs to |

An order ID that is not a subscription charge simply returns **no results** —
one-off orders have no subscription to point at. Same for an ID that does not
exist, or one belonging to another merchant.

Alongside the search box the list filters on **Period**, **Payment Method**,
**Status** (all nine subscription statuses are selectable) and **Created
Datetime**. Created Datetime is required and the selectable span is capped at
roughly two months, so a subscription created outside your chosen window will
not appear no matter which search dimension you use — widen the date range
first when a lookup unexpectedly returns nothing.

## What does the Portal subscription list show?

| Column | Notes |
| - | - |
| Subscription Id | Waffo's ID |
| Merchant Subscription Id | Your ID |
| Period Interval | Rendered as the cycle, from period type + interval |
| Payment Method | |
| Card Number | Masked; card-based subscriptions only |
| User Id | |
| User Email | |
| Subscription Amount | With the subscription currency |
| Status | |
| Created Datetime | |
| Action | Opens the subscription detail page |

Double-clicking a row opens the same detail page as the Action link.

## Who can view and cancel subscriptions in the Portal?

Viewing the Agreements list and a subscription's detail page is available to
Portal roles with access to the Subscription menu.

**Cancelling** additionally requires the subscription-cancel permission on your
role, so a read-only user sees the subscription but no cancel button. Beyond the
permission, the cancel button only appears when Waffo marks that particular
subscription as cancellable — a subscription that has already reached a terminal
status offers no cancel action. Cancelling asks for confirmation and then
cancels immediately; there is no undo, and re-subscribing means creating a new
subscription.

## Can I list every refund or charge under one subscription?

Yes, on both transaction lists. In **Transactions → Refunds** and
**Transactions → Payments**, the search dimension selector includes
**Subscription Id**. Select it, paste the subscription ID, and the list returns
every matching record.

Both lists also carry a **Subscription Id** column so you can tell subscription
traffic from one-off traffic at a glance:

| Case | Column value |
| - | - |
| The record belongs to a subscription | The subscription ID |
| One-off order, not from a subscription | `-` |

Behavior worth knowing before you build a process on it:

* A subscription ID that does not exist, or belongs to another merchant, returns
  an **empty list rather than an error**. An empty result is therefore not proof
  the subscription has no refunds.
* Refund records filtered this way include refunds you initiated **and** refunds
  arising from disputes; the filter does not distinguish the origin.
* One-off refunds have no subscription ID, so they can never be matched by this
  filter.
* The Refunds list applies its own default date range when you open it directly
  (roughly the last three months), so widen the range when looking for older
  refunds.

## How do I jump from a subscription to its refunds?

The subscription detail page shows a **Refund count** for that subscription.
When the count is greater than zero it is a link; clicking it opens
**Transactions → Refunds** with the Subscription Id filter already applied.

The date range applied by that jump is **the subscription's start date through
now** — deliberately wide, so the whole lifetime of the subscription is covered
rather than a recent slice. A count of zero is plain text with no link.

Two caveats:

* The count aggregates all refunds on that subscription regardless of origin,
  matching what the filtered list shows.
* If a subscription's charges were in more than one currency, the count is a
  **number of refunds**, not a summed value — read the amounts from the list
  itself.

## Why is there no "Yearly" option in the period filter?

Because there is no `YEARLY` cycle. The period filter is a two-level picker whose
top level is exactly the three cycle types Waffo supports — **Weekly**,
**Monthly**, **Daily** — and whose second level is the interval.

An annual subscription is `MONTHLY` with an interval of `12`, so that is how you
filter for it: pick **Monthly**, then **12**. Quarterly is Monthly + 3,
half-yearly Monthly + 6, fortnightly Weekly + 2. The full mapping is on
[Subscription creation & validation](/docs/en/support/subscription-creation-validation-faq).

The same shape appears in the exported file, which renders the cycle as interval
plus unit (`1 Month`, `12 Month`, `3 Week`) rather than as a named plan. Page and
export use the same wording deliberately, so a value you filtered on is the value
you will find in the file.

## How do I export the subscription list, and where does the file appear?

Set your filters on **Subscription → Agreements**, then use **Export**. The
export is a **server-side job, not a direct download**: submitting it takes you
to the **Subscription → Reports** tab, which lists your export records with a
status and, once ready, the download.

| Export status | Meaning |
| - | - |
| Wait Export | Queued |
| Need Retry | The job failed and will be attempted again |
| Success | Ready to download |
| Failed | Did not produce a file |

The Reports tab refreshes itself while a record is still in a non-final state, so
you can leave it open rather than reloading.

The export uses **the filters that were applied when you pressed the button**,
including the mandatory created-datetime range. It is not "export everything".

## Why is the Export button greyed out, and is there a row limit?

Three different conditions, with three different messages:

| What you see | Why | What to do |
| - | - | - |
| The button is disabled | You already have an export running. Only one at a time. | Wait for the record on the Reports tab to reach a final status. |
| "The current query data is empty" | The filters you applied match no subscriptions. | Widen the filters — the created-datetime range is the usual culprit. |
| "Too many records to export, please narrow your query" | The result set is over the export cap. | Narrow the date range or add a filter, then export in slices. |

The cap itself is enforced server-side and is not shown in the Portal, so treat
the message as the signal rather than trying to compute the limit. Splitting by
created-datetime is the reliable way to get a large history out, because that
filter is mandatory anyway.

## How do I pick out the automatic renewals in the transaction list?

**Transactions → Payments** has a **Card Transaction Type** filter with two
values:

| Value | Meaning |
| - | - |
| `CIT` | Customer-initiated — the customer was present and authorised this payment themselves |
| `MIT` | Merchant-initiated — charged in the background against a stored mandate |

Automatic renewals are `MIT`. A subscription's first charge, and a plan change
the customer authorised at checkout, are `CIT`.

One behaviour that catches people out: the **Card Transaction Type column is only
populated when you filter by it**. On an unfiltered query the column is blank for
every row — that is not missing data, it is a projection that only the filtered
query asks for. So to see CIT/MIT you must filter on CIT/MIT.

The filter applies to card traffic. For "is this row part of a subscription at
all", regardless of payment method, use the **Subscription Id** column and filter
described above.

## What are the extra rows on a subscription that are not billing periods?

A subscription can have charges attached to it that are not one of its periods.
They appear in the transaction lists with the subscription's ID like any other
subscription traffic, and the order inquiry response tells them apart through
`subscriptionInfo.subscriptionEvent`:

| `subscriptionEvent` | What it is | Has a period number? |
| - | - | - |
| `PERIOD_CHARGE` | A billing period | Yes |
| `USER_ADD_PAYMENT_METHOD` | The zero-amount authorisation raised when the customer adds a payment method | No |
| `TOPUP_CHARGE` | An additional charge taken inside an existing period, rather than a period of its own | Carries the period it was taken inside, not a period of its own |

Reading this correctly matters for reconciliation:

* **A zero-amount verification row is a real order.** It appears in the
  transaction list, it has an order ID, and it collects nothing. Filter it out of
  revenue reporting by amount or by event rather than assuming subscriptions only
  ever produce period charges.
* **A `TOPUP_CHARGE` is not a period.** It has no trial marker and no period
  start/end of its own, because it belongs inside a period that already has
  them. On `/api/v1/subscription/inquiry` with `paymentDetails=1` it is
  returned **nested under the period it was taken in**, as a `topupDetails`
  list on that period's entry (each item carries the top-up's
  `acquiringOrderId`, `topupRequest`, `topupAmount`, `orderCurrency` and
  `orderStatus`; the list is absent when the period has none). So a
  subscription's total collected amount is the period amounts **plus** their
  `topupDetails`, not the sum of the top-level rows alone.
* In the Portal and on the hosted management page these additional charges are
  shown against the period they belong to, numbered **`period-n`** — `5-2`
  means the second top-up accepted inside period 5. Only the period row carries
  a plain period number; a top-up row's own period column is `-`. That is why a
  billing history can show a period followed by extra lines rather than a clean
  one-row-per-period list.

Top-ups are created through `/api/v1/subscription/update` with a `topupInfo`
block — see the amounts page for what that does to the recurring amount.

To find them, query the order by its `acquiringOrderId` and read
`subscriptionInfo`; there is no dedicated list endpoint for them.


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