Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/template-retail-react-app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## v10.1.0-dev
- [Bugfix] Sync `amount` on the basket payment instrument before placing the order so the persisted `PaymentTransaction` matches `orderTotal`.
- [Feature] Add cancel order modal on order detail page. Registered users with OMS enabled can cancel orders that are not yet shipped. Modal includes reason code selection and inline success/error feedback. Gated behind `app.oms.enabled` config flag. [#3861](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3861)
- [Bugfix] Render the search refinements panels open during server-side rendering. ChakraUI v2's Accordion forces every item closed in SSR (its open state depends on a post-mount layout effect that never runs on the server), so the panels opened only after hydration — a late relayout that could become the PLP largest-contentful-paint element. Replaced the Accordion with a controlled disclosure that honors its open state server-side.
- [Bugfix] Memoize the search refinements panel so it stops re-rendering on every parent render when the filter set is unchanged, improving PLP render stability. [#3855](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3855)
Expand Down
20 changes: 20 additions & 0 deletions packages/template-retail-react-app/app/pages/checkout/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ const Checkout = () => {
const {confirmingBasket} = useSFPayments()
const [isLoading, setIsLoading] = useState(false)
const {mutateAsync: createOrder} = useShopperOrdersMutation('createOrder')
const {mutateAsync: updatePaymentInstrumentInBasket} = useShopperBasketsMutation(
'updatePaymentInstrumentInBasket'
)
const {passwordless = {}, social = {}} = getConfig().app.login || {}
const idps = social?.idps
const isSocialEnabled = !!social?.enabled
Expand Down Expand Up @@ -117,6 +120,23 @@ const Checkout = () => {
}

const doCreateOrder = async () => {
// Sync payment instrument amount to current orderTotal right before placing
// the order. The amount stamped at addPaymentInstrumentToBasket time can go
// stale if the basket changes (promo, shipping method, items) afterward.
const appliedPayment = basket?.paymentInstruments?.[0]
if (
appliedPayment?.paymentInstrumentId &&
basket?.orderTotal != null &&
appliedPayment.amount !== basket.orderTotal
) {
await updatePaymentInstrumentInBasket({
parameters: {
basketId: basket.basketId,
paymentInstrumentId: appliedPayment.paymentInstrumentId
},
body: {amount: basket.orderTotal}
})
}
return await createOrder({
body: {basketId: basket.basketId}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,7 @@ describe('Checkout error display and submitOrder', () => {
]

let orderPostCalled = false
let updatePaymentInstrumentCalled = false
global.server.use(
rest.post('*/orders', (req, res, ctx) => {
orderPostCalled = true
Expand All @@ -1340,6 +1341,10 @@ describe('Checkout error display and submitOrder', () => {
}),
rest.get('*/baskets', (req, res, ctx) => {
return res(ctx.json({baskets: [currentBasket], total: 1}))
}),
rest.patch('*/baskets/*/payment-instruments/*', (req, res, ctx) => {
updatePaymentInstrumentCalled = true
return res(ctx.json(currentBasket))
})
)

Expand All @@ -1364,6 +1369,9 @@ describe('Checkout error display and submitOrder', () => {
await waitFor(() => {
expect(orderPostCalled).toBe(true)
})
// The fixture's payment instrument has amount=0 while orderTotal is non-zero,
// so doCreateOrder should have synced the amount before placing the order.
expect(updatePaymentInstrumentCalled).toBe(true)
expect(
screen.queryByText(/An unexpected error occurred during checkout/i)
).not.toBeInTheDocument()
Expand Down
Loading