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.

329 lines
13 KiB

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