You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
13 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\JsonRpc;
  3. use App\Commons\Log;
  4. use App\Constants\v3\ErrorCode;
  5. use App\Constants\v3\LogLabel;
  6. use App\Constants\v3\OrderState;
  7. use App\Exception\ErrorCodeException;
  8. use App\Model\v3\Order;
  9. use App\Model\v3\OrderGoods;
  10. use App\Model\v3\OrderMain;
  11. use App\Service\v3\Interfaces\FinancialRecordServiceInterface;
  12. use App\Service\v3\Interfaces\OrderOnlineServiceInterface;
  13. use App\Service\v3\Interfaces\SeparateAccountsServiceInterface;
  14. use EasyWeChat\Factory;
  15. use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
  16. use Hyperf\DbConnection\Db;
  17. use Hyperf\Guzzle\CoroutineHandler;
  18. use Hyperf\RpcServer\Annotation\RpcService;
  19. use Hyperf\Di\Annotation\Inject;
  20. /**
  21. * @RpcService(name="OrdersService", protocol="jsonrpc-http", server="jsonrpc-http", publishTo="")
  22. */
  23. class OrdersService implements OrdersServiceInterface
  24. {
  25. /**
  26. * @Inject
  27. * @var Log
  28. */
  29. protected $log;
  30. /**
  31. * @Inject
  32. * @var OrderOnlineServiceInterface
  33. */
  34. protected $orderOnlineService;
  35. /**
  36. * @Inject
  37. * @var SeparateAccountsServiceInterface
  38. */
  39. protected $separateAccountsService;
  40. /**
  41. * @Inject
  42. * @var FinancialRecordServiceInterface
  43. */
  44. protected $financialRecordService;
  45. /**
  46. * @Inject
  47. * @var \App\Service\v3\Interfaces\BadgeServiceInterface
  48. */
  49. protected $badgeService;
  50. /**
  51. * 订单完成
  52. * @param $global_order_id
  53. * @param $user_id
  54. * @return array
  55. */
  56. public function onlineComplete($global_order_id, $user_id)
  57. {
  58. Db::beginTransaction();
  59. try {
  60. $this->orderOnlineService->doComplete($global_order_id, $user_id);
  61. $this->separateAccountsService->orderOnlineCompleted($global_order_id, $user_id);
  62. Db::commit();
  63. return [
  64. "status" => 200,
  65. "code" => 0,
  66. "result" => [],
  67. "message" => '处理成功'
  68. ];
  69. } catch (\Exception $e) {
  70. Db::rollBack();
  71. $this->log->event(LogLabel::ORDER_COMPLETE_LOG, ['jsonrpc_order_service_exception_onlineComplete' => $e->getMessage(), 'params' => json_encode([$global_order_id, $user_id])]);
  72. return [
  73. "status" => 200,
  74. "code" => $e->getCode() ?? ErrorCode::ORDER_COMPLETE_FAIL,
  75. "result" => [],
  76. "message" => $e->getMessage()
  77. ];
  78. }
  79. }
  80. /**
  81. * 线上订单退款,整个订单退,这个是专门用于处理用户的申请退款的同意退款操作
  82. * @param $global_order_id
  83. * @param $user_id
  84. * @return array
  85. */
  86. public function onlineRefund($global_order_id, $user_id)
  87. {
  88. try {
  89. $result = $this->orderOnlineService->doRefund($global_order_id, $user_id);
  90. return [
  91. "status" => 200,
  92. "code" => 0,
  93. "result" => [],
  94. "message" => '处理成功'
  95. ];
  96. } catch (\Exception $e) {
  97. return [
  98. "status" => 200,
  99. "code" => $e->getCode(),
  100. "result" => [],
  101. "message" => $e->getMessage()
  102. ];
  103. }
  104. }
  105. /**
  106. * 线上订单单笔退款,主要用于后台强行操作退单退款
  107. * 支持单商品、单店、整单
  108. * 按比例计算红包进行退款
  109. * 比如:两个子订单和子订单商品,分别是2元,98元,使用了10元优惠券
  110. * 退2元商品时,退款金额为
  111. * 红包 (2/(98+2)*10 = 0.2
  112. * 退款:2-0.2=1.8
  113. * @param $user_id *用户ID
  114. * @param $global_order_id *全局总订单ID
  115. * @param $order_child_id *主订单ID,
  116. * @param $order_goods_id *订单商品ID
  117. * @param $note
  118. * @return array
  119. */
  120. public function onlineSingleRefund($user_id, $note, $global_order_id, $order_child_id='', $order_goods_id='')
  121. {
  122. Db::beginTransaction();
  123. try {
  124. $params = [
  125. 'user_id' => $user_id,
  126. 'note' => $note,
  127. 'global_order_id' => $global_order_id,
  128. 'order_child_id' => $order_child_id,
  129. 'order_goods_id' => $order_goods_id,
  130. ];
  131. if (!$user_id || !$global_order_id || !$note) {
  132. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  133. 'jsonrpc_order_service_exception_onlineSingleRefund' => '参数不对',
  134. 'params' => json_encode($params)
  135. ]);
  136. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  137. }
  138. // 主订单
  139. $orderMain = OrderMain::query()
  140. ->where(['global_order_id' => $global_order_id, 'user_id' => $user_id])
  141. ->whereIn('state', array_merge(OrderState::CAN_REFUND_DIRECT, [OrderState::REFUNDED_DIRECT]))
  142. ->first();
  143. if (is_null($orderMain)) {
  144. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  145. 'jsonrpc_order_service_exception_onlineSingleRefund' => '订单不存在',
  146. 'params' => json_encode($params)
  147. ]);
  148. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  149. }
  150. // 子订单
  151. $orderChild = null;
  152. if ($order_child_id) {
  153. $orderChild = Order::query()->with('store')->where(['order_main_id' => $orderMain->global_order_id, 'id' => $order_child_id])->first();
  154. }
  155. // 订单商品
  156. $orderGoods = null;
  157. if ($order_goods_id) {
  158. if (!$order_child_id || is_null($orderChild)) {
  159. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  160. 'jsonrpc_order_service_exception_onlineSingleRefund' => '子订单参数异常[单品]',
  161. 'params' => json_encode($params)
  162. ]);
  163. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  164. }
  165. $orderGoods = OrderGoods::query()
  166. ->where(['order_id' => $orderChild->id, 'id' => $order_goods_id, 'status' => 1])
  167. ->first();
  168. if (is_null($orderGoods)) {
  169. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  170. 'jsonrpc_order_service_exception_onlineSingleRefund' => '订单商品参数异常[单品]',
  171. 'params' => json_encode($params)
  172. ]);
  173. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  174. }
  175. }
  176. $totalAmount = $orderMain->total_money; // 订单可退款金额,总订单金额不含配送费和服务费
  177. $preRefundAmount = 0; // 预退款金额
  178. $refundAmount = 0; // 实际退款金额
  179. $refundType = 'main'; // 退款类型, Main整单 Sub子单 Goods单品
  180. if ($orderGoods) { // 1. 如果订单商品存在则说明要退单品
  181. $preRefundAmount = bcmul($orderGoods->price, $orderGoods->number, 2);
  182. $refundType = 'goods';
  183. } elseif ($orderChild) { // 2. 否则如果存在子订单说明退子订单
  184. $preRefundAmount = $orderChild->money;
  185. $refundType = 'sub';
  186. } else { // 3. 再则如果存在主订单说明退主订单
  187. $preRefundAmount = $orderMain->total_money;
  188. $refundType = 'main';
  189. }
  190. // 占订单金额的比例
  191. $rate = bcdiv($preRefundAmount, $totalAmount, 6);
  192. // 计算优惠券所占金额
  193. $couponMoney = bcmul($orderMain->coupon_money, $rate, 6);
  194. // 计算本次退款实际退款金额
  195. $refundAmount = bcsub($preRefundAmount, $couponMoney, 2);
  196. if ($refundAmount <= 0) {
  197. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  198. 'jsonrpc_order_service_exception_onlineSingleRefund' => '没有可退款金额[实际退款金额]',
  199. 'params' => json_encode($params)
  200. ]);
  201. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  202. }
  203. // 开始退款
  204. $config = config('wxpay');
  205. $app = Factory::payment($config);
  206. $app['guzzle_handler'] = CoroutineHandler::class;
  207. $result = $app->refund->byOutTradeNumber(
  208. $orderMain->global_order_id,
  209. $orderMain->global_order_id.$order_child_id.$order_goods_id,
  210. bcmul($orderMain->money, 100, 0),
  211. bcmul($refundAmount, 100, 0),
  212. [
  213. 'refund_desc' => '订单协商退款[' . $refundType . ']',
  214. 'notify_url' => config('wechat.notify_url.refund_single'),
  215. ]
  216. );
  217. if ($result['return_code'] == 'FAIL') {
  218. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  219. 'jsonrpc_order_service_exception_onlineSingleRefund' => $result['return_msg'] . '[微信退款失败]',
  220. 'params' => json_encode($result)
  221. ]);
  222. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  223. }
  224. if ($result['result_code'] == 'FAIL') {
  225. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  226. 'jsonrpc_order_service_exception_onlineSingleRefund' => $result['err_code_des'] . '[微信退款失败]' . $result['err_code'],
  227. 'params' => json_encode($result)
  228. ]);
  229. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  230. }
  231. // 退款申请成功,查询退款状态,此处暂时无法传递和记录单品退的信息,所以直接查询退款状态处理,不做回调
  232. $refundResult = $app->refund->queryByRefundId($result['refund_id']);
  233. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  234. 'jsonrpc_order_service_exception_onlineSingleRefund' => '[微信退款查询]',
  235. 'params' => json_encode($refundResult)
  236. ]);
  237. // 退款成功
  238. if (
  239. !($refundResult['return_code'] == 'SUCCESS'
  240. && isset($refundResult['result_code'])
  241. && $refundResult['result_code'] == 'SUCCESS')
  242. ) {
  243. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  244. }
  245. $currentTime = time();
  246. // 处理订单状态
  247. $orderMain->state = OrderState::REFUNDED_DIRECT;
  248. $orderMain->total_refund_note = $note;
  249. $orderMain->refund_time = $currentTime;
  250. $orderMain->save();
  251. if ($refundType == 'sub') {
  252. $orderChild->status = 3;
  253. $orderChild->refund_note = $note;
  254. $orderChild->refund_time = $currentTime;
  255. $orderChild->save();
  256. } elseif ($refundType == 'goods') {
  257. $orderGoods->status = 3;
  258. $orderGoods->refund_note = $note;
  259. $orderGoods->refund_time = $currentTime;
  260. $orderGoods->save();
  261. }
  262. // 处理用户和商户流水
  263. $orderChildren = [];
  264. if ($refundType == 'main') { # 整单退的话还得处理所有子订单的商户
  265. $orderChildren = Order::query()->with('store:user_id')->where(['order_main_id' => $orderMain->global_order_id])->get();
  266. foreach ($orderChildren as $key => &$order) {
  267. // 占订单金额的比例
  268. $rate = bcdiv($order->money, $totalAmount, 6);
  269. // 计算优惠券所占金额
  270. $couponMoney = bcmul($orderMain->coupon_money, $rate, 6);
  271. // 计算本次退款实际退款金额
  272. $refundStoreAmount = bcsub($order->money, $couponMoney, 2);
  273. $this->financialRecordService->storeRefundDirect($order->store->user_id, $order->id, $refundStoreAmount);
  274. }
  275. } elseif ($refundType == 'sub'||$refundType == 'goods') { # 退子订单或者退单品的话,商户只有一个
  276. $orderChildren = Order::query()->with('store:user_id')->where(['id' => $order_child_id])->get();
  277. $this->financialRecordService->storeRefundDirect($orderChild->store->user_id, $orderChild->id, $refundAmount);
  278. }
  279. $this->financialRecordService->userRefundDirect($orderMain->user_id, $orderMain->global_order_id, $refundAmount);
  280. Db::commit();
  281. // 记录badge
  282. $orderChildIds = array_values(array_column($orderChildren->toArray(), 'store_id'));
  283. $this->badgeService->doByOrder($orderMain->user_id, $orderChildIds, $orderMain->global_order_id, OrderState::REFUNDED);
  284. return [
  285. "status" => 200,
  286. "code" => 0,
  287. "result" => [],
  288. "message" => '处理成功'
  289. ];
  290. } catch (\Exception $e) {
  291. Db::rollBack();
  292. return [
  293. "status" => 200,
  294. "code" => $e->getCode(),
  295. "result" => [],
  296. "message" => '[退款失败]'.$e->getMessage()
  297. ];
  298. }
  299. }
  300. }