Просмотр исходного кода

Merge branch 'master' into clear-cg

shish 2 месяцев назад
Родитель
Сommit
e8737743d4

+ 7 - 5
app-ghs/controllers/DeliveryController.php

@@ -304,9 +304,9 @@ class DeliveryController extends BaseController
         $form->loadAndValidate();
 
         $post = $form->getAttributes();
-        $orderId = $post['orderId'] ?? 0; // xhGhsOrder.id
-        $params = $post['allParams'] ?? [];
-        $platform = $params['en_name'] ?? '';// 平台英文名,$post['platform'] 是中文
+        $orderId = $post['orderId']; // xhGhsOrder.id
+        $params = $post['allParams'];
+        $platform = $params['en_name'];// 平台英文名,$post['platform'] 是中文
         if (empty($platform)) {
             util::fail('请选择跑腿');
         }
@@ -316,7 +316,9 @@ class DeliveryController extends BaseController
         $params['ip'] = Yii::$app->request->userIP;
         $params['weight'] = $post['weight'];
         $params['remark'] = $post['remark'] ?? '';
-        $params['total_price_fen'] = $post['price'];
+        if (empty($params['total_price_fen'])) {
+            $params['total_price_fen'] = $post['price'];
+        }
         $params['pickupTime'] = $pickupTime['value'];
 
         $ghsOrder = OrderClass::getById($orderId, true);
@@ -1012,4 +1014,4 @@ class DeliveryController extends BaseController
             Yii::error('滴滴授权回调保存失败' . $e->getMessage(), 'didi');
         }
     }
-}
+}

+ 2 - 2
biz-ghs/order/classes/OrderClass.php

@@ -3083,8 +3083,8 @@ XL;
                 return false;
             }
         }
-        //东莞我要花 福州福清我要花 手机后四号变星号
-        if (in_array((int)$orderMainId, [25119, 2644,10652,2084,89473], true)) {
+        //东莞我要花 手机后四号变星号
+        if (in_array((int)$orderMainId, [2644,10652,2084,89473], true)) {
             $customMobile = preg_replace('/(.{4})$/u', '****', trim($customMobile));
         }
         $customName = $order->customName ?? '';

+ 1 - 1
common/components/delivery/platform/huolala/Huolala.php

@@ -19,7 +19,7 @@ class Huolala
      */
     protected $notifyUrl;
 
-    const CITY_VEHICLE_CACHE_TTL = 259200; // 3 days
+    const CITY_VEHICLE_CACHE_TTL = 86400; // 1 day
     const CITY_VEHICLE_CACHE_KEY_PATTERN = 'huolala-city-vehicle:%d_%s';
 
     public function __construct($accessToken='')

+ 35 - 8
common/components/delivery/services/DispatchService.php

@@ -426,7 +426,7 @@ class DispatchService
             }
 
             $vehicleStd = [];
-            $vehicleStd[] = count($vehicle['vehicle_std_item']) > 0 ? $vehicle['vehicle_std_item'][0]['name'] : '';
+            $vehicleStd[] = count($vehicle['vehicle_std_item']) > 0 ? $vehicle['vehicle_std_item'][0]['name'] : "";
 
             $vehicleTypeList[$vehicle['order_vehicle_id']] = [
                 'order_vehicle_id' => $vehicle['order_vehicle_id'],
@@ -742,13 +742,13 @@ class DispatchService
                 $adapter = $this->adapters[$adapterKey];
                 
                 // 根据平台类型调用相应的 buildPriceRequest(s) 方法
-                if ($platform === 'huolala') {
-                    // 货拉拉返回多个请求(一个请求对应一个车型)
+                if ($platform === 'huolala') {// 货拉拉返回多个请求(一个请求对应一个车型)
                     $priceRequests = $adapter->buildPriceRequest($data, $orderTime);
                     foreach ($priceRequests as $requestKey => $requestInfo) {
                         $allRequests[$requestKey] = $requestInfo;
                         $platformMapping[$requestKey] = ['platform' => 'huolala', 'data' => $data];
                     }
+
                 } else {
                     // 其他平台各返回一个请求
                     $requestInfo = $adapter->buildPriceRequest($data, $orderTime);
@@ -1000,16 +1000,20 @@ class DispatchService
                             Yii::warning('货拉拉 报价数据 priceInfo 缺少 price_calculate_id:' . json_encode($priceInfo) . ' key: ' . $key);
                         }
                         //车型信息
-                        $vehicleAttr = $priceInfo['vehicle_info']['vehicle_attr'] ?? [];
-                        if(empty($vehicleAttr)){
+                        $vehicleAttr = $priceInfo['vehicle_info']['vehicle_attr'] ?? 0;
+                        if(!isset($priceInfo['vehicle_info']['vehicle_attr'])){
                             Yii::info('货拉拉 报价数据 vehicleAttr 为空:' . ' key: ' . $key); //json_encode($priceInfo) .
                         }
+                        $priceCondition = !empty($priceConditions) ? $priceConditions[0] : [];
+                        $priceInfoData = $priceCondition['price_info'] ?? [];
+                        $huolalaPrice = $priceInfoData['total_price'] ?? 0;
+                        $couponId = $this->extractHuolalaCouponId($priceConditions);
 
                         $deliveryList[] = [
                             'name' => '货拉拉' . ' (' . $vehicleType['_meta']['vehicle_type'] . ')',
                             'vehicle_type' => $vehicleType['_meta']['vehicle_type'],
                             'en_name' => 'huolala',
-                            'price' => !empty($priceConditions) ? $priceConditions[0]['price_info']['total_price'] : 0,
+                            'price' => $huolalaPrice,
                             'distance' => !empty($distanceInfo) ? $distanceInfo['distance_total'] : 0,
                             'type' => $vehicleType['_meta']['vehicle_type'],
                             'isAble' => true,
@@ -1020,11 +1024,18 @@ class DispatchService
                             // 下单需要透传的数据
                             'vehicle_std' => $vehicleType['vehicle_std'],
                             'spec_req' => $vehicleType['spec_req'],
+                            'addr_info' => $vehicleType['addr_info'],
+                            'order_time' => $vehicleType['order_time'],
+                            'invoice_type' => $vehicleType['invoice_type'],
+                            'total_price_fen' => $huolalaPrice,
                             'price_calculate_id' => $priceCalculateId,
-                            'price_item_encryption' => !empty($priceConditions) ? $priceConditions[0]['price_item_encryption'] : '',
+                            'price_item_encryption' => $priceCondition['price_item_encryption'] ?? '',
                             'vehicle_attr' => $vehicleAttr,
-                            'commodity_info' => !empty($priceConditions) ? $priceConditions[0]['commodity_item'] : [],
+                            'commodity_info' => $priceCondition['commodity_item'] ?? [],
                         ];
+                        if (!empty($couponId)) {
+                            $deliveryList[count($deliveryList) - 1]['coupon_id'] = $couponId;
+                        }
                     }
                     break;
                 case 'dada':
@@ -1078,4 +1089,20 @@ class DispatchService
 
         return $deliveryList;
     }
+
+    private function extractHuolalaCouponId($priceConditions)
+    {
+        if (empty($priceConditions) || !is_array($priceConditions)) {
+            return null;
+        }
+
+        foreach ($priceConditions as $condition) {
+            $couponId = $condition['coupon_info']['coupon_id'] ?? null;
+            if (!empty($couponId)) {
+                return (string)$couponId; //转换为字符串,以免在传递过程中丢失精度(包括传递给前端时)
+            }
+        }
+
+        return null;
+    }
 }

+ 54 - 40
common/components/delivery/services/adapter/HuolalaAdapter.php

@@ -61,44 +61,49 @@ class HuolalaAdapter extends Huolala implements Adapter
      */
     public function formatOrderData($order, $orderType, $shop, $params)
     {
-        //用车时间 -- 用车时间(unix时间戳:秒)。 取值范围:(当前时间+10分钟)<=用车时间<=(当前时间+5天)
-        $orderTime = strtotime($params['pickupTime']);
-        if($orderTime < (time()+600) ) {
-            $orderTime = time() + 600;
+        // 下单时必须优先使用估价时透传的时间,避免货拉拉核价失败。
+        $orderTime = $params['order_time'] ?? null;
+        if (empty($orderTime)) {
+            $orderTime = strtotime($params['pickupTime']);
+            if($orderTime < (time()+600) ) {
+                $orderTime = time() + 600;
+            }
         }
         $cityId = $params['city_id'];
+        // 下单时必须优先使用估价时透传的地址
+        $addrInfo = $params['addr_info'] ?? [
+             [
+                 'name' => $shop['merchantName'], //地址名称
+                 'addr' => $shop['fullAddress'],
+                 'lat_lon' => ['lat' => (float)$shop['lat'], 'lon' => (float)$shop['long']],
+                 'city_id' => $cityId,
+                 'city_name' => $shop['city'],
+                 'district_name' =>  $shop['dist'],
+                 'house_number' => $shop['floor'],
+                 'contacts_name' => $shop['mobile'],
+                 'contacts_phone_no' => $shop['mobile'],
+             ],
+             [
+                 'name' => empty($order['showAddress']) ? $order['customName'] : $order['showAddress'],
+                 'addr' => $order['fullAddress'],
+                 'lat_lon' => ['lat' => (float)$order['lat'], 'lon' => (float)$order['long']],
+                 'city_id' => $cityId,
+                 'city_name' => $order['city'],
+                 'district_name' => $order['dist'],
+                 'house_number' => $order['floor'],
+                 'contacts_name' => $order['customName'],
+                 'contacts_phone_no' => Functions::getMobile($order),
+             ]
+        ];
 
         $apiData = [
             'city_id' => $cityId,
             'city_info_revision' => $params['city_info_revision'],
             'order_vehicle_id' => $params['order_vehicle_id'],
-            'addr_info' => [
-                 [
-                     'name' => $shop['merchantName'], //地址名称
-                     'addr' => $shop['fullAddress'],
-                     'lat_lon' => ['lat' => (float)$shop['lat'], 'lon' => (float)$shop['long']],
-                     'city_id' => $cityId,
-                     'city_name' => $shop['city'],
-                     'district_name' =>  $shop['dist'],
-                     'house_number' => $shop['floor'],
-                     'contacts_name' => $shop['mobile'],
-                     'contacts_phone_no' => $shop['mobile'],
-                 ],
-                 [
-                     'name' => empty($order['showAddress']) ? $order['customName'] : $order['showAddress'],
-                     'addr' => $order['fullAddress'],
-                     'lat_lon' => ['lat' => (float)$order['lat'], 'lon' => (float)$order['long']],
-                     'city_id' => $cityId,
-                     'city_name' => $order['city'],
-                     'district_name' => $order['dist'],
-                     'house_number' => $order['floor'],
-                     'contacts_name' => $order['customName'],
-                     'contacts_phone_no' => Functions::getMobile($order),
-                 ]
-            ],
-            'vehicle_std' => $params['vehicle_std'],
+            'addr_info' => $addrInfo,
+            'vehicle_std' => $params['vehicle_std'] ?? [],
             'spec_req' => $params['spec_req'],
-            'order_time' => $orderTime,
+            'order_time' => (int)$orderTime,
             'contact_name' => $shop['merchantName'],
             'contact_phone_no' => $shop['mobile'],
             'out_user_id' => $params['mainId'], //下单人在服务商平台侧的唯一ID,如果没有,可以传0
@@ -109,12 +114,21 @@ class HuolalaAdapter extends Huolala implements Adapter
             'price_calculate_id' => $params['price_calculate_id'], //计价id,从计价接口返回中取值
             'price_item_encryption' => $params['price_item_encryption'], //估价返回的费用项,从计价接口返回中取值
             'vehicle_attr' => $params['vehicle_attr'], //大小车属性,从计价接口返回中取值
-            'commodity_info' => $params['commodity_info'] //商品信息(直接透传u-price-multiCalculate返回的commodity_item字段)
+            'commodity_info' => $params['commodity_info'], //商品信息(直接透传u-price-multiCalculate返回的commodity_item字段)
+            'invoice_type' => $params['invoice_type']
         ];
+        if (isset($params['coupon_id'])) {
+            $apiData['coupon_id'] = $params['coupon_id'];
+        }
 
         // 添加订单消息回调地址
         $apiData['notify_url'] = $this->notifyUrl;
 
+        // 跑腿车型,传这个值会出现"核价失败",所以要去除
+        if ($params['type'] == '跑腿') {
+            unset($apiData['spec_req']);
+        }
+
         return $apiData;
     }
 
@@ -153,24 +167,24 @@ class HuolalaAdapter extends Huolala implements Adapter
         $apiData = [
             'city_id' => (int)($order['city_id'] ?? 0),
             'city_info_revision' => (int)($order['city_info_revision'] ?? 0),
-            'order_vehicle_id' => (int)($order['order_vehicle_id'] ?? 0),
+            'order_vehicle_id' => (int)$order['order_vehicle_id'],
             'addr_info' => $addrInfo,
             'contact_name' => $order['contact_name'] ?? '',
             'contact_phone_no' => $order['contact_phone_no'] ?? '',
-            'total_price_fen' => (int)($order['total_price_fen'] ?? 0),
-            'pay_type' => (int)($order['pay_type'] ?? 0),
+            'total_price_fen' => (int)$order['total_price_fen'],
+            'pay_type' => $order['pay_type'],
             'price_calculate_id' => $order['price_calculate_id'] ?? '',
             'price_item_encryption' => $order['price_item_encryption'] ?? '',
             'vehicle_attr' => (int)($order['vehicle_attr'] ?? 0),
-            'order_time' => (int)($order['order_time'] ?? time()),
+            'order_time' => (int)$order['order_time'],
         ];
 
         // 可选参数
         if (!empty($order['vehicle_std'])) {
             $apiData['vehicle_std'] = is_array($order['vehicle_std']) ? $order['vehicle_std'] : [$order['vehicle_std']];
         }
-        if (!empty($order['spec_req'])) {
-            $apiData['spec_req'] = is_array($order['spec_req']) ? $order['spec_req'] : [$order['spec_req']];
+        if (!empty($order['spec_req']) && is_array($order['spec_req'])) {
+            $apiData['spec_req'] = array_map('intval', $order['spec_req']);
         }
         if (!empty($order['remark'])) {
             $apiData['remark'] = $order['remark'];
@@ -185,7 +199,7 @@ class HuolalaAdapter extends Huolala implements Adapter
             $apiData['notify_url'] = $order['notify_url'];
         }
         if (isset($order['coupon_id'])) {
-            $apiData['coupon_id'] = $order['coupon_id'];
+            $apiData['coupon_id'] = intval($order['coupon_id']);
         }
         if (isset($order['invoice_type'])) {
             $apiData['invoice_type'] = (int)$order['invoice_type'];
@@ -388,7 +402,7 @@ class HuolalaAdapter extends Huolala implements Adapter
             );
             
             // 使用车型ID作为请求的唯一标识
-            $vehicleId = (int)($vehicle['order_vehicle_id'] ?? 0);
+            $vehicleId = $vehicle['order_vehicle_id'];
             $requests["huolala_vehicle_{$vehicleId}"] = [
                 'url' => $this->baseUrl,
                 'data' => $payload,
@@ -495,4 +509,4 @@ class HuolalaAdapter extends Huolala implements Adapter
             'data' => null,
         ];
     }
-}
+}

+ 157 - 16
scripts/test_ghs_stock_concurrency.php

@@ -48,8 +48,7 @@ $defaultProductIds = [27286, 27282];
 
 $commonHeaders = [
     'Connection: keep-alive',
-    'account: 12362',
-    'content-type: application/x-www-form-urlencoded;charset=UTF-8',
+    'content-type: application/json;charset=UTF-8',
     'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0.1 Mobile/21A360 Safari/604.1 wechatdevtools/2.01.2510260 MicroMessenger/8.0.5 Language/zh_CN webview/ hash/1237023557 sid/o7x10OLPfZ',
     'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjBfOTMyIn0.eyJpc3MiOiJodHRwczpcL1wvYXBpLnNob3AuaHpnaGQuY29tIiwiYXVkIjoiaHR0cHM6XC9cL2FwaS5zaG9wLmh6Z2hkLmNvbSIsImp0aSI6IjBfOTMyIiwiaWF0IjoxNzc5Mjc1MzgxLCJuYmYiOjE3NzkyNzUzODEsImV4cCI6MTg3Mzg4MzM4MSwidW5pcXVlSWQiOjkzMiwic291cmNlSWQiOjB9.OVuQTjXMvklAFnYdIIRGVR2D5VK14Dp6QX5bDQAUHyE',
     'appVersion: 2',
@@ -63,8 +62,7 @@ $commonHeaders = [
 
 $hdHeaders = [
     'Connection: keep-alive',
-    'account: 12362',
-    'content-type: application/x-www-form-urlencoded;charset=UTF-8',
+    'content-type: application/json;charset=UTF-8',
     'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1 wechatdevtools/2.01.2510260 MicroMessenger/8.0.5 Language/zh_CN webview/ hash/1009552471 sid/xxB6sCFvvu',
     'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjBfMTM5MSJ9.eyJpc3MiOiJodHRwczpcL1wvYXBpLnNob3AuaHVhbWwuY29tIiwiYXVkIjoiaHR0cHM6XC9cL2FwaS5zaG9wLmh1YW1sLmNvbSIsImp0aSI6IjBfMTM5MSIsImlhdCI6MTc4MDAzNzMxMSwibmJmIjoxNzgwMDM3MzExLCJleHAiOjE4NzQ2NDUzMTEsInVuaXF1ZUlkIjoiMTM5MSIsInNvdXJjZUlkIjowfQ.BgiDsxOd9cktOLsDe8Ud_hcFqzmVwSzKjCVwUdrSXKw',
     'appVersion: 2',
@@ -76,16 +74,126 @@ $hdHeaders = [
     'Accept-Language: zh-CN,zh;q=0.9',
 ];
 
-$orderBody = 'shopId=12986&shopName=&sendType=0&transType=4&sendDate=&sendTimeWant=&historyDate=&sendCost=&customSendCost=0&customId=914&packCost=&product=%5B%7B%22productId%22%3A%2227286%22%2C%22bigNum%22%3A2%2C%22smallNum%22%3A0%2C%22classId%22%3A%223322%22%2C%22itemId%22%3A%221009%22%2C%22price%22%3A%2227.00%22%2C%22remark%22%3A%22%22%7D%2C%7B%22productId%22%3A%2227282%22%2C%22bigNum%22%3A2%2C%22smallNum%22%3A0%2C%22classId%22%3A%223322%22%2C%22itemId%22%3A%221005%22%2C%22price%22%3A%220.30%22%2C%22remark%22%3A%22%22%7D%5D&remark=&payWay=0&needPrint=1&hasPay=2&getStaffId=0&getStaffName=&dealPrice=0&wlName=&reductionRule=0&callErrand=0&weight=1&deliveryRemark=&customName=%E5%BE%AE%E5%8D%87%E6%BC%AB%E8%8A%B1&modifyPrice=54.6&newVersion=1&book=0&xj=%22%22&treeData=%22%22';
-$stockOutBody = 'confirmIn=1&date=2026-05-25&remark=123&inShopId=37043&itemInfo=%5B%7B%22productId%22%3A%2227286%22%2C%22bigNum%22%3A1%2C%22smallNum%22%3A0%2C%22name%22%3A%22%E8%8B%8F%E9%86%92%22%7D%2C%7B%22productId%22%3A%2227282%22%2C%22bigNum%22%3A1%2C%22smallNum%22%3A0%2C%22name%22%3A%22%E6%B4%9B%E7%A5%9E%22%7D%5D&action=confirm';
-$hdPurchaseBody = 'getType=2&sendTimeWant=&sendType=0&transType=4&remark=&product=%5B%7B%22productId%22%3A%2227286%22%2C%22bigNum%22%3A1%2C%22smallNum%22%3A0%2C%22classId%22%3A%223322%22%2C%22itemId%22%3A%221009%22%2C%22weight%22%3A%221.20%22%2C%22name%22%3A%22%E8%8B%8F%E9%86%92%22%7D%5D&ghsId=932&hbId=0&wlName=&xj=%22%22&itemTotalAmount=27&version=10&direct=0';
-$wastageBody = http_build_query([
-    'remark' => 'concurrency-test',
-    'product' => json_encode([
-        ['productId' => '27286', 'bigNum' => 1, 'smallNum' => 0, 'classId' => '3322', 'itemId' => '1009'],
-        ['productId' => '27282', 'bigNum' => 1, 'smallNum' => 0, 'classId' => '3322', 'itemId' => '1005'],
-    ], JSON_UNESCAPED_UNICODE),
-]);
+$orderBody = '{
+  "shopId": "12986",
+  "shopName": "",
+  "sendType": "0",
+  "transType": "4",
+  "sendDate": "",
+  "sendTimeWant": "",
+  "historyDate": "",
+  "sendCost": "",
+  "customSendCost": "0",
+  "customId": "914",
+  "packCost": "",
+  "product": [
+    {
+      "productId": "27286",
+      "bigNum": 2,
+      "smallNum": 0,
+      "classId": "3322",
+      "itemId": "1009",
+      "price": "27.00",
+      "remark": ""
+    },
+    {
+      "productId": "27282",
+      "bigNum": 2,
+      "smallNum": 0,
+      "classId": "3322",
+      "itemId": "1005",
+      "price": "0.30",
+      "remark": ""
+    }
+  ],
+  "remark": "",
+  "payWay": "0",
+  "needPrint": "1",
+  "hasPay": "2",
+  "getStaffId": "0",
+  "getStaffName": "",
+  "dealPrice": "0",
+  "wlName": "",
+  "reductionRule": "0",
+  "callErrand": "0",
+  "weight": "1",
+  "deliveryRemark": "",
+  "customName": "微升漫花",
+  "modifyPrice": "54.6",
+  "newVersion": "1",
+  "book": "0",
+  "xj": "\"\"",
+  "treeData": "\"\""
+}';
+$stockOutBody = '{
+  "confirmIn": "1",
+  "date": "2026-05-25",
+  "remark": "123",
+  "inShopId": "37043",
+  "itemInfo": [
+    {
+      "productId": "27286",
+      "bigNum": 1,
+      "smallNum": 0,
+      "name": "苏醒"
+    },
+    {
+      "productId": "27282",
+      "bigNum": 1,
+      "smallNum": 0,
+      "name": "洛神"
+    }
+  ],
+  "action": "confirm"
+}';
+$hdPurchaseBody = '{
+  "getType": "2",
+  "sendTimeWant": "",
+  "sendType": "0",
+  "transType": "4",
+  "remark": "",
+  "product": [
+    {
+      "productId": "27286",
+      "bigNum": 1,
+      "smallNum": 0,
+      "classId": "3322",
+      "itemId": "1009",
+      "weight": "1.20",
+      "name": "苏醒"
+    }
+  ],
+  "ghsId": "932",
+  "hbId": "0",
+  "wlName": "",
+  "xj": "\"\"",
+  "itemTotalAmount": "27",
+  "version": "10",
+  "direct": "0"
+}';
+$wastageBody = '{
+  "remark": "concurrency-test",
+  "product": [
+    {
+      "productId": "27286",
+      "bigNum": 1,
+      "smallNum": 0,
+      "classId": "3322",
+      "itemId": "1009"
+    },
+    {
+      "productId": "27282",
+      "bigNum": 1,
+      "smallNum": 0,
+      "classId": "3322",
+      "itemId": "1005"
+    }
+  ]
+}';
+$orderBody = jsonBodyToJsonRequestBody($orderBody, ['product', 'xj', 'treeData']);
+$stockOutBody = jsonBodyToJsonRequestBody($stockOutBody, ['itemInfo']);
+$hdPurchaseBody = jsonBodyToJsonRequestBody($hdPurchaseBody, ['product', 'xj']);
+$wastageBody = jsonBodyToJsonRequestBody($wastageBody, ['product']);
 
 $requestDefs = [
     'order' => [
@@ -245,6 +353,27 @@ function normalizeNonNegativeInt($options, $key, $default)
     return $value;
 }
 
+function jsonBodyToJsonRequestBody($jsonBody, $jsonStringFields)
+{
+    $params = json_decode($jsonBody, true);
+    if (!is_array($params)) {
+        throw new RuntimeException('Invalid JSON request body: ' . json_last_error_msg());
+    }
+
+    return encodeJsonRequestBody($params, $jsonStringFields);
+}
+
+function encodeJsonRequestBody($params, $jsonStringFields)
+{
+    foreach ($jsonStringFields as $field) {
+        if (isset($params[$field]) && is_array($params[$field])) {
+            $params[$field] = json_encode($params[$field], JSON_UNESCAPED_UNICODE);
+        }
+    }
+
+    return json_encode($params, JSON_UNESCAPED_UNICODE);
+}
+
 function loadStocks($productIds)
 {
     $rows = Yii::$app->db->createCommand(
@@ -355,9 +484,18 @@ function collectProductIds($requestDefs, $defaultProductIds)
 
 function refreshOrderBodyPrices($orderBody, &$summary)
 {
-    parse_str($orderBody, $params);
+    $isJsonBody = false;
+    $params = json_decode($orderBody, true);
+    if (is_array($params)) {
+        $isJsonBody = true;
+    } else {
+        parse_str($orderBody, $params);
+    }
+
     $customId = isset($params['customId']) ? intval($params['customId']) : 0;
-    $products = isset($params['product']) ? json_decode($params['product'], true) : [];
+    $products = isset($params['product']) && is_array($params['product'])
+        ? $params['product']
+        : (isset($params['product']) ? json_decode($params['product'], true) : []);
     if (empty($customId) || empty($products) || !is_array($products)) {
         $summary = 'keep original prices';
         return $orderBody;
@@ -416,6 +554,9 @@ function refreshOrderBodyPrices($orderBody, &$summary)
     $params['product'] = json_encode($products, JSON_UNESCAPED_UNICODE);
     $params['modifyPrice'] = $modifyPrice;
     $summary = implode('; ', $parts) . '; modifyPrice=' . $modifyPrice;
+    if ($isJsonBody) {
+        return encodeJsonRequestBody($params, ['product', 'xj', 'treeData']);
+    }
     return http_build_query($params);
 }