The starting point for the decision
A product priced at KRW 33,333 is canceled, but the refund is calculated as KRW 20,333. In a bundled order with a coupon, canceling another product first changes that same product’s refund again. Comparing product prices alone cannot establish an error. You must also examine changes to discounts on remaining items and shipping charges.
A partial-cancellation amount should be the difference between the confirmed current balance and the amount that must remain under the agreed policy, not the canceled product’s displayed price. Preserve original sales amounts, item-level discount allocations, shipping, taxes, and adjustments, and accumulate only successful cancellations. With the same remaining products under the same conditions, different cancellation orders must produce the same cumulative refund and balance.
The following is a technical design example for one ordinary KRW card payment before shipping. It excludes card-issuer instant discounts, points, mixed tenders, currency conversion, additional charges, disputes, and chargebacks. It does not prescribe the legally correct treatment of discount clawbacks or shipping liability. Actual services must establish policies after reviewing terms and applicable law.
Do not hide product prices, discounts, and shipping in one number
The product amount relevant to refunds is the customer’s sale price, not procurement cost. Storing only an order-level coupon total makes it difficult to reproduce its allocation to particular items and quantities later. Preserve the following distinctions when confirming the order.
| Item | Information to preserve | Partial-cancellation question |
|---|---|---|
| Sales amount | Unit sale price and currency by product, option, and quantity | Which sold unit is being canceled? |
| Discount | Provider, rule version, eligible items, allocation by item/quantity | Do discounts on remaining items change too? |
| Shipping | Original charge and free-shipping conditions by shipping group | What changes when eligibility is lost or all items are canceled? |
| Taxes and adjustments | Whether tax is included, tax category, adjustment reason and amount | Was tax added twice or hidden under another item? |
| Payment | Approved currency/amount, payment ID, successful cancellations | How much has actually been canceled within the approved amount? |
Keep merchant coupons, issuer discounts, and store credit separate to preserve who funded the benefit and which payment method receives the return. If prices include tax, recording a tax breakdown must not add it to the total again. For mixed taxation, distinguish the total refund from its tax-exempt portion; Toss Payments handles taxFreeAmount separately in partial-cancellation requests for such merchants.[5]
If an ecommerce platform already manages the order, first inspect its confirmed allocations rather than estimates of your own. Shopify distinguishes DiscountApplication, which describes discount rules, from DiscountAllocation, which records the amount actually applied to a product or shipping line. Those objects do not automatically determine your refund policy.[6]
Assess shipping separately from item cancellation too. Shopify Admin lets operators select shipping refunds separately, capped at the refundable amount already charged. The free-shipping reassessment below is a separately chosen hypothetical-store policy, not a restatement of that feature.[7]
Connect transaction states from order to refund — Integrate payment methods with orders and verify delays, retries, cancellations and refunds.
Design example: a KRW 10,000 coupon on a KRW 66,666 order
Assume one each of products A, B, and C. All amounts are in KRW, and sale prices include applicable taxes. There are no additional taxes or other adjustments.
| Product | Sales amount | Initial coupon allocation | Discounted product amount |
|---|---|---|---|
| A | 33,333 | 5,000 | 28,333 |
| B | 22,222 | 3,333 | 18,889 |
| C | 11,111 | 1,667 | 9,444 |
| Total | 66,666 | 10,000 | 56,666 |
The following coupon and shipping policies are choices made to explain the calculation, not defaults of a particular gateway or ecommerce platform.
| Policy item | Rule chosen in this example |
|---|---|
| Coupon provider and allocation | Allocate and store a merchant-funded KRW 10,000 coupon proportionally to original sale amounts. |
| Coupon after partial cancellation | If the remaining items’ pre-discount total is at least KRW 50,000, keep only their original allocations; do not redistribute canceled items’ shares. Below KRW 50,000, remaining-item discounts become zero. |
| Shipping | Zero when the remaining pre-discount total is at least KRW 50,000; KRW 3,000 for the whole order when items remain below that threshold. |
| Cancellation of the final item | Shipping becomes zero because nothing has shipped and no other costs remain. |
The original approved amount is 66,666 - 10,000 + 0 = KRW 56,666. A later change to KRW 3,000 shipping is not a new card approval. It changes the composition of the amount retained from the existing payment balance. The operations interface should therefore distinguish item refunds, discount changes, and shipping changes.
Decide even the one-won allocation at the start
Exact proportional allocations are KRW 5,000 for A, 3,333⅓ for B, and 1,666⅔ for C. Rounding each down to whole won totals 9,999. Allocate the remaining won to C, which has the largest fractional remainder. Break equal remainders using a fixed item/sold-unit identifier order, not cancellation-request order.
This is the allocation rule chosen for this example, using the largest-remainder method. Other rules are possible, but allocations must sum to the original coupon and reproduce the same result when recalculated. Multiple quantities of the same product require per-unit allocations or an equivalent deterministic rule so canceling only some units remains reproducible.
Internal arithmetic in this KRW example uses integers. For fractional prices or ratios, use exact decimal/fraction arithmetic and convert units at a defined boundary. API serialization is separate: Toss Payments’ cancelAmount type is number, so KRW 9,444 is sent as numeric 9444.[1]
Do not copy that representation unchanged to other currencies and APIs. Stripe, for example, accepts amounts in the currency’s smallest unit: USD 10 is 1000, while JPY 10 is 10, with additional exceptions for some currencies. Label internal amounts with currency and unit, and perform gateway-specific conversion at one boundary.[8]
Calculate what should remain before the canceled item’s price
Let S be the set of remaining products in this example.
Retained amount V(S)
= sum of remaining products’ sale amounts
- sum of item discounts retained under the policy
+ shipping under the policy
Current cancellation request amount
= previously confirmed payment balance - V(items remaining after cancellation)The previous balance is the value after earlier cancellations are confirmed and internal records agree with the gateway. If a cancellation is still processing or a separate cancellation in the gateway console is discovered, resolve that discrepancy first.
Canceling C first
A and B total KRW 55,555 before discounts. They meet coupon eligibility, so retain their original KRW 8,333 allocation and zero shipping.
Amount to retain = 55,555 - 8,333 = KRW 47,222
C cancellation = 56,666 - 47,222 = KRW 9,444Cancel B next, leaving only A. The pre-discount total falls below KRW 50,000, removing A’s KRW 5,000 discount and adding KRW 3,000 shipping.
Amount to retain = 33,333 + 3,000 = KRW 36,333
B cancellation = 47,222 - 36,333 = KRW 10,889
Cumulative total = 9,444 + 10,889 = KRW 20,333Refunding B’s original discounted amount of KRW 18,889 would miss the KRW 5,000 change to A’s remaining discount and the KRW 3,000 shipping change.
Canceling B first
A and C total KRW 44,444. Coupon eligibility is lost and shipping begins at the first partial cancellation.
Amount to retain = 44,444 + 3,000 = KRW 47,444
B cancellation = 56,666 - 47,444 = KRW 9,222
Amount to retain after canceling C = 33,333 + 3,000 = KRW 36,333
C cancellation = 47,444 - 36,333 = KRW 11,111
Cumulative total = 9,222 + 11,111 = KRW 20,333Individual refunds for B and C differ between the two sequences. But once only A remains, both have cumulative cancellations of KRW 20,333 and a balance of KRW 36,333. Compare cumulative outcomes under the same final conditions, not a fixed refund assigned to one item. If shipping progress or policy versions also changed, the conditions are no longer identical; record those changes too.
This is not a refund formula that works unconditionally under every policy. Suppose products priced at KRW 49,900 and 100 receive a KRW 10,000 coupon, producing a KRW 40,000 payment. Canceling the KRW 100 item while removing the coupon and adding shipping leaves KRW 52,900 to retain: a negative refund of KRW -12,900. Do not clamp it to zero or send a negative cancellation. Stop the automatic-refund path and review policy adjustments, such as retaining the discount, or whether a separate additional payment is needed. This example does not establish a legal right to collect that amount. Also check that a gateway request is positive and no greater than the confirmed balance. A legitimate business cancellation whose calculated refund is exactly zero needs a separate path, distinct from an amount error.
Invariants connecting order state and the gateway balance
For ordinary cancellations against one original approved payment, define:
P = original approved amount
C = deduplicated sum of successful cancellation amounts
B = cancellable balance confirmed by the gateway
P = C + B
0 ≤ C ≤ P
0 ≤ B ≤ PIn Toss Payments, totalAmount is the original amount and balanceAmount the cancellable balance. Verify that each cancellation’s cancelStatus is DONE. Within this article’s ordinary card-payment scope, check amounts alongside PARTIAL_CANCELED when some funds are canceled and CANCELED when all are canceled.[1]
Internal checks must be stricter: successful totals can match the gateway balance even when the wrong item was canceled or shipping deducted twice. Once processing is complete and both sides are synchronized, B = V(remaining items) must also hold. During processing, store the confirmed balance and target post-cancellation amount separately; do not label the discrepancy resolved.
Do not add amounts in internal states such as REQUESTED, PROCESSING, UNKNOWN, or FAILED to the successful total. These are proposed internal states, not a copied list of Toss cancellation states. Request counts and HTTP retry counts are not cancellation counts either.
Preserve cancellation transactions by their individual identifiers. Toss lastTransactionKey points only to the last transaction; overwriting that one field cannot preserve earlier partial cancellations. Record each transactionKey and amount, and apply repeated receipt of the same transaction once.[4]
Even when the final item disappears, do not automatically force the balance to zero. Here V(empty order)=0 because shipping has not begun and no costs remain. A different policy retaining incurred costs requires distinguishing item state from payment state. Also distinguish gateway cancellation success from when the refund appears on the customer’s card or account; they may occur at different times.[2]
From transaction alerts to response and provider review — Follow symptom-based checks to establish transaction status and handle incidents, refunds and recurring issues.
Do not decide the amount again during a retry
Before executing a cancellation, persist its operation ID, sold units, policy version, pre-calculation balance, target retained amount, and fixed request amount. A repeat of the same operation returns the existing result rather than creating a new calculation and cancellation. Reject reuse of an identifier with a different amount or target.
Use an operation-specific idempotency key externally. Toss Payments’ Idempotency-Key is valid for 15 days from first use, and a still-processing request can return 409 IDEMPOTENT_REQUEST_PROCESSING. Its guidance also warns against changing the key to repeat the same cancellation merely because an error was received.[3]
Treat a timeout as “outcome unresolved,” not “cancellation definitely failed.” Confirm through permitted identical retries within the validity period and payment lookup. Remember that the API key, URL, and method also define request identity, alongside the key and body. If validity expired or credentials changed, establish whether the previous operation ran before proceeding with a new key.[3]
Partial and full cancellations share one payment balance. This example permits only one unresolved cancellation per payment, with customer interfaces, operator interfaces, and retry jobs using the same execution path. Acquire execution ownership and a version in a short database transaction; avoid long locks across network calls. Worker restarts must not erase ownership or unresolved work.
Persist the meaning of “full cancellation” too. If it means canceling everything remaining after an earlier partial cancellation completes, fix the amount using the latest verified balance. Toss Payments performs a full cancellation when cancelAmount is omitted; prevent accidental omission for an amount-fixed operation. Payments with isPartialCancelable=false must not enter this partial-cancellation path.[2][1]
If the gateway succeeded but internal storage failed
Do not cancel the same amount again as a new operation. Keep the unresolved record, recover the same operation’s response or re-query payment, and verify cancellation transactions and balance. Record the confirmed transaction once under deduplication constraints, applying the internal successful total and order refund display together. Link subsequent notifications to the confirmed record so they can be redelivered.
An idempotent retry may return the first request’s response. Use it to verify that operation, not to overwrite a newer balance reflecting later cancellations. Compare internal operation versions and observation times, querying current payment state again when necessary.[3]
Separate cancellations made by operators in the gateway console can also appear. Equal amounts alone do not link one to a particular item request. Without proof from stored transaction identifiers, operation records, or before/after transaction sets, escalate to operator review. Do not arbitrarily change item state merely to match the gateway balance.
Give operators order/payment/cancellation IDs, policy version, expected amount, verified transactions and balance, error classification, and last check time. Exclude secret keys and complete card/account information. Hold automatic execution for work exceeding retry or verification deadlines, amount mismatches, and external cancellations that cannot be linked.
Verify different cancellation orders and test failures separately
Canceling the example order completely in all six possible sequences produces the following. These are design-example calculations checked with local code, not results of real gateway requests. The first, second, and third columns are cancellation request amounts in each sequence.
| Cancellation order | First cancellation | Second cancellation | Third cancellation | Successful total | Final balance |
|---|---|---|---|---|---|
| A → B → C | 20,333 | 22,222 | 14,111 | 56,666 | 0 |
| A → C → B | 20,333 | 11,111 | 25,222 | 56,666 | 0 |
| B → A → C | 9,222 | 33,333 | 14,111 | 56,666 | 0 |
| B → C → A | 9,222 | 11,111 | 36,333 | 56,666 | 0 |
| C → A → B | 9,444 | 22,000 | 25,222 | 56,666 | 0 |
| C → B → A | 9,444 | 10,889 | 36,333 | 56,666 | 0 |
At every step, the original approved amount equaled successful cancellations plus the remaining balance. Correct arithmetic alone, however, does not validate the integration. After implementation, verify the following expectations separately in an authorized test environment.
| Test situation | Expected outcome |
|---|---|
| Cancel C once | Cancellation KRW 9,444; balance KRW 47,222; A and B remain. |
| Submit the same cancellation twice | Recognize one operation; gateway execution effect and internal success application each occur once. |
| Re-query or receive a successful transaction again | Do not add an already-recorded transaction ID to the successful total again. |
| An old cancellation response arrives late | Record operation success with deduplication, but do not revert current order/payment state to the old response’s balance. |
| Partial and full cancellation contend | Confirm them one at a time. If C is canceled first and the rest follows, amounts are KRW 9,444 and 47,222. If full cancellation completes first, do not execute the later partial request. |
| Timeout or still-processing response | Preserve the unresolved amount without increasing the successful total; make new work against the same balance wait. |
| Internal application fails after gateway success | Recover and apply the successful transaction once without a new cancellation. |
| A different item is canceled for the same amount | Do not link requests by amount alone; verify item, operation, and transaction associations. |
| Rounding remainder or negative refund | Reproduce the stored allocation rule. Do not push unexplained differences or a need for extra collection onto the final item. |
Refunding the verified remaining balance in the final cancellation completes this example’s full-return policy; it is not a correction mechanism for earlier arithmetic errors. Resolve minimum-unit remainders through the original allocation rule, and leave later unexplained balance differences as exceptions.
When policies change, include eligibility boundaries, cancellation sequences, retries, and unresolved states in regression scope, rather than rerunning only normal cancellations. Use the change-based regression-scope workbook to record selected tests and exclusions.
Reliable partial cancellation begins with explainable amount policies and preserved transactions, not a new tool. With one gateway and simple orders, start inside the existing order system with policy versions, allocations, per-operation idempotency, and successful-transaction records. Mixed payments or external operator cancellations require additional design for returns by payment method and exception handling.
If implementation and verification responsibilities are difficult to divide after agreeing on policy, see the support scope of IXC’s payment-gateway integration and payment-system implementation service.



