## Affected product and snapshot
- Product: `macrozheng/mall`
- Modules: `mall-admin`, `mall-portal`
- Source-reviewed commit: `dcaa93b3150352e5044708d7211b5bed0af4509f`
- Fee adjustment endpoint: `POST /order/update/moneyInfo`
- Downstream payment endpoints: `GET /alipay/pay` and `GET /alipay/webPay`
- Dynamic verification: local Docker deployment on 2026-09-15, with `mall-admin` on port 8080 and `mall-portal` on port 8085
## Summary
The administrative fee-adjustment endpoint updates `oms_order.freight_amount` and `oms_order.discount_amount` without recalculating `oms_order.pay_amount`.
This behavior was reproduced locally: an administrator changed an unpaid order's `discount_amount` from CNY 0.00 to CNY -500.00. The API returned success and persisted the adjustment, while `pay_amount` remained unchanged at CNY -824.00. A subsequent member order-detail request returned the same values.
Source review confirms that freight adjustments follow the same partial-update path. Although the order's payable calculation explicitly includes freight, changing freight through this endpoint does not update the stored payable.
The reviewed Alipay implementation reads this stored `pay_amount` and uses it as the payment request's `total_amount`. Consequently, a freight increase can be omitted from a subsequently initiated payment.
## Root cause
### 1. Fee adjustments do not update the payable
[[OmsOrderServiceImpl.updateMoneyInfo()](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-admin/src/main/java/com/macro/mall/service/impl/OmsOrderServiceImpl.java#L119-L134)](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-admin/src/main/java/com/macro/mall/service/impl/OmsOrderServiceImpl.java#L119-L134) creates a partial order update:
```java
OmsOrder order = new OmsOrder();
order.setId(moneyInfoParam.getOrderId());
order.setFreightAmount(moneyInfoParam.getFreightAmount());
order.setDiscountAmount(moneyInfoParam.getDiscountAmount());
order.setModifyTime(new Date());
int count = orderMapper.updateByPrimaryKeySelective(order);
```
The method does not load the existing monetary components, calculate the adjusted payable, or call `setPayAmount()`.
The [[selective mapper update](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-mbg/src/main/resources/com/macro/mall/mapper/OmsOrderMapper.xml#L646-L684)](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-mbg/src/main/resources/com/macro/mall/mapper/OmsOrderMapper.xml#L646-L684) updates `pay_amount` only when the supplied `payAmount` is non-null. Therefore, the previous payable is preserved while the submitted fee fields are updated.
### 2. Freight is part of the payable calculation
[[OmsPortalOrderServiceImpl.calcPayAmount()](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java#L548-L555)](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java#L548-L555) calculates:
```text
pay_amount = total_amount + freight_amount
- promotion_amount - coupon_amount - integration_amount
```
Updating freight without recalculating `pay_amount` breaks this relationship.
### 3. Payment initiation consumes the stored payable
[[AlipayServiceImpl.pay()](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java#L55-L65)](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java#L55-L65) and [[webPay()](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java#L153-L163)](https://github.com/macrozheng/mall/blob/dcaa93b3150352e5044708d7211b5bed0af4509f/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java#L153-L163) load the order and use:
```java
BigDecimal totalAmount = order.getPayAmount();
bizContent.put("total_amount", totalAmount);
```
Neither method recalculates the payable from the updated monetary components.
1. Read the unpaid order before adjustment
The recorded test used order ID 95, with status=0.
SELECT id, status, total_amount, pay_amount,
coupon_amount, promotion_amount,
freight_amount, discount_amount
FROM oms_order
WHERE id = 95;
Field | Before adjustment
-- | --
id | 95
status | 0
total_amount | 176.00
coupon_amount | 1000.00
promotion_amount | 0.00
freight_amount | 0.00
discount_amount | 0.00
pay_amount | -824.00
A subsequent payment request:
GET /alipay/pay?outTradeNo=<ORDER_SN>&subject=freight-adjustment HTTP/1.1
Host: localhost:8085
uses the stored CNY 100.00 as total_amount, omitting the CNY 20.00 freight increase. /alipay/webPay uses the same amount source.
The administrator performs an authorized fee adjustment, and the buyer subsequently initiates payment for their order. This scenario requires neither a client-supplied payment amount nor an excessive coupon deduction.
The fee-adjustment API and database behavior were dynamically verified as shown above. The positive-freight example and its Alipay consequence are supported by source review; a completed Alipay sandbox transaction is not part of the recorded test.
Expected behavior
An accepted monetary adjustment should update the payable consistently within the same transaction.