3 Commits
Author SHA1 Message Date
shafi-VM abffde336e fix(paypal): don't scale zero-decimal currency amounts on order creation (#29984)
PayPal order creation divided every amount by 100 before sending it,
regardless of currency. JPY has no minor unit, so `convertToSmallestCurrencyUnit`
stores it unscaled and dividing corrupted it: a JPY 10000 event asked PayPal
to collect 100.

Use the same zero-decimal-aware conversion that the price input and
`formatPrice` already use, so the amount charged matches the amount stored
and displayed. Currencies with minor units are unaffected — among PayPal's
supported currencies only JPY is both zero-decimal and supported, and the
added tests pin USD, EUR, HUF and TWD to their existing behaviour.
2026-09-04 15:40:06 +05:30
shafi-VM b879a54250 fix(hitpay): respect zero-decimal currencies when charging and storing (#30039)
HitPay's payment-requests API takes `amount` as a decimal string in major
units, so the integration divided by 100 on the way out and multiplied by 100
on the way back in. Both conversions were unconditional, but the price input
writes the amount through `convertToSmallestCurrencyUnit`, which stores
zero-decimal currencies unscaled.

For the three zero-decimal currencies HitPay offers — JPY, KRW and VND — a
¥10,000 event therefore asked HitPay to collect ¥100. The two unconditional
conversions cancelled out in the database, so the Payment row and the booker's
page both still showed ¥10,000 while only 1% was charged, and `refund()` is
not implemented for this app.

Route both directions through the shared helpers so the amount charged matches
the amount stored and displayed. Currencies with a minor unit are unaffected;
the added tests pin USD and SGD to their existing behaviour.
2026-09-04 15:39:40 +05:30
shafi-VM 761b780aa2 fix(bookings): refund all seat payments when cancelling a paid seated… (#29685)
* fix(bookings): refund all seat payments when cancelling a paid seated booking

A paid seated booking creates one Payment row per seat on the same bookingId (each seat's payment_intent.succeeded webhook flips only its own payment via handlePaymentSuccess). processPaymentRefund only refunded payment.find((p) => p.success) — the first successful payment — so fully cancelling a multi-seat paid booking with an auto-refund policy refunded a single seat and left the other attendees charged with no error surfaced.

Refund every successful, not-yet-refunded payment instead.

Tests: unit regression in processPaymentRefund.test.ts (all payments refunded, verified red before the fix); integration test in handleCancelBooking.test.ts proving a full cancel of a seated multi-payment booking hands all payments to the refund path.

* fix(bookings): make seat refunds resilient and drop dayjs in test

Address CodeRabbit review on #29685:

- processPaymentRefund now attempts every seat's refund independently and throws an aggregate error, so one provider failure no longer skips the remaining seats.
- Add a regression test for that resilience, and use native Date instead of dayjs for fixed-offset test timestamps.
2026-07-09 14:23:03 +05:30