Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.gameball.co/llms.txt

Use this file to discover all available pages before exploring further.

Discount Reconciliation Examples

This guide explains how the Discount Reconciliation Layer distributes order-level discounts (coupons, gift cards, points redemptions) across line items when the POS sends them only at the order level. It walks through the algorithm with the same payload shape used by the Order API. Endpoint: POST https://api.gameball.co/api/v4.0/integrations/orders Headers:
apikey: {your-api-key}
secretkey: {your-secret-key}
Content-Type: application/json

Why Reconciliation Is Needed

Many POS and e-commerce systems apply a discount, gift card, or loyalty redemption at the order level (via totalDiscount or a reduced totalPaid) and never push the discount down to the line items. The cashback engine calculates rewards per line item, so without reconciliation the customer is over-rewarded.
Order:  totalPaid = 80, totalDiscount = 20
Items:  Item A ($30) + Item B ($70) = $100
Gap:    Line items sum to $100, but customer paid $80 → 20 extra points awarded
The reconciliation layer detects this mismatch and distributes the gap proportionally across eligible line items so that Σ line items = totalPaid.

Field Definitions

Line Item Fields

FieldDefinitionExample
priceOriginal unit price of a single item (does NOT change with quantity)If 1 unit = $50, then price: 50 even if qty 2
quantityNumber of unitsquantity: 2
discountTotal discount applied to this line item (NOT per unit)If $10 off this line, then discount: 10
taxesTotal tax for this line item after discount (NOT per unit)taxes: 7.5

Order Level Fields

FieldDefinitionExample
totalPriceSum of all line items + taxes (before any discounts)Items = 100 → totalPrice: 100
totalDiscountSum of ALL discounts (line item + order level + points + coupon)Coupon 20 + Points 20 → totalDiscount: 40
totalPaidWhat the customer actually paid (totalPrice - totalDiscount)100 - 40 = totalPaid: 60
Cashback is earned per line item based on (price × quantity) + taxes - discount. The reconciliation layer ensures the sum of line item totals equals totalPaid before cashback runs.

Reconciliation Formulas

Step 0 — Filter Negative-Price Items

FOR each line item:
    IF item.price < 0:
        remove the item   // gift card redemptions, store credit, etc.

Step 1 — Detect Mismatch

itemNet       = (item.price × item.quantity) + item.taxes - item.discount
lineItemTotal = Σ itemNet

mismatch = (lineItemTotal + order.totalShipping) - order.totalPaid
totalPaid includes shipping but line items don’t, so shipping is added on the line-item side of the equation.

Step 2 — Distribute Proportionally Across ALL Items

totalNet = Σ itemNet         // denominator uses each item's NET value (after any existing discount)

FOR each item (in order):
    IF remainingAmount ≤ 0: break

    share = ROUND( (itemNet / totalNet) × mismatch, 2 )

    IF share ≥ itemNet:
        item.discount += itemNet           // cap: drives this item's cashback base to 0, never negative
        remainingAmount -= itemNet
    ELSE:
        item.discount += share
        remainingAmount -= share
Items that already have a POS-applied discount aren’t skipped — they’re weighted by their net value, so a more-discounted item naturally receives a smaller share.

Step 3 — Final Validation

newLineItemTotal = Σ item.GetTotalPaid()
finalMismatch    = ABS( (newLineItemTotal + totalShipping) - order.totalPaid )

IF finalMismatch > 0.01:
    log warning, proceed anyway   // never block the order

Decision Table

MismatchMeaningAction
≤ 0.01Line items already match totalPaidSkip — proceed to cashback engine
> 0.01Order-level discount/redemption not on line itemsDistribute across eligible items
< -0.01Customer paid more than line items sum (fees/tips)Log warning, no distribution

Example 1: Order-Level Coupon (Most Common)

Scenario: Customer applied a coupon at checkout. The POS reduced totalPaid and set totalDiscount but left line items at full price.
ComponentCalculationValue
Item A (1 × $30)$30
Item B (1 × $70)$70
Line Item Total$100
totalPaid$80
Mismatch100 - 80$20
Item A share(30 / 100) × 20$6
Item B share(70 / 100) × 20$14
Item A cashback base30 - 6$24
Item B cashback base70 - 14$56
{
  "orderId": "INV-2026-002001",
  "totalPrice": 100,
  "totalDiscount": 20,
  "totalPaid": 80,
  "lineItems": [
    { "productId": "A", "price": 30, "quantity": 1, "discount": 6 },
    { "productId": "B", "price": 70, "quantity": 1, "discount": 14 }
  ]
}
The discount values shown (6 and 14) are what reconciliation writes onto the line items. The integration only needs to send discount: 0 — Gameball fills these in.

Example 2: Mixed — Some Items Already Discounted

Scenario: Item A has a $5 line discount from the POS. The mismatch is distributed across both items, weighted by net value, so the item with the bigger existing discount automatically gets a smaller share.
ComponentCalculationValue
Item A (1 × $30, disc $5)net$25
Item B (1 × $70)net$70
Line Item Total$95
totalPaid$75
Mismatch95 - 75$20
Item A share(25 / 95) × 20$5.26
Item B share(70 / 95) × 20$14.74
Item A final discount5 + 5.26$10.26
Item B final discount0 + 14.74$14.74
Item A cashback base30 - 10.26$19.74
Item B cashback base70 - 14.74$55.26
{
  "orderId": "INV-2026-002002",
  "totalPrice": 100,
  "totalDiscount": 25,
  "totalPaid": 75,
  "lineItems": [
    { "productId": "A", "price": 30, "quantity": 1, "discount": 10.26 },
    { "productId": "B", "price": 70, "quantity": 1, "discount": 14.74 }
  ]
}

Example 3: All Items Already Discounted

Scenario: Every line item already has a POS-applied discount, but their sum still doesn’t match totalPaid. The remaining gap is distributed across all items by net value.
ComponentCalculationValue
Item A (1 × $30, disc $5)net$25
Item B (1 × $70, disc $15)net$55
Line Item Total$80
totalPaid$60
Mismatch80 - 60$20
Item A added share(25 / 80) × 20$6.25
Item B added share(55 / 80) × 20$13.75
Item A final discount5 + 6.25$11.25
Item B final discount15 + 13.75$28.75
Item A cashback base30 - 11.25$18.75
Item B cashback base70 - 28.75$41.25
{
  "orderId": "INV-2026-002003",
  "totalPrice": 100,
  "totalDiscount": 40,
  "totalPaid": 60,
  "lineItems": [
    { "productId": "A", "price": 30, "quantity": 1, "discount": 11.25 },
    { "productId": "B", "price": 70, "quantity": 1, "discount": 28.75 }
  ]
}

Example 4: External Loyalty Redemption as Payment Method

Scenario: A POS like Square treats a points redemption as a payment method (not a discount). totalPaid is already reduced; line items remain at full price; totalDiscount is zero.
ComponentCalculationValue
Item A (1 × $150)$150
Item B (1 × $150)$150
Line Item Total$300
totalPaid (after redemption)$200
Mismatch300 - 200$100
Item A share(150 / 300) × 100$50
Item B share(150 / 300) × 100$50
{
  "orderId": "INV-2026-002004",
  "totalPrice": 300,
  "totalDiscount": 0,
  "totalPaid": 200,
  "lineItems": [
    { "productId": "A", "price": 150, "quantity": 1, "discount": 50 },
    { "productId": "B", "price": 150, "quantity": 1, "discount": 50 }
  ]
}
The algorithm doesn’t need to know why there’s a mismatch (coupon, gift card, loyalty as discount, loyalty as payment). It reconciles purely on lineItemTotal vs totalPaid — making it universal across POS patterns.

Example 5: POS Coupon + External Redemption Combined

Scenario: Customer used a 20couponANDredeemed20 coupon AND redeemed 20 of points through the POS. Neither was distributed to line items. Reconciliation handles both in a single pass.
ComponentCalculationValue
Item A (1 × $50)$50
Item B (1 × $50)$50
Line Item Total$100
Coupon discount$20
Points redeemed$20
totalPaid100 - 20 - 20$60
Mismatch100 - 60$40
Item A share(50 / 100) × 40$20
Item B share(50 / 100) × 40$20
{
  "orderId": "INV-2026-002005",
  "totalPrice": 100,
  "totalDiscount": 40,
  "totalPaid": 60,
  "redeemedAmount": 20,
  "lineItems": [
    { "productId": "A", "price": 50, "quantity": 1, "discount": 20 },
    { "productId": "B", "price": 50, "quantity": 1, "discount": 20 }
  ]
}
This was the previous double-distribution bug: reconciliation distributed $40, then DistributeRedeemedAmount distributed another $20 on top. The single-pass reconciliation now handles both deductions together, so cashback is correctly calculated on $60.

Example 6: Cap Protection — Discount Larger Than Cheap Item

Scenario: A large redemption could mathematically push a cheap item’s cashback base below zero. The cap ensures the per-item discount never exceeds the item’s value.
ComponentCalculationValue
Item A (1 × $5)$5
Item B (1 × $95)$95
Line Item Total$100
totalPaid$10
Mismatch100 - 10$90
Item A share (capped at $5)(5 / 100) × 90 = 4.50$4.50
Item B share(95 / 100) × 90 = 85.50$85.50
Item A cashback base5 - 4.50$0.50
Item B cashback base95 - 85.50$9.50
{
  "orderId": "INV-2026-002006",
  "totalPrice": 100,
  "totalDiscount": 90,
  "totalPaid": 10,
  "lineItems": [
    { "productId": "A", "price": 5,  "quantity": 1, "discount": 4.5  },
    { "productId": "B", "price": 95, "quantity": 1, "discount": 85.5 }
  ]
}

Example 7: No Mismatch (Pass-Through)

Scenario: The POS already distributed every discount onto line items. Σ line items already equals totalPaid. Reconciliation runs but does nothing.
ComponentCalculationValue
Item A (1 × $30, disc $6)$24
Item B (1 × $70, disc $14)$56
Line Item Total$80
totalPaid$80
Mismatch80 - 80$0
{
  "orderId": "INV-2026-002007",
  "totalPrice": 100,
  "totalDiscount": 20,
  "totalPaid": 80,
  "lineItems": [
    { "productId": "A", "price": 30, "quantity": 1, "discount": 6  },
    { "productId": "B", "price": 70, "quantity": 1, "discount": 14 }
  ]
}

Edge Cases Reference

Edge CaseHandling
No mismatchSkip — proceed directly to cashback
Items with existing discountIncluded in distribution, weighted by net value (smaller share)
Single line itemFull mismatch applied to that item (capped at its net value)
Share ≥ item net valueCapped at net value — item’s cashback base goes to 0, not negative
Rounding remainderSilently absorbed (typically < $0.01)
No line itemsSkip — cashback uses transaction-level totalPaid
totalPaid > lineItemTotal + shippingLog warning, no distribution (fees / tips case)
Negative-price items (gift card)Filtered out in Step 0
ShippingAdded to line item side of the mismatch equation
Gameball’s own redemption (HoldReference)Same flow — DistributeAmountAcrossLineItems is shared

Summary Table

ExampleScenarioLine Item TotaltotalPaidMismatchCashback Base
1Order-level coupon$100$80$20$80
2Mixed — some items pre-discounted$95$75$20$75
3All items pre-discounted (fallback)$80$60$20$60
4Loyalty as payment method$300$200$100$200
5POS coupon + external redemption$100$60$40$60
6Cap protection — cheap item$100$10$90$10
7No mismatch (pass-through)$80$80$0$80

Key Takeaway

Reconciliation guarantees Σ line item cashback base = totalPaid.Integrations don’t need to figure out which deduction caused the gap (coupon, gift card, loyalty as discount, loyalty as payment). Just send:
  • Line items at their natural prices
  • totalPaid reflecting what the customer actually paid
Gameball detects the mismatch, distributes it proportionally across eligible items, and never blocks the order if reconciliation can’t fully close the gap.