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

# Payin Agreements & Transaction Limits

> How to read Service → Payin: every column on your payin agreement list, the fee and settlement-cycle notation, agreement statuses, the amount limits — plus what the two limit-exceeded errors mean, how the daily limit is counted and released, and which name your customer sees at checkout.

**Service → Payin** is the Portal's record of what you are actually contracted to accept: which
payment methods, in which countries and currencies, on what fee basis, with which amount limits and
refund rules. This page explains how to read it, and what happens when a payment hits one of those
limits.

Order creation itself is covered in the
[Order Create field guide](/docs/en/support/order-create-field-guide); refund behaviour in
[Refund FAQ](/docs/en/support/refund-faq); what the checkout screen shows customers in
[Cashier display rules](/docs/en/support/cashier-display-rules-faq).

## Where do I see which payment methods I am contracted for?

**Service → Payin**. One row per contracted combination, with these columns:

| Column | What it holds |
| - | - |
| `Payment Method Country` | The country or region the payment method applies to |
| `Product` | The product the row belongs to (for example a one-time payment or a subscription product) |
| `Payment method type` | The category — card, wallet, bank transfer and so on |
| `Payment method` | The specific payment method, with its logo |
| `Payment option` | Sub-rows where a method prices differently per option |
| `Fees` | Your fee basis for the row — see below |
| `User Fee` | Only present where at least one row charges the payer a fee — see below |
| `Settlement cycle` | Notation such as `T+3` — see below |
| `Minimum amount` | Smallest order amount accepted for the row |
| `Maximum amount` | Largest order amount accepted for the row |
| `Order Currency` | The currency the order is created in |
| `User Currency` | The currency the payer is charged in |
| `Refundable` | Which refund shapes the row permits — see below |
| `Refund Period` | How long after payment a refund remains possible |
| `Additional Fields For Refunds` | Whether refunds on this row require extra fields |
| `Order Expiry Customization` | Shown only where the row supports a custom order expiry |
| `Status` | The agreement state for the row |
| `Mini Apps Info` | Shown only where mini-app details exist for the row |

`Fees` and `Settlement cycle` are not shown for every contract type — if they are absent from your
page, that is your contract shape, not a missing permission.

Anything the backend does not supply renders as `-` rather than being left blank, so a dash means
"nothing configured for this row", not "zero".

## How do I read the Fees column?

The Fees cell is composed, not a single number. It is assembled from up to four parts:

| Shape you may see | Reading |
| - | - |
| `<percentage>% <currency>` | Percentage of the amount only |
| `<amount> <currency>` | Flat amount per transaction only |
| `<percentage>%+<amount> <currency>` | Both — a percentage plus a flat amount |
| `… (min <amount>)` | A floor: the fee never falls below this |
| `… (max <amount>)` | A ceiling: the fee never rises above this |
| `… (min <amount>, max <amount>)` | Both a floor and a ceiling |

Where the ceiling is expressed as a proportion rather than a fixed amount, the `max` part appears as
a percentage instead.

Two further details:

* The column header carries the tooltip **"Rounding up to 2 decimal places per transaction."** —
  rounding is applied per transaction, not once per settlement batch.
* Where a payment method prices differently per payment option, the row expands into one fee line
  per option. A payment option with no meaningful per-option distinction renders as `-`.

Your actual rates are commercial terms and are shown only on your own page — they are not published
anywhere.

## What is the User Fee column, and why can't I see it?

`User Fee` is a fee borne by the **payer**, not by you, on payment methods that work that way. The
column is **conditional**: the Portal only renders it when at least one row on your page actually
carries a user fee. If none do, the column is absent — nothing is hidden from you.

Its header tooltip reads **"Fees charged to end users."**

Where a user fee exists, the `Refundable` cell for that row gains a second line telling you what
happens to that fee on a refund:

| Second line | Meaning |
| - | - |
| `User fee refundable` (green check) | The user fee is returned as part of a refund |
| `User fee not refundable` (amber warning) | The user fee is not returned |

This matters when you quote a refund total to a customer: the payer's fee and your fee follow
separate rules. Your own fee behaviour on refunds is covered in
[Refund FAQ](/docs/en/support/refund-faq).

## What does a settlement cycle like T+3 mean?

The notation is a **cycle letter, a `+`, and a count**:

| Letter | Cycle |
| - | - |
| `T` | Daily |
| `W` | Weekly |
| `H` | Half-monthly |
| `M` | Monthly |

So `T+3` is a daily cycle with a delay of 3, `W+1` a weekly cycle with a delay of 1, `M+1` a monthly
one. A monthly variant also appears as `M+1X`.

Note the daily letter is **`T`**, not `D` — if you are mapping this into your own systems, do not
assume `D`. How the cycle turns into an actual payout is covered in
[Settlement cycle](/docs/en/support/settlement-cycle-faq).

## What do the agreement statuses mean?

The `Status` column carries one of five values, colour-coded:

| Status shown | Colour |
| - | - |
| `Active` | Green |
| `Initial` | Yellow |
| `Onboarding` | Yellow |
| `Suspended` | Red |
| `Terminated` | Red |

Read the colour as the practical signal: green is the settled, in-service state; yellow means the
row is still being brought up; red means it is not in service. A value the Portal does not
recognise is shown as-is rather than being hidden, and an empty one as `-`.

If a row you expect to transact on is not green, raise it with your Waffo contact before debugging
your integration — the agreement state is set on Waffo's side, not from the API.

## What do the Refundable values mean?

Four values, and they are about **shape**, not about whether any individual order can be refunded
right now:

| Value | What the row permits |
| - | - |
| `No` | No refunds on this payment method |
| `Only Full Refund` | One refund, for the entire amount |
| `Full Refund or One-time Partial Refund` | Either one full refund, or a single partial refund |
| `Full Refund or Multiple Partial Refunds` | A full refund, or several partial refunds |

`Refund Period` next to it is the window after payment inside which a refund is still accepted.
Both feed the Portal's per-order refund control — see
[Why is the Refund button missing or greyed out?](/docs/en/support/transaction-field-reference-faq).

## Which filters does this page give me, and can I export it?

Six filters: `Payment Method Country`, `Payment method type`, `Payment method`, `Order Currency`,
`User Currency` and `Status`. `Product` is a **column, not a filter** — to narrow by product, filter
on the other fields and read the Product column.

**Export** produces a CSV of the current view:

| Behaviour | Detail |
| - | - |
| Scope | Whatever your filters currently select |
| Row cap | Up to 2,000 rows per export |
| Delivery | Downloads straight away — this is not one of the queued report exports |
| Headers | Backend header names, which do not all match the on-screen labels — notably the currency columns are exported as *acquiring currency* and *local currency* where the page shows `Order Currency` and `User Currency` |

Queued, asynchronous exports elsewhere in the Portal behave differently; see
[Exports & Report Downloads](/docs/en/support/export-center-faq).

## Why doesn't the row order match the Payment Method Country column?

Rows are sorted **country → payment method type → payment method name → product**, ascending.

The subtlety: the sort key is the agreement's own country field, while the first column displays the
payment method's country. For most rows these coincide; where they differ, the visible order will
not look strictly alphabetical by the column you are reading. Sorting is also applied per page of
results, so paging does not re-sort the whole set.

## What does the `Order Expiry Customization` column actually tell me?

The **range of order expiry you are allowed to ask for** on that row — not a
single default. Where a payment method accepts a custom expiry, the cell shows a
time range; where it does not, the cell shows `-` and the expiry is whatever the
contract sets.

Two details that decide how you read it:

* The range shown is the **intersection** of what the row allows on a mobile app
  and what it allows on a desktop browser, so it is the window that is safe
  regardless of which terminal your shopper is on. A row can be more permissive
  for one terminal than the range implies.
* The whole column is **hidden** unless at least one row on your page has a
  customisable expiry. Its absence means "none of your rows support this", not a
  missing permission.

## Can I set the order expiry myself, and what wins if it conflicts?

Send `orderExpiredAt` on `order/create`. Omit it and the contract's expiry
applies — that is the documented default.

What happens to a value you do send:

| What you send | What happens |
| - | - |
| Nothing | The contract expiry applies |
| A time already in the past | The value is **discarded** and the contract expiry applies. The order is created; nothing is refused |
| A time only a few minutes out | Accepted as sent. Bear in mind the shopper needs time to complete the payment, and some methods need more than others |
| A time outside the row's allowed range | Treat the range as a hard requirement and stay inside it. Sending a value outside it is a different situation from sending a value in the past, and the order-creation response does not tell you which of the two happened |

One code not to misread: **`A0034`** is returned when the expiry you provided
**has already expired** by the time the order is being acted on. It is not the
code for "your expiry was outside the allowed range".

Order expiry, once set, is the shopper's payment window — what happens when it
closes, and which order states are final, is in
[Payment Timeouts & Finality](/docs/en/support/payment-timeout-and-finality-faq).

## What is the `Mini Apps Info` column?

The wallet mini-programs registered for that row. Each entry is a mini-app name;
hovering it shows the app's ID and name. The column is pinned to the right of the
table and is **only rendered when at least one row on your page has mini-app
entries** — otherwise it is absent entirely, and a row with none shows `-`.

You need this when you are taking payments from inside a wallet's mini program,
because the app ID is what identifies your mini program to the payment method.
The integration itself, including the product (`MINI_PROGRAM_PAYMENT`) and the
terminal value to send, is in
[Mini Program Payments](/docs/en/support/mini-program-payments-faq).

## My order was rejected for exceeding a limit. Which limit?

There are three, and the message tells you which:

| Message | Error code | Meaning |
| - | - | - |
| `Payment amount should be within the range` | `A0008` | The order amount is outside the `Minimum amount` / `Maximum amount` of the **Service → Payin row** — a payment-method-and-currency range |
| `Payment amount exceeds transaction limit.` | `A0020` | This **single** order exceeds an **account-level** per-transaction limit set for that **order currency**. It is independent of the payment method |
| `Accumulative payment amount exceeds daily transaction limit.` | `A0021` | The order is within the per-transaction limit, but your **daily total in that order currency, across all payment methods**, would exceed its cap |

For `A0008`, compare the order amount against the matching Service → Payin row. For `A0020` and
`A0021`, the row is the wrong place to look: these limits are keyed by **merchant ID + order
currency** only, so switching the payer to a different payment method in the same currency does
not help, and raising one currency's limit does nothing for another. For `A0021` the order size is
not the problem — the day is. All three are returned at order creation, so no order is left
half-created.

Where no account-level limit is configured for that order currency, the `A0020` / `A0021` checks
do not run and neither error can occur. The account-level limits are not shown on Service → Payin;
if you need to know them, ask your Waffo contact.

## How exactly is the daily limit counted, and when is headroom released?

| Question | Answer |
| - | - |
| Which day? | The **UTC+8 calendar day**. A day boundary at `00:00 UTC+8`, not your local midnight and not UTC |
| When is the amount counted? | At **order creation**, immediately after the order is recorded — not at payment success |
| Which orders count? | Orders still in progress **and** successful ones |
| When is headroom released? | Only when an order reaches the **closed** state, and only if it was created on the same UTC+8 day |
| Do refunds release headroom? | **No.** Refunding a successful payment does not give the day its allowance back |

Two practical consequences:

* An order created just before `00:00 UTC+8` that succeeds after it still counts against the day it
  was **created**.
* Abandoned checkouts consume headroom until they close. If you are batching high volume near the
  cap, that is usually what has eaten the difference — not the payments that succeeded.

## Can my limits change without me doing anything?

Yes. Where the provider underlying a payment method revises its limits, Waffo recalculates the
limits on your agreement, and the new range appears on **Service → Payin**. Two mechanics are worth
knowing:

* Where the provider's limit is expressed in a different currency from your agreement, it is
  converted before being applied, so the resulting figure will rarely be a round number.
* Rounding is deliberately conservative: the **minimum is rounded up** and the **maximum is rounded
  down**, so the resulting range is never wider than the underlying one. For currencies with no
  minor unit — `JPY`, `KRW`, `VND` and similar — the minimum lands on a whole unit.

Requests outside the new range are rejected with the two errors above from the moment the change
lands. If your order volumes sit close to a limit, read the values off Service → Payin rather than
caching them in your integration.

## A payment method I use is being discontinued. How am I told?

When an agreement of yours is terminated, an email naming the payment method goes to the
administrator and super-administrator users on your merchant account. Details:

| Behaviour | Detail |
| - | - |
| Trigger | The agreement row moving into `Terminated` |
| Recipients | Users holding the Administrator or Super Administrator role on the merchant account at the time — so the recipients are whoever you have in those roles under Portal team management, not a fixed contact address |
| Deduplication | One email per merchant per payment method, not one per agreement row |
| Sandbox | Not sent in sandbox |

After that, the row shows `Terminated` in red on Service → Payin, and new orders on that
combination are refused. Existing orders and their refunds are unaffected by the status change
itself. If the method matters to your checkout, raise a replacement with your Waffo contact — the
Portal has no self-service path to re-enable a terminated row.

## Which merchant name does my customer see at checkout?

The checkout page shows a **display name**, which is resolved in this order:

| Case | Name shown |
| - | - |
| Your order-creation request passed a display name explicitly | That name wins over everything below |
| A single merchant account, display name configured | The configured display name |
| A single merchant account, **no** display name configured | **No merchant name is shown at all** on the order-information line |
| Sub-merchant under a parent, sub-merchant name configured | The sub-merchant's name |
| Sub-merchant under a parent, sub-merchant name **not** configured | Falls back to the parent's name |

The important asymmetry: an unconfigured display name does not fall back to your legal entity name
on the main order-information line — it simply shows nothing. If customers report a blank or
unexpected merchant name at checkout, this resolution order is the first thing to check. Other parts
of the checkout page have their own fallbacks, so a name can be absent in one place and present in
another. See [Cashier display rules](/docs/en/support/cashier-display-rules-faq) for what else the page
shows.


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