DouYinService.php 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. <?php
  2. namespace app\api\service;
  3. use app\adminapi\logic\external\ExternalConsultationLogic;
  4. use app\api\logic\ServiceOrderLogic;
  5. use app\common\enum\PayEnum;
  6. use app\common\logic\PayNotifyLogic;
  7. use app\common\model\Config;
  8. use app\common\model\external\DouyinOrder;
  9. use app\common\model\external\DouyinRefundOrder;
  10. use app\common\model\external\DouyinUserAuth;
  11. use app\common\model\external\ExternalConsultation;
  12. use app\common\model\external\ExternalConsultationOrder;
  13. use app\common\model\external\ExternalPlatformGoods;
  14. use app\common\model\goods\Goods;
  15. use app\common\model\goods_category\GoodsCategory;
  16. use app\common\model\recharge\RechargeOrder;
  17. use app\common\model\user\User;
  18. use app\common\model\user\UserAuth;
  19. use app\common\model\works\ServiceWork;
  20. use app\common\model\works\ServiceWorkLog;
  21. use app\common\service\ConfigService;
  22. use app\common\service\FileService;
  23. use GuzzleHttp\Client;
  24. use think\facade\Db;
  25. use think\facade\Log;
  26. class DouYinService
  27. {
  28. protected static int $terminal = \app\common\enum\user\UserTerminalEnum::DOUYIN;
  29. protected static int $external_platform_id = 6;
  30. protected CONST EXTERNAL_PLATFORM_ID = 6;
  31. // ********************************* 注册登录
  32. public static function register(array $params)
  33. {
  34. $userSn = User::createUserSn();
  35. $params['password'] = !empty($params['password'])?$params['password']:rand(100000,999999);
  36. $passwordSalt = \think\facade\Config::get('project.unique_identification');
  37. $password = create_password($params['password'], $passwordSalt);
  38. $avatar = ConfigService::get('default_image', 'user_avatar');
  39. $user = User::create([
  40. 'sn' => $userSn,
  41. 'avatar' => $avatar,
  42. 'nickname' => '用户' . $userSn,
  43. 'account' => $params['account'],
  44. 'mobile' => !empty($params['mobile'])?$params['mobile']:'',
  45. 'password' => $password,
  46. 'channel' => self::$terminal,
  47. 'user_type' => $params['user_type']??0,
  48. ]);
  49. return $user;
  50. }
  51. public static function phoneLogin(array $params)
  52. {
  53. try {
  54. $where = ['mobile' => $params['mobile']];
  55. $params['account'] = $params['mobile'];
  56. $user = User::where($where)->findOrEmpty();
  57. if ($user->isEmpty()) {
  58. //直接注册用户
  59. $params['channel'] = self::$terminal;
  60. $user = self::register($params);
  61. }
  62. //更新登录信息
  63. $user->login_time = time();
  64. $user->login_ip = request()->ip();
  65. $user->save();
  66. $userInfo = UserTokenService::setToken($user->id, self::$terminal);
  67. //返回登录信息
  68. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  69. $avatar = FileService::getFileUrl($avatar);
  70. return [
  71. 'nickname' => $userInfo['nickname'],
  72. 'sn' => $userInfo['sn'],
  73. 'mobile' => $userInfo['mobile'],
  74. 'avatar' => $avatar,
  75. 'token' => $userInfo['token'],
  76. ];
  77. } catch (\Exception $e) {
  78. throw new \Exception($e->getMessage());
  79. }
  80. }
  81. public static function getDouyinUserByOpenId(array $openId)
  82. {
  83. try {
  84. $user = DouyinUserAuth::where('openid',$openId)->findOrEmpty();
  85. if ($user->isEmpty()) {
  86. //直接注册用户
  87. $params['channel'] = self::$terminal;
  88. $user = self::register($params);
  89. }
  90. //更新登录信息
  91. $user->login_time = time();
  92. $user->login_ip = request()->ip();
  93. $user->save();
  94. $userInfo = UserTokenService::setToken($user->id, self::$terminal);
  95. //返回登录信息
  96. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  97. $avatar = FileService::getFileUrl($avatar);
  98. return [
  99. 'nickname' => $userInfo['nickname'],
  100. 'sn' => $userInfo['sn'],
  101. 'mobile' => $userInfo['mobile'],
  102. 'avatar' => $avatar,
  103. 'token' => $userInfo['token'],
  104. ];
  105. } catch (\Exception $e) {
  106. throw new \Exception($e->getMessage());
  107. }
  108. }
  109. // **************************** 商品管理 goods_category_id goods_id external_platform_id
  110. public static function addProduct($params)
  111. {
  112. $send_url = env('internal_api.api_url_host').'platf/dou_yin/addGoods';
  113. $res = http_request($send_url,http_build_query($params));
  114. Log::info('addProduct:'
  115. .'url:'.$send_url
  116. .'|data:'.json_encode($params,JSON_UNESCAPED_UNICODE)
  117. .'|res:'.json_encode([$res],JSON_UNESCAPED_UNICODE)
  118. );
  119. return $res?:[];
  120. }
  121. // ******************************** 订单业务
  122. public static function getOrderDetail($params)
  123. {
  124. //抖音订单信息/商品信息/预约信息(地址、时间、履约状态与信息)
  125. // $params['order_number'] user_id
  126. $order = DouyinOrder::with(['goods','serviceWork','douyinRefundOrder'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
  127. if($order->isEmpty()){
  128. return [];
  129. }
  130. $orderInfo = $order->toArray();
  131. empty($orderInfo['goods']) && $orderInfo['goods'] = [];
  132. empty($orderInfo['serviceWork']) && $orderInfo['serviceWork'] = [];
  133. if($orderInfo['serviceWork'] && $orderInfo['serviceWork']['service_status'] == 3 && in_array($orderInfo['order_status'],[1,2])){
  134. $order->order_status = 4;
  135. $order->pay_status = 1;
  136. $order->save();
  137. }
  138. empty($orderInfo['douyinRefundOrder']) && $orderInfo['douyinRefundOrder'] = [];
  139. $orderInfo['book_info'] = json_decode($orderInfo['book_info']?:'{}',true);
  140. $rechargeOrder = RechargeOrder::where(['work_id'=>$orderInfo['work_id']?:0,'payment_type'=>2])->findOrEmpty();
  141. if($rechargeOrder->isEmpty()){
  142. $orderInfo['tail_order'] = [];
  143. }else{
  144. $orderInfo['tail_order'] = $rechargeOrder->toArray();
  145. // 尾款未支付时 展示尾单信息加入临时总金额用于展示
  146. if(isset($orderInfo['tail_order']['pay_status']) && $orderInfo['tail_order']['pay_status'] == 0){
  147. $orderInfo['total_amount'] += $orderInfo['tail_order']['order_amount'];
  148. }
  149. }
  150. $work_status = $orderInfo['serviceWork']['work_status']??0;
  151. $performance = [];
  152. // tmp
  153. switch ($work_status){
  154. case 0:
  155. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  156. break;
  157. case 1:
  158. case 2:
  159. case 3:
  160. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  161. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  162. break;
  163. case 4:
  164. case 5:
  165. case 6:
  166. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  167. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  168. $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
  169. break;
  170. case 7:
  171. case 8:
  172. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  173. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  174. $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
  175. $performance[] = ['status' => '已完结','title' => '已完结','time' => date('Y-m-d H:i:s',time())];
  176. break;
  177. }
  178. $orderInfo['performance'] = $performance;
  179. return $orderInfo;
  180. }
  181. public static function createOrder($params)
  182. {
  183. try {
  184. $goods_id = $params['goods_id']??0;
  185. $open_id = '_000o7ntqTR--_hCTBOBCSR_NJkyp_hiqlEK';
  186. $goods = Goods::where('id',$goods_id)->findOrEmpty();
  187. if($goods->isEmpty()){
  188. throw new \Exception('商品不存在!');
  189. }
  190. $goods = $goods->toArray();
  191. $platformGoods = ExternalPlatformGoods::where('goods_id', $goods_id)->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  192. if($platformGoods->isEmpty()){
  193. throw new \Exception('外部商品不存在!');
  194. }
  195. $platformGoods = $platformGoods->toArray();
  196. $quantity = $params['quantity']?:1;
  197. $appointment_time = strtotime($params['appointment_time']??date('Y-m-d H:i:s',time()));
  198. $bookStartTime = $appointment_time * 1000;
  199. $bookEndTime = ($appointment_time + (2 * 86400)) * 1000;
  200. $order_number = self::submitOrder([
  201. 'open_id'=>$open_id,
  202. 'goods_id'=>$goods_id,
  203. 'user_id'=>$params['user_id']??0,
  204. 'mobile'=>$params['user_info']['mobile']??'',
  205. 'quantity'=>$quantity
  206. ]);
  207. $data = [
  208. "total_amount" => $platformGoods['service_fee'] * $quantity * 100,
  209. "open_id" => $open_id,
  210. "out_order_no" => $order_number,
  211. "order_entry_schema" => ['path'=>'pages/order/detail','params'=>json_encode(['order_number'=>$order_number??0])],
  212. "cp_extra" => json_encode([
  213. "outShopId" => self::EXTERNAL_PLATFORM_ID,
  214. "skuId" => (string)$platformGoods['external_goods_sn'],
  215. "quantity" => $quantity,
  216. "user_id" => $params['user_id'],
  217. "user_address" => $params['user_address'],
  218. "lon" => $params['lon'],
  219. "lat" => $params['lat'],
  220. "appointment_time" => $params['appointment_time']
  221. ]),
  222. "goods_list" => [
  223. [
  224. "goods_id_type" => 1,
  225. "goods_id" => (string)$platformGoods['external_goods_sn'],
  226. "quantity" => 1
  227. ]
  228. ],
  229. "goods_book_info" => [
  230. "book_type" => 2,
  231. "cancel_policy" => 3,
  232. "cancel_advance_hour" => 5
  233. ],
  234. ];
  235. // 服务器向抖音发起创建订单
  236. $url = 'api/apps/trade/v2/order/create_order';
  237. $resData = self::toDyRequestUrl($url,$data);
  238. return $resData;
  239. } catch (\Exception $e) {
  240. throw new \Exception($e->getMessage());
  241. }
  242. }
  243. // 预下单接口 - 前端 首次/尾款
  244. public static function getPluginCreateOrderData($goods_id, $quantity, $douyinOrderId,$params)
  245. {
  246. try {
  247. $goods = Goods::where('id',$goods_id)->findOrEmpty();
  248. if($goods->isEmpty()){
  249. throw new \Exception('商品不存在!');
  250. }
  251. $goods = $goods->toArray();
  252. $platformGoods = ExternalPlatformGoods::where('goods_id', $goods_id)->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  253. if($platformGoods->isEmpty()){
  254. throw new \Exception('外部商品不存在!');
  255. }
  256. $platformGoods = $platformGoods->toArray();
  257. $quantity = $quantity?:1;
  258. $appointment_time = strtotime($params['appointment_time']??date('Y-m-d H:i:s',time()));
  259. $bookStartTime = $appointment_time * 1000;
  260. $bookEndTime = ($appointment_time + (2 * 86400)) * 1000;
  261. $data = [
  262. /*"goodsList" => [
  263. "quantity" => $quantity,
  264. "price" => $platformGoods['service_fee'] * 100,
  265. "goodsName" => $goods['goods_name'],
  266. "goodsPhoto" => $goods['goods_image']??'',
  267. "goodsId" => '',
  268. "goodsType" => 1
  269. ],*/
  270. "skuList" => [
  271. [
  272. "quantity" => (int)$quantity,
  273. "skuId" => (string)$platformGoods['external_goods_sn'],
  274. "skuType" => 1, // 1:商品库商品 2:非商品库商品(融合预约品走加价时,固定传2)
  275. "price" => $platformGoods['service_fee'] * 100,
  276. ]
  277. ],
  278. "bookInfo" => [
  279. "itemBookInfoList"=>[
  280. [
  281. "poiId" => '7511543640776017961',
  282. "shopName" => '亿蜂快修·武汉市',
  283. "outShopId" => (string)self::EXTERNAL_PLATFORM_ID,
  284. "skuId" => (string)$platformGoods['external_goods_sn'],
  285. "bookStartTime" => $bookStartTime?:'',
  286. "bookEndTime" => $bookEndTime?:'',
  287. ]
  288. ]
  289. ],
  290. "payment" => [
  291. "totalAmount" => $quantity * $platformGoods['service_fee'] * 100,
  292. ],
  293. //"callbackUrl" => $callbackUrl?:'',
  294. "callbackData" => [
  295. "outShopId" => self::EXTERNAL_PLATFORM_ID,
  296. "skuId" => (string)$platformGoods['external_goods_sn'],
  297. "quantity" => $quantity,
  298. "user_id" => $params['user_id'],
  299. "douyinOrderId" => (string)$douyinOrderId?:'',
  300. "user_name" => $params['user_name']??'',
  301. "mobile" => $params['mobile']??'',
  302. "user_address" => $params['user_address']??'',
  303. "lon" => $params['lon']??'',
  304. "lat" => $params['lat']??'',
  305. "appointment_time" => $params['appointment_time']??''
  306. ],
  307. /*"tradeOption" => json_encode([
  308. "life_trade_flag" => 1,
  309. "order_relation_info" => [
  310. "related_order_id" => 0, // 加价时上个订单号
  311. "relation_type" => 'multi_buy_as_one'
  312. ]
  313. ])*/
  314. ];
  315. if($douyinOrderId){ // 说明是来自首单订单即要创建尾款
  316. unset($data['skuList']);
  317. unset($data['bookInfo']);
  318. unset($data['payment']);
  319. $douyinOrder = DouyinOrder::where(['user_id'=>$params['user_id'],'dy_order_id'=>$douyinOrderId])->findOrEmpty();
  320. $rechargeOrder = RechargeOrder::where(['work_id'=>$douyinOrder->work_id,'payment_type'=>2])->findOrEmpty();
  321. if(empty($rechargeOrder->order_amount)) throw new \Exception('尾款报价不存在!');
  322. $data['skuList'] = [
  323. [
  324. "quantity" => (int)$quantity,
  325. "skuId" => (string)$goods_id,
  326. "skuType" => 2, // 1:商品库商品 2:非商品库商品(融合预约品走加价时,固定传2)
  327. "price" => $rechargeOrder->order_amount * 100,
  328. "goodsInfo" => [
  329. "goodsName"=>$goods['goods_name'],
  330. "goodsPhoto"=>$goods['goods_image'], // 商品图片链接 必填
  331. "goodsId"=> (string)$goods_id,
  332. "goodsType"=>2
  333. ],
  334. "extraInfo" =>["feeType"=>14]
  335. ]
  336. ];
  337. $data['payment'] = [
  338. "totalAmount" => $rechargeOrder->order_amount * 100,
  339. ];
  340. //$data['callbackUrl'] = env('douyin.pay_tail_notify_url')??'';
  341. $bookInfo = json_decode($douyinOrder->book_info,true);
  342. $item_order_id = $bookInfo['item_order_info_list'][0]['item_order_id']??$douyinOrderId;
  343. $data['tradeOption'] = [
  344. "life_trade_flag" => 1,
  345. "trade_mode" => '3',
  346. "order_relation_info" => [
  347. "related_order_id" => (string)$item_order_id, // 加价时上个订单号
  348. "relation_type" => 'multi_buy_as_one'
  349. ]
  350. ];
  351. }
  352. /*else{ // 创建首单订单
  353. $data['callbackUrl'] = '';
  354. }*/
  355. return $data;
  356. } catch (\Exception $e) {
  357. throw new \Exception($e->getMessage());
  358. }
  359. }
  360. // ++++++++++++++++++++ 首次/尾款 扩展点
  361. /**
  362. * 预下单扩展点
  363. * @param $params
  364. * @throws \Exception
  365. * @author liugc <466014217@qq.com>
  366. * @date 2025/6/4 14:03
  367. */
  368. public static function submitOrderNotify($params = [])
  369. {
  370. try {
  371. $params['external_platform_id'] = self::EXTERNAL_PLATFORM_ID;
  372. // order_id goods total_amount discount cp_extra create_order_time phone_num contact_name open_id
  373. $params['cp_extra'] = json_decode($params['cp_extra'], true);
  374. $user_id = $params['cp_extra']['user_id'];
  375. $user = User::where('id',$user_id)->findOrEmpty();
  376. if($params['cp_extra']['douyinOrderId']>0){ // 说明是尾款单
  377. // 创建尾款单
  378. $order_number = self::tailOrder([
  379. 'order_id'=> $params['order_id'],
  380. 'douyinOrderId'=>$params['cp_extra']['douyinOrderId'],
  381. 'user_id'=>$user_id??0,
  382. 'total_amount'=>$params['total_amount'],
  383. 'discount_amount'=>$params['discount_amount']??0,
  384. ]);
  385. $path_order_number = DouyinOrder::where(['dy_order_id'=>$params['cp_extra']['douyinOrderId']])->value('order_number')??0;
  386. $payNotifyUrl = 'https://weixiudev.kyjlkj.com/api/dou_yin/payTailNotify';
  387. }else{
  388. // 创建首单 goods_id user_info.mobile user_id quantity
  389. $params['cp_extra']['open_book_info'] = $params['open_book_info']??[];
  390. $params['cp_extra']['item_order_info_list'] = $params['item_order_info_list']??[];
  391. $order_number = self::submitOrder([
  392. 'open_id'=>$params['open_id'],
  393. 'order_id'=>$params['order_id'], // 抖音订单号
  394. 'goods_id'=>$params['cp_extra']['skuId'],
  395. 'user_id'=>$user_id??0,
  396. 'mobile'=>$user['mobile']??'',
  397. 'quantity'=>$params['cp_extra']['quantity'],
  398. 'book_info'=>$params['cp_extra'],
  399. ]);
  400. $path_order_number = $order_number;
  401. $payNotifyUrl = 'https://weixiudev.kyjlkj.com/api/dou_yin/payNotify';
  402. }
  403. return [
  404. "out_order_no" => $order_number,
  405. "order_entry_schema" => [
  406. "path" => "pages/order/detail",
  407. "params" => json_encode(['order_number' => $path_order_number])
  408. ],
  409. "pay_notify_url" => $payNotifyUrl
  410. ];
  411. } catch (\Exception $e) {
  412. throw new \Exception($e->getMessage());
  413. }
  414. }
  415. public static function refundOrderNotify($params = [])
  416. {
  417. try {
  418. // 抖音单取消
  419. $douyinOrder = DouyinOrder::where(['order_number'=>$params['out_order_no']??0])->findOrEmpty();
  420. if(!$douyinOrder->isEmpty()){
  421. $douyinOrder->status = 4;
  422. $douyinOrder->save();
  423. // 有工单则工单取消
  424. if($douyinOrder->work_id){
  425. // 工单信息
  426. $service_work = ServiceWork::where('id',$douyinOrder->work_id)->findOrEmpty();
  427. if($service_work->isEmpty()) return true;
  428. // 取消工单
  429. if($service_work->work_status < 7){
  430. //更新工单状态为已取消并退款
  431. $service_work->service_status = 4;
  432. $service_work->work_status = 9;
  433. $service_work->user_confirm_status = 5;
  434. $service_work->save();
  435. ServiceWorkLog::create([
  436. 'work_id' => $service_work->id,
  437. 'master_worker_id' => $service_work->master_worker_id,
  438. 'opera_log' => "工单:{$service_work->work_sn} 取消并终止结束服务"
  439. ]);
  440. }
  441. }
  442. }
  443. return [
  444. "out_order_no" => $params['out_order_no'],
  445. "order_entry_schema" => [
  446. "path" => "pages/order/detail",
  447. "params" => json_encode(['order_number' => $params['out_order_no']])
  448. ]
  449. ];
  450. } catch (\Exception $e) {
  451. throw new \Exception($e->getMessage());
  452. }
  453. }
  454. // ++++++++++++++++++++ 首次创单
  455. /**
  456. * 预下单扩展点-子
  457. * @param array $params goods_id user_info.mobile user_id quantity
  458. * @return array|false
  459. */
  460. public static function submitOrder($params)
  461. {
  462. Db::startTrans();
  463. try {
  464. $platformGoods = ExternalPlatformGoods::where('external_goods_sn', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  465. if($platformGoods->isEmpty()){
  466. throw new \Exception('产品不存在!');
  467. }
  468. $goods = Goods::findOrEmpty($platformGoods['goods_id']);
  469. if($goods->isEmpty()){
  470. throw new \Exception('产品不存在!');
  471. }
  472. if(empty($params['mobile'])){
  473. throw new \Exception('请先补充您的联系方式后在提交订单');
  474. }
  475. // TODO tmp防抖1m
  476. $isExist = DouyinOrder::where(['user_id'=>$params['user_id'],'goods_id'=>$goods['id']])->where('create_time','>',(time() - 60))->findOrEmpty();
  477. if(!$isExist->isEmpty()){
  478. //throw new \Exception('请勿重复下单!');
  479. }
  480. $quantity = $params['quantity']??1;
  481. //生成订单
  482. $create_data = [
  483. 'user_id' => $params['user_id'],
  484. 'mobile' => $params['mobile'],
  485. 'open_id' => $params['open_id'],
  486. 'goods_id'=>$goods['id'],
  487. 'title' => $goods['goods_name'],
  488. 'book_info' => json_encode($params['book_info']??[]),
  489. 'unit_price' => $platformGoods['service_fee'],
  490. 'quantity' => $quantity,
  491. 'total_amount' => $platformGoods['service_fee'] * $quantity,
  492. 'dy_order_id' => $params['order_id']??'',
  493. 'order_number' => generate_sn(DouyinOrder::class, 'order_number'),
  494. ];
  495. $order = DouyinOrder::create($create_data);
  496. Db::commit();
  497. return $create_data['order_number'];
  498. } catch (\Exception $e) {
  499. Db::rollback();
  500. throw new \Exception($e->getMessage());
  501. }
  502. }
  503. // 支付成功回调
  504. public static function payNotify($params)
  505. {
  506. Log::write(json_encode($params));
  507. $transaction_id = $params['order_id']??'';
  508. $rechargeOrder = RechargeOrder::where(['transaction_id'=>$transaction_id,'payment_type'=>2])->findOrEmpty();
  509. if(!$rechargeOrder->isEmpty()){
  510. return self::payTailNotify($params);
  511. }
  512. // 查询抖音订单是否完成支付
  513. if ($params['status'] === 'SUCCESS') {
  514. $params['cp_extra'] = json_decode($params['cp_extra'], true);
  515. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  516. $out_order_no = $params['out_order_no'];
  517. $pay_time = time();
  518. $order = DouyinOrder::where('order_number', $out_order_no)->findOrEmpty();
  519. if(!$order->isEmpty() && empty($order->consultation_id)){
  520. // 更新充值订单状态
  521. $order->transaction_id = $transaction_id;
  522. $order->order_status = 2;
  523. $order->pay_time = $pay_time;
  524. $order->paid_amount = $paid_amount;
  525. $user = User::where('id',$order->user_id)->findOrEmpty()->toArray();
  526. $form_detail = [
  527. 'user_name' => $user['real_name']??'',
  528. 'mobile' => $user['mobile'],
  529. 'transaction_id' => $transaction_id,
  530. 'out_trade_no' => $out_order_no,
  531. 'paid_amount' => $paid_amount,
  532. 'params' => $params,
  533. ];
  534. $consultation = ExternalConsultation::create([
  535. 'external_platform_id' => self::EXTERNAL_PLATFORM_ID,
  536. 'form_detail' => json_encode($form_detail),
  537. 'user_name' => $user['real_name']??'',
  538. 'mobile' => $user['mobile'],
  539. 'goods_id' => $order->goods_id,
  540. 'amount' => $paid_amount
  541. ]);
  542. $order->consultation_id = $consultation->id;
  543. $order->save();
  544. //order_number user_address lon lat appointment_time
  545. self::reservation([
  546. 'order_number' => $out_order_no,
  547. 'user_address' => $params['cp_extra']['user_address'],
  548. 'lon' => $params['cp_extra']['lon'],
  549. 'lat' => $params['cp_extra']['lat'],
  550. 'appointment_time' => $params['cp_extra']['appointment_time'],
  551. ]);
  552. return true;
  553. }elseif (!empty($order->consultation_id)){
  554. return true;
  555. }
  556. }elseif ($params['status'] === 'CANCEL' && $params['message'] == 'TIME_OUT'){
  557. // 超时取消支付
  558. Log::info('支付超时取消:order_id'.$params['order_id']."--out_order_no:" .$params['out_order_no']);
  559. return true;
  560. }
  561. return false;
  562. }
  563. // ++++++++++++++++++++ 首次创单 end
  564. // ++++++++++++++++++++ 尾款创单
  565. /**
  566. * 预下单扩展点-子
  567. * @param array $params goods_id user_info.mobile user_id quantity
  568. * @return array|false
  569. */
  570. public static function tailOrder($params)
  571. {
  572. Db::startTrans();
  573. try {
  574. $amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  575. $work_id = DouyinOrder::where(['dy_order_id'=>$params['douyinOrderId']])->value('work_id')??0;
  576. $sn = '';
  577. $rechargeOrder = RechargeOrder::where(['work_id'=>$work_id,'payment_type'=>2])->findOrEmpty();
  578. if($work_id && $rechargeOrder->isEmpty()){
  579. //新增待支付尾款
  580. $sn = generate_sn(RechargeOrder::class, 'sn');
  581. $order_data = [
  582. 'order_type' => 0,
  583. 'sn' => $sn,
  584. 'order_terminal' => 1,
  585. 'work_id' => $work_id,
  586. 'user_id' => $params['user_id'],
  587. 'payment_type' => 2,
  588. 'order_total' => $amount,
  589. 'order_amount' => $amount,
  590. 'pay_status' => 0,
  591. 'paid_amount' => 0,
  592. 'pay_way' => 4
  593. ];
  594. RechargeOrder::create($order_data);
  595. }else{
  596. $sn = $rechargeOrder->sn;
  597. $rechargeOrder->transaction_id = $params['order_id']??'';
  598. $rechargeOrder->save();
  599. }
  600. Db::commit();
  601. return $sn;
  602. } catch (\Exception $e) {
  603. Db::rollback();
  604. throw new \Exception($e->getMessage());
  605. }
  606. }
  607. // 尾款支付成功回调
  608. public static function payTailNotify($params)
  609. {
  610. Log::write(json_encode($params));
  611. // 查询抖音订单是否完成支付
  612. if ($params['status'] === 'SUCCESS') {
  613. $transaction_id = $params['order_id']??'';
  614. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  615. $pay_time = time();
  616. /*$out_order_no = $params['out_order_no'];
  617. $params['cp_extra'] = json_decode($params['cp_extra'], true);
  618. $work_id = DouyinOrder::where(['id'=>$params['cp_extra']['douyinOrderId']])->value('work_id')??0;
  619. $rechargeOrder = RechargeOrder::where(['work_id'=>$work_id,'sn'=>$out_order_no,'payment_type'=>2])->findOrEmpty();*/
  620. $rechargeOrder = RechargeOrder::where(['transaction_id'=>$transaction_id,'payment_type'=>2])->findOrEmpty();
  621. if(!$rechargeOrder->isEmpty()){
  622. // 更新充值订单状态
  623. $rechargeOrder->transaction_id = $transaction_id;
  624. $rechargeOrder->pay_status = 1;
  625. $rechargeOrder->pay_time = $pay_time;
  626. $rechargeOrder->paid_amount = $paid_amount;
  627. $rechargeOrder->save();
  628. // 尾款订单支付成功后续操作 fun
  629. //抖音订单服务完成
  630. $rechargeOrderPaidAmount = \app\common\model\recharge\RechargeOrder::where(['work_id'=>$rechargeOrder->work_id,'pay_status'=>1])->sum('paid_amount');
  631. $order = DouyinOrder::where(['work_id'=>$rechargeOrder->work_id])->findOrEmpty();
  632. $order->order_status = 3;
  633. $order->pay_status = 1;
  634. $order->total_amount = $rechargeOrderPaidAmount;
  635. $order->paid_amount = $rechargeOrderPaidAmount;
  636. $order->save();
  637. //工单完结
  638. self::paymentSuccessful(['sn'=>$rechargeOrder->sn,'pay_way'=>4]);
  639. //抖音履约完成
  640. self::pushDelivery($order->order_number);
  641. return true;
  642. }
  643. }elseif ($params['status'] === 'CANCEL' && $params['message'] == 'TIME_OUT'){
  644. // 超时取消支付
  645. Log::info('支付超时取消:transaction_id'.$params['order_id']."--out_order_no:" .($params['out_order_no']??''));
  646. return true;
  647. }
  648. return false;
  649. }
  650. public static function paymentSuccessful($data = [])
  651. {
  652. try {
  653. $params = $data;
  654. $params['sn'] = mb_substr($params['sn'], 0, 18);
  655. $order = RechargeOrder::where(['sn' => $params['sn']])->findOrEmpty();
  656. if($order->isEmpty()) {
  657. throw new \Exception('内部订单不存在:'.$params['sn'],404);
  658. }
  659. if($order->pay_status == PayEnum::ISPAID) {
  660. return true;
  661. }
  662. if(!empty($params['pay_way']??'')) $params['extra']['pay_way'] = $params['pay_way'];
  663. $payNotifyLogic = PayNotifyLogic::handle('goods', $params['sn'], $params['extra']??[]);
  664. if($payNotifyLogic === true){
  665. // 用户下单后,给订单运营专员(配置固定ID)发送公众号提醒(订单信息)
  666. $order = RechargeOrder::where('sn', $params['sn'])
  667. ->where('payment_type','IN',[0,1])
  668. ->where('pay_status','=',1)
  669. ->findOrEmpty();
  670. if(!$order->isEmpty()){
  671. $workDetail = ServiceWork::findOrEmpty($order->work_id);
  672. if(!$workDetail->isEmpty()){
  673. event('Notice', [
  674. 'scene_id' => 100,
  675. 'params' => [
  676. 'user_id' => 0,
  677. 'order_id' => $workDetail['id'],
  678. 'thing3' => $workDetail['title'],
  679. 'time6' => $workDetail['appointment_time'],
  680. 'phone_number8' => asteriskString($workDetail['mobile']),
  681. 'thing5' => (iconv_strlen($workDetail['address'])>15)?(mb_substr($workDetail['address'],0,15,'UTF-8').'...'):$workDetail['address'],
  682. ]
  683. ]);
  684. }
  685. }
  686. return true;
  687. }
  688. throw new \Exception($payNotifyLogic,404);
  689. }catch(\Exception $e){
  690. throw new \Exception($e->getMessage());
  691. }
  692. }
  693. // 工单不同状态履约 - 发送抖音 同步履约状态即核销
  694. public static function pushDelivery($order_number)
  695. {
  696. try {
  697. // $params['order_number']
  698. $order = DouyinOrder::where('order_number', $order_number)->findOrEmpty();
  699. if($order->isEmpty()){
  700. throw new \Exception('订单不存在');
  701. }
  702. $orderInfo = $order->toArray();
  703. $book_info = json_decode($orderInfo['book_info']?:'{}',true);
  704. $url = 'api/apps/trade/v2/fulfillment/push_delivery/';
  705. $data = [
  706. "book_id" => (string)$book_info['open_book_info']['book_id'],
  707. "use_all" => true,
  708. "out_order_no" => $order_number,
  709. "delivery_status" => 2,
  710. ];
  711. $resData = self::toDyRequestUrl($url,$data);
  712. Log::info('pushDelivery:'.formatLogData($resData));
  713. return true;
  714. } catch (\Exception $e) {
  715. Log::info('pushDelivery-error:'.$e->getMessage());
  716. return false;
  717. }
  718. }
  719. // ++++++++++++++++++++ 尾款创单 end
  720. // ******************************** 订单预约/改约
  721. // 创建预约单
  722. public static function reservation($params)
  723. {
  724. /*$lon_lat = get_address_lat_lng($params['user_address']);
  725. $params['lon'] = $lon_lat['lon'];
  726. $params['lat'] = $lon_lat['lat'];*/
  727. // $params['order_number'] user_address lon lat appointment_time
  728. Db::startTrans();
  729. try {
  730. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  731. if(!$order->isEmpty() && $order->consultation_id && empty($order->work_id)){
  732. $consultation = ExternalConsultation::where('id', $order->consultation_id)->findOrEmpty()->toArray();
  733. $consultation['user_name'] = $params['user_name']??$consultation['user_name'];
  734. $consultation['mobile'] = $params['mobile']??$consultation['mobile'];
  735. $consultation['user_address'] = $params['user_address'];
  736. $consultation['lon'] = $params['lon'];
  737. $consultation['lat'] = $params['lat'];
  738. $consultation['appointment_time'] = $params['appointment_time'];
  739. $consultationOrderId = ExternalConsultationLogic::order($consultation);
  740. if (false === $consultationOrderId) {
  741. throw new \Exception('预约失败');
  742. }
  743. if(!empty($consultationOrderId)){
  744. $consultationOrder = ExternalConsultationOrder::where('id', $consultationOrderId)->findOrEmpty()->toArray();
  745. $work_status = ServiceWork::where('id', $consultationOrder['work_id'])->value('work_status');
  746. $order->work_id = $consultationOrder['work_id'];
  747. $order->fulfillment_status = $work_status;
  748. $order->save();
  749. }
  750. $params['dy_order_id'] = $order->dy_order_id;
  751. $params['consultationOrderId'] = $consultationOrderId;
  752. $params['open_id'] = $order->open_id;
  753. $params['goods_id'] = $order->goods_id;
  754. }elseif ($order->consultation_id && $order->work_id){
  755. $consultationOrderId = ExternalConsultationOrder::where('work_id', $order->work_id)->where('consultation_id', $order->consultation_id)->value('id')??0;
  756. $params['dy_order_id'] = $order->dy_order_id;
  757. $params['consultationOrderId'] = $consultationOrderId;
  758. $params['open_id'] = $order->open_id;
  759. $params['goods_id'] = $order->goods_id;
  760. }else{
  761. throw new \Exception('预约失败');
  762. }
  763. Db::commit();
  764. // 抖音创建预约单
  765. //$url = 'api/apps/trade/v2/book/create_book';
  766. //$resData = self::toDyRequestUrl($url,self::getCreateBookParams($params));
  767. //book_id result
  768. // 抖音预约接单结果回调
  769. $bookInfo = json_decode($order->book_info, true);
  770. if(isset($bookInfo['open_book_info']) && $bookInfo['open_book_info']){
  771. $bookurl = 'api/apps/trade/v2/book/book_result_callback';
  772. $res = self::toDyRequestUrl($bookurl,[
  773. 'book_id' => $bookInfo['open_book_info']['book_id']??$params['dy_order_id'],
  774. 'result' => 1,
  775. ]);
  776. Log::info('book_result_callback:'.formatLogData($res));
  777. }
  778. return $order['id']??0;
  779. } catch (\Exception $e) {
  780. Db::rollback();
  781. throw new \Exception($e->getMessage());
  782. }
  783. }
  784. public static function getCreateBookParams($params)
  785. {
  786. try {
  787. $platformGoods = ExternalPlatformGoods::where('goods_id', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  788. if($platformGoods->isEmpty()){
  789. throw new \Exception('产品不存在!');
  790. }
  791. $appointment_time = strtotime($params['appointment_time']);
  792. $book_start_time = $appointment_time * 1000;
  793. $book_end_time = ($appointment_time + (2 * 86400)) * 1000;
  794. $data = [
  795. "order_id"=> (string)$params['dy_order_id']??'',
  796. "out_book_no"=> (string)$params['consultationOrderId']??'',
  797. "open_id"=> (string)$params['open_id']??'',
  798. "item_book_info_list" => [
  799. [
  800. "poi_id" => '7511543640776017961',
  801. "shop_name" => '亿蜂快修·武汉市',
  802. "ext_shop_id" => (string)self::EXTERNAL_PLATFORM_ID,
  803. "goods_id" => (string)$platformGoods->external_goods_sn,
  804. "book_start_time" => (int)$book_start_time??0,
  805. "book_end_time" => (int)$book_end_time??0,
  806. ]
  807. ]
  808. ];
  809. return $data;
  810. } catch (\Exception $e) {
  811. return [];
  812. }
  813. }
  814. public static function upReservation($params)
  815. {
  816. // $params['order_number']
  817. Db::startTrans();
  818. try {
  819. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  820. if(!$order->isEmpty()){
  821. // sn appointment_time
  822. $result = ServiceOrderLogic::approvalChangeAppointment(['sn'=>RechargeOrder::where('work_id', $order->work_id)->value('sn'),'appointment_time'=>$params['appointment_time']]);
  823. if (false === $result) {
  824. throw new \Exception(ServiceOrderLogic::getError());
  825. }
  826. $order->fulfillment_status = ServiceWork::where('id', $order->work_id)->value('work_status');
  827. $order->save();
  828. }
  829. Db::commit();
  830. return $order['id'];
  831. } catch (\Exception $e) {
  832. Db::rollback();
  833. throw new \Exception($e->getMessage());
  834. }
  835. }
  836. // ******************************** 订单退款
  837. public static function cancelOrder($params)
  838. {
  839. // $params['order_number']
  840. Db::startTrans();
  841. try {
  842. $douyinOrder = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  843. if(!$douyinOrder->isEmpty()){
  844. if($douyinOrder->order_status == 1 && $douyinOrder->pay_status == 0){
  845. $douyinOrder->order_status = 4;
  846. $douyinOrder->save();
  847. // 有工单则工单取消
  848. if($douyinOrder->work_id){
  849. // 工单信息
  850. $service_work = ServiceWork::where('id',$douyinOrder->work_id)->findOrEmpty();
  851. if($service_work->isEmpty()) return true;
  852. // 取消工单
  853. if($service_work->work_status < 7){
  854. //更新工单状态为已取消并退款
  855. $service_work->service_status = 4;
  856. $service_work->work_status = 9;
  857. $service_work->user_confirm_status = 5;
  858. $service_work->save();
  859. ServiceWorkLog::create([
  860. 'work_id' => $service_work->id,
  861. 'master_worker_id' => $service_work->master_worker_id,
  862. 'opera_log' => "工单:{$service_work->work_sn} 取消并终止结束服务"
  863. ]);
  864. }
  865. }
  866. }else{
  867. throw new \Exception('订单状态不可取消!');
  868. }
  869. }
  870. Db::commit();
  871. return $douyinOrder['id'];
  872. } catch (\Exception $e) {
  873. Db::rollback();
  874. throw new \Exception($e->getMessage());
  875. }
  876. }
  877. // 申请退款
  878. public static function refund($params)
  879. {
  880. Db::startTrans();
  881. try {
  882. // $params['order_number'] user_id
  883. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->findOrEmpty();
  884. if($order->isEmpty()){
  885. throw new \Exception('订单不存在');
  886. }
  887. $orderInfo = $order->toArray();
  888. $work_status = $orderInfo['serviceWork']['work_status']??0;
  889. if(3 < $work_status){
  890. //throw new \Exception('该订单禁止退款');
  891. }
  892. DouyinRefundOrder::create([
  893. 'refund_number' => generate_sn(DouyinRefundOrder::class, 'refund_number'),
  894. 'order_number' => $orderInfo['order_number'],
  895. 'transaction_id' => $params['transaction_id']??'',
  896. 'reason' => $params['reason']??'',
  897. 'refund_status' => 0,
  898. 'user_id' => $orderInfo['user_id'],
  899. 'refund_amount' => $orderInfo['paid_amount'],
  900. ]);
  901. Db::commit();
  902. // 默认审核通过
  903. //self::refundExamine(['is_examine_ok'=>'pass','order_number'=>$params['order_number']]);
  904. return true;
  905. } catch (\Exception $e) {
  906. Db::rollback();
  907. throw new \Exception($e->getMessage());
  908. }
  909. }
  910. // 后台退款审核
  911. public static function refundExamine($params)
  912. {
  913. Db::startTrans();
  914. try {
  915. // $params['order_number']
  916. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->findOrEmpty();
  917. if($order->isEmpty()){
  918. throw new \Exception('订单不存在');
  919. }
  920. $orderInfo = $order->toArray();
  921. //$refund_number = $params['refund_number']??'';
  922. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $params['order_number'])->order('id', 'desc')->findOrEmpty();
  923. if($params['is_examine_ok'] === 'pass'){
  924. $douyinRefundOrder->refund_status = 2;
  925. RechargeOrder::where('work_id', $orderInfo['work_id'])->update([
  926. 'pay_status' => 2,
  927. 'pay_time' => 0,
  928. 'paid_amount' => 0,
  929. ]);
  930. ServiceWork::where('id', $orderInfo['work_id'])->update([
  931. 'work_status' => 0,
  932. 'user_confirm_status' => 0,
  933. 'service_status' => 4,
  934. 'work_pay_status' => 0
  935. ]);
  936. }else{
  937. $douyinRefundOrder->refund_status = 1;
  938. }
  939. $douyinRefundOrder->save();
  940. if($params['is_examine_ok'] === 'pass'){
  941. //通过后向抖音申请退款
  942. //self::sendRefundCreate($params['order_number']);
  943. //通过后同意退款审核结果
  944. self::sendRefundMerchant($params['order_number'],$douyinRefundOrder->refund_number);
  945. }else{
  946. //不同意退款审核结果
  947. self::sendRefundMerchant($params['order_number'],$douyinRefundOrder->refund_number,2,$params['reason']??'');
  948. }
  949. Db::commit();
  950. return true;
  951. } catch (\Exception $e) {
  952. Db::rollback();
  953. throw new \Exception($e->getMessage());
  954. }
  955. }
  956. // 后台审核通过 - 发送抖音退款申请
  957. public static function sendRefundCreate($order_number)
  958. {
  959. try {
  960. // $params['order_number']
  961. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $order_number)->findOrEmpty();
  962. if($order->isEmpty()){
  963. throw new \Exception('订单不存在');
  964. }
  965. $orderInfo = $order->toArray();
  966. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $order_number)->order('id', 'desc')->findOrEmpty();
  967. //通过后向抖音申请退款
  968. $url = 'api/trade_basic/v1/developer/refund_create/';
  969. $data = [
  970. "order_id" => $orderInfo['transaction_id'],
  971. "out_refund_no" => $douyinRefundOrder->refund_number,
  972. "cp_extra" => $orderInfo['id'].'|'.$douyinRefundOrder->id,
  973. "order_entry_schema" => [
  974. /*"path" => "page/index/index",
  975. "params" => json_encode(['refund_number'=>$douyinRefundOrder->refund_number])*/
  976. "path" => "pages/order/detail",
  977. "params" => json_encode(['order_number' => $order_number])
  978. ],
  979. "refund_total_amount " => $douyinRefundOrder->refund_amount * 100,
  980. //"notify_url" => config('douyin.refundNotifyUrl'),
  981. "refund_reason" => [
  982. [
  983. "code" => 101,
  984. "text" => "不想要了"
  985. ]
  986. ]
  987. ];
  988. $resData = self::toDyRequestUrl($url,$data);
  989. if(isset($resData['data']) && $resData['data']){
  990. $douyinRefundOrder->transaction_id = $resData['data']['refund_id'];
  991. $douyinRefundOrder->save();
  992. }
  993. return true;
  994. } catch (\Exception $e) {
  995. Log::info($e->getMessage());
  996. return false;
  997. }
  998. }
  999. // 后台审核通过 - 发送抖音 同步退款审核结果
  1000. public static function sendRefundMerchant($order_number,$refund_number,$refund_audit_status = 1,$deny_message='')
  1001. {
  1002. try {
  1003. // $params['order_number']
  1004. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $order_number)->findOrEmpty();
  1005. if($order->isEmpty()){
  1006. throw new \Exception('订单不存在');
  1007. }
  1008. $orderInfo = $order->toArray();
  1009. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $order_number)->where('refund_number', $refund_number)->order('id', 'desc')->findOrEmpty();
  1010. //通过后向抖音申请退款
  1011. $url = 'api/apps/trade/v2/refund/merchant_audit_callback';
  1012. $data = [
  1013. "out_refund_no" => (string)$douyinRefundOrder->refund_number,
  1014. "refund_audit_status" => $refund_audit_status,
  1015. "deny_message" => $deny_message??''
  1016. ];
  1017. $resData = self::toDyRequestUrl($url,$data);
  1018. Log::info('sendRefundMerchant:'.formatLogData($resData));
  1019. /*if(isset($resData['data']) && $resData['data']){
  1020. $douyinRefundOrder->transaction_id = $resData['data']['refund_id'];
  1021. $douyinRefundOrder->save();
  1022. }*/
  1023. return true;
  1024. } catch (\Exception $e) {
  1025. Log::info('sendRefundMerchant-error:'.$e->getMessage());
  1026. throw new \Exception($e->getMessage());
  1027. }
  1028. }
  1029. // 抖音申请退款回调
  1030. public static function refundNotify($params)
  1031. {
  1032. Db::startTrans();
  1033. try {
  1034. // $params['refund_id'] $params['out_order_no']
  1035. // 前端组件申请退款---抖音回调申请退款通知---我方生成退款订单并审核---审核通过通知抖音“同意退款”---完成
  1036. $douyinRefundOrder = DouyinRefundOrder::where('transaction_id', $params['refund_id'])->order('id', 'desc')->findOrEmpty();
  1037. if($douyinRefundOrder->isEmpty()){
  1038. // transaction_id order_number reason
  1039. self::refund(['order_number'=>$params['out_order_no'],'transaction_id'=>$params['refund_id'],'reason'=>is_array($params['refund_reason']??[])?$params['refund_reason'][0]??'':'']);
  1040. $douyinRefundOrder = DouyinRefundOrder::where('transaction_id', $params['refund_id'])->order('id', 'desc')->findOrEmpty();
  1041. }
  1042. if($douyinRefundOrder->refund_status == 0){
  1043. $douyinRefundOrder->transaction_id = $params['refund_id']??'';
  1044. $douyinRefundOrder->save();
  1045. }
  1046. Db::commit();
  1047. return [
  1048. 'out_refund_no'=>$douyinRefundOrder->refund_number,
  1049. 'order_entry_schema'=>[
  1050. "path" => "pages/order/refundDetail",
  1051. "params" => json_encode(['order_number' => $params['out_order_no']])
  1052. ],
  1053. 'notify_url'=> 'https://weixiudev.kyjlkj.com/api/dou_yin/refundPassNotify'
  1054. ];
  1055. } catch (\Exception $e) {
  1056. Db::rollback();
  1057. throw new \Exception($e->getMessage());
  1058. }
  1059. }
  1060. // 抖音退款成功回调
  1061. public static function refundPassNotify($params)
  1062. {
  1063. Db::startTrans();
  1064. try {
  1065. // $params['refund_id'] $params['out_order_no']
  1066. // 前端组件申请退款---抖音回调申请退款通知---我方生成退款订单并审核---审核通过通知抖音“同意退款”---完成
  1067. $douyinRefundOrder = DouyinRefundOrder::where('transaction_id', $params['refund_id'])->order('id', 'desc')->findOrEmpty();
  1068. if($douyinRefundOrder->isEmpty()){
  1069. throw new \Exception('未找到此退款订单');
  1070. }
  1071. //if($douyinRefundOrder->refund_status == 0){
  1072. if($params['status'] === 'SUCCESS'){
  1073. $douyinRefundOrder->refund_status = 3;
  1074. DouyinOrder::where('order_number', $douyinRefundOrder->order_number)->update([
  1075. 'order_status' => 4,
  1076. 'pay_status' => 3,
  1077. ]);
  1078. }elseif($params['status'] === 'FAIL'){
  1079. $douyinRefundOrder->refund_status = 4;
  1080. }else{
  1081. throw new \Exception('退款状态未知');
  1082. }
  1083. $douyinRefundOrder->save();
  1084. //}
  1085. Db::commit();
  1086. return true;
  1087. } catch (\Exception $e) {
  1088. Db::rollback();
  1089. throw new \Exception($e->getMessage());
  1090. }
  1091. }
  1092. public static function toDyRequestUrl($url,$data,$headers = [],$resFunction = 'extraErrorCodeReturn',$isHost = 0)
  1093. {
  1094. $toData = [
  1095. 'url' => $url,
  1096. 'data' => $data,
  1097. 'headers' => $headers,
  1098. 'resFunction' => $resFunction,
  1099. 'isHost' => $isHost
  1100. ];
  1101. $res = commonHttpClient(env('internal_api.api_url_host').'platf/dou_yin/toDyRequestUrl', $toData, 'post', 'json', ['Content-Type' => 'application/json']);
  1102. Log::info(json_encode($res));
  1103. if(isset($res['code']) && $res['code'] === 0){
  1104. Log::info("toDyRequestUrl:".json_encode($res));
  1105. return $res['data'];
  1106. }else{
  1107. Log::info("toDyRequestUrl:".$res['msg']);
  1108. throw new \Exception($res['msg']);
  1109. }
  1110. }
  1111. }