Sleva se ztratí, když se částka pro bránu skládá znovu z položek
import { Aside } from ‘@astrojs/starlight/components’;
Symptom
Sekce “Symptom”Zákazník s věrnostní slevou zaplatí na platební bráně plnou cenu bez slevy. Objednávka v DB má správný total (se slevou), platba na bráně je vyšší — účetní pak „platbu nevidí”, protože ji hledá podle částky objednávky, která na bráně neexistuje. Projeví se jen u objednávek se slevou, takže bug dlouho uniká (většina objednávek slevu nemá).
Root cause
Sekce “Root cause”Kód pro start platby skládal Total znovu ze zdrojových položek:
// PŘED — sleva se ztratílet total = items.reduce((s, it) => s + it.ItemTotal, 0);if (shipping > 0) total += shipping;// order.total = subtotal - discount + shipping ← discount nikdeObjednávka přitom měla discount_amount uložený a promítnutý do total. Stejný vzor byl na dvou místech: v checkoutu i v „zaplatit znovu” flow (které vzniklo později kopií téže logiky — druhá cesta bug zdvojila). Druhá brána (starší integrace) slevu řešila kuponem, takže bug se projevil až po migraci.
Fix
Sekce “Fix”Sleva jako záporná položka — brána záporné ItemTotal akceptuje (ověřeno testovací platbou proti živému API, která se nechá expirovat):
// PO — total sedí na order.totalif (discountAmount > 0) { items.push({ Name: `Sleva ${discountPercent} %`, Quantity: 1, Unit: 'ks', UnitPrice: -discountAmount, ItemTotal: -discountAmount }); total -= discountAmount;}V měně brány ≠ měně objednávky pozor: pokud položky používají vlastní ceníkové ceny (ne kurzový přepočet), slevu počítat procentem ze součtu položek v měně brány, ne přepočtem uložené částky.
Přeplatky dohledat SQL porovnáním zaplacené částky z payment logu proti total objednávky a vrátit partial refundem.
Jak to najít u sebe
Sekce “Jak to najít u sebe”-- zaplaceno vs. total objednávky (payment log vs orders)SELECT o.order_number, paid.amount, o.totalFROM orders o JOIN payment_log paid ON ...WHERE paid.amount <> o.total;Každé místo, kde se před voláním brány objevuje reduce((s, it) => s + it.price), je podezřelé — grep na stavbu gateway items a kontrola, že výsledek prochází assertem proti uloženému totalu.