Skip to main content
Three subjects meet on this page because they share one property: the normal case is simple and the edge case is where money goes wrong. Each has a main page that this one supplements rather than repeats:
  • Fee & Tax Treatment — who bears a tax, what the customer is charged in each mode, and how tax appears in your settlement files.
  • Coupons & Promotion Codes — the discount engine end to end: defining coupons, issuing codes, redemption records, analytics, account-level caps.
  • Point Topup Reseller APIs — the six reseller endpoints, catalogue and pricing fields, order statuses.

I refunded an order in stages. How much tax comes back each time?

Each refund gets tax calculated on its own share of the order — except the last one, which is corrected so the totals reconcile exactly. The reason is rounding. Tax is rounded to two decimals on every calculation, so across several partial refunds the sum of the individually calculated tax amounts can drift above or below the tax that was actually collected on the original payment. Left alone, a customer fully refunded in four instalments could get back a cent more, or a cent less, than they paid. So the last refund is not calculated — it is derived: “Exhausts the order” means refunds already made plus this one reach or exceed the original order amount. Failed tax refunds are excluded from the running total, so a refund that failed on the tax side does not eat into the remainder. And if the subtraction would come out negative, the amount is floored at zero rather than being allowed to go the wrong way. The property this guarantees: fully refund an order in any number of steps, and the tax you get back is exactly the tax that was collected. No accumulated rounding drift, in either direction.

What if my refunds add up to more than the original order?

The excess is not refunded. Before any tax is worked out, the refund’s taxable base is clamped against what is left: The third row is the one to plan for: once the tax on an order has been fully refunded, additional refund activity against it produces no further tax refund, silently, without an error. Nothing is over-refunded, but nothing tells you the tax component was zero either. If you are reconciling tax, reconcile it against the original payment’s tax figure rather than by summing what each refund appeared to reverse.

Does a chargeback refund tax the same way as a refund?

It goes through the same cumulative accounting — chargebacks and refunds share one running total against the original payment, so a chargeback consumes remaining refundable tax exactly as a refund does, and the last-transaction correction applies to whichever of them lands last. One difference in the default: a chargeback is assumed to be for the full amount. Where a partial chargeback occurs, the amount has to be supplied explicitly; without it the full original amount is what gets reversed. What a chargeback does to the rest of your money — fees, settlement, the dispute amount — is a separate subject: Fee & Tax Treatment.

Tax could not be calculated on my order. Was the order rejected?

It depends on why, and the two outcomes are opposite: So “tax calculation failed” is not one behaviour. The downgrade path exists for the case where tax legitimately does not apply to you for that order, and it is deliberately silent: a downgraded order looks like an ordinary untaxed order, with no tax fields populated. It is not an error you will see. The postcode case is the one worth handling in your integration, because it is caused by the data you sent and it is fixable. An address whose postcode does not resolve against its country — a transposed code, a country mismatch, a postcode format the country does not use — fails the order rather than being downgraded. If you collect addresses yourself, validate the postcode against the country before you create the order; if the checkout page collects them, the customer is prompted to correct it. There is also a short retry on a timeout, so a single slow calculation does not by itself fail your order.

Why would tax not be calculated on my order at all?

Because tax arrangements are held per country, not per account. Having one does not give you all of them. For an order in a country you have no arrangement for, there is nothing to calculate against, so the calculation reports a downgradable failure and the order proceeds untaxed. That is the normal, intended path — not a misconfiguration. Two further properties of the per-country model that explain otherwise puzzling behaviour:
  • The inputs needed to calculate a tax and the inputs needed to remit it are two different sets, defined separately per country. An order can therefore carry everything needed to compute tax and still be incomplete for remittance purposes, or the reverse. Which fields a given country requires comes from your arrangement, not from a universal list.
  • The country that determines the tax is not necessarily where it is paid. Both are recorded per arrangement, and they can differ.
Who is charged the tax and who bears it — and why a merchant-borne tax is not shown to your customer at checkout — is covered in Fee & Tax Treatment.

Why is my redemption record still CREATED?

Because the order it belongs to has not reached a final state yet. A discount record is reserved at order creation and only confirmed later, which means CREATED is normal for an order still in progress, not a stuck record. The record advances when the order does: Consequences worth designing around:
  • CREATED reservations hold stock. An abandoned checkout holds its slot until the order closes. If a limited coupon looks exhausted while your redemption history shows fewer confirmed uses than the limit, unclosed reservations are usually the difference.
  • Only COMPLETED counts. Analytics counts confirmed redemptions, so a record in any other state will not appear in your discount reporting.
  • A record can be voided without your order ever existing. Where order creation fails after a discount was reserved but before the order was persisted, the reservation is still released — you will see a VOIDED record with no order behind it. That is cleanup, not a phantom order.

Does a refund give the redemption slot back?

Only a full refund does, and only under the account’s reversal strategy. A partial refund leaves the record COMPLETED and the slot consumed. The reasoning is that a partial refund has not undone the purchase, so the discount was still used. The practical consequence is that a customer who buys with a ONCE coupon and takes a partial refund does not get the coupon back. The strategy setting, what a zero-amount subscription trial does to the slot, and why reversal can never push a counter negative are all covered in Coupons & Promotion Codes.

Can a customer combine two discounts on one order?

No. One order carries one discount. There is no stacking, in either direction: you cannot apply a second discount to an order that already has one, and a customer cannot add one to an order you already discounted. Three separate rules enforce it, with three distinct messages: All three come back under A0003, with the specific message carried in the response — see Reading Your Payment Method Configuration for how A0003 details are formatted. If you need “two offers at once” commercially, that is one coupon configured for the combined value, not two coupons on one order.

What does “discount calculation result has expired” mean?

The discount amount you were quoted is time-limited, and you tried to create the order with a stale one. Calculating a discount and creating the order are two steps: the calculation returns what the customer will pay, and the order create references that result. If too long passes between them — a customer who left the page open, a checkout flow that holds the quote across a session — the referenced result is no longer accepted and the order is refused, with a message telling you to recalculate. Recalculating is the fix, and it should be automatic: on that error, re-run the discount calculation and create the order against the fresh result. Do not treat it as a coupon problem and do not show the customer a discount failure — the coupon is fine, only your quote went stale. Be prepared for the recalculated amount to differ, since eligibility is re-checked: a code that hit its redemption limit in the meantime will now be refused outright.

Which discount errors mean my setup is the problem, not the customer’s?

These come back on order creation, all under A0003. The split matters because half of them are yours to fix and half are simply the offer working as configured: The first row is the one that wastes the most time, because it looks like a coupon problem: every coupon you create will appear valid in the Portal and be refused at order creation. If nothing discounts, check the master switch before checking the coupon. The full eligibility order, including per-customer and first-time-only rules that depend on you sending a customer identifier, is in Coupons & Promotion Codes.

How long does an unpaid Point Topup order hold inventory?

Not indefinitely. Creating a Point Topup order reserves the stock it needs, and if the order is never paid, that reservation is released automatically after a default holding period (an operational setting, not a contract term — do not build a countdown on it). Two consequences:
  • An unpaid order is not a hold you can rely on. If you are staging orders ahead of payment, or letting a customer come back later to pay, the item may no longer be available when they do. Where availability matters, pay promptly rather than creating the order early.
  • Released stock does not resurrect the order. The order remains in its original state; releasing the reservation frees the item for other buyers, it does not cancel or reprice anything on your side.
Where an order fails outright, any amount already debited is returned to your balance — see Point Topup Reseller APIs.

Can one Point Topup order carry more than one item?

No. A Point Topup order carries exactly one SKU, with a quantity of one. Any other quantity is rejected at creation. To buy several items, create several orders. Each gets its own idempotency key, its own reservation and its own outcome, which is generally what you want anyway: one item failing does not take the others with it. Note also that a status of PAY_SUCCESS on such an order means the payment succeeded and fulfillment is yours to complete — Waffo does not deliver that SKU. Do not read PAY_SUCCESS as “the customer has their goods”. The field reference and the other statuses are on Point Topup Reseller APIs.

Where do I look next?