model = new OrderReturn(); $this->validate = true; $this->validateClass = new ReturnValidate(); } public function select(Request $request): Response { [$where, $format, $limit, $field, $order] = $this->selectInput($request); $order = $request->get('order', 'desc'); $type = $request->get('type', ''); $field = $field ?? 'order_return_addtimes'; $where['order_return_category'] = '退款'; if (!empty($where['order_return_addtimes'])) { $where['order_return_addtimes'][0] = strtotime($where['order_return_addtimes'][0]); $where['order_return_addtimes'][1] = strtotime($where['order_return_addtimes'][1]); } if ($type == 'today') { $where['order_return_addtimes'] = [ strtotime(date('Y-m-d') . ' 00:00:00'), strtotime(date('Y-m-d') . ' 23:59:59') ]; } $query = $this->doSelect($where, $field, $order); return $this->doFormat($query, $format, $limit); } protected function doSelect(array $where, string $field = null, string $order = 'desc') { $model = $this->model->with([ 'member' => function ($query) { $query->select('member_id', 'member_mobile'); }, 'cert' => function ($query) { $query->select('join_cert_member_id', 'member_cert_name'); }, 'order' => function ($query) { $query->select('order_id', 'order_name'); } ]); foreach ($where as $column => $value) { if (is_array($value)) { if ($value[0] === 'like' || $value[0] === 'not like') { $model = $model->where($column, $value[0], "%$value[1]%"); } elseif (in_array($value[0], ['>', '=', '<', '<>'])) { $model = $model->where($column, $value[0], $value[1]); } elseif ($value[0] == 'in' && !empty($value[1])) { $valArr = $value[1]; if (is_string($value[1])) { $valArr = explode(",", trim($value[1])); } $model = $model->whereIn($column, $valArr); } elseif ($value[0] == 'not in' && !empty($value[1])) { $valArr = $value[1]; if (is_string($value[1])) { $valArr = explode(",", trim($value[1])); } $model = $model->whereNotIn($column, $valArr); } elseif ($value[0] == 'null') { $model = $model->whereNull($column); } elseif ($value[0] == 'not null') { $model = $model->whereNotNull($column); } elseif ($value[0] !== '' || $value[1] !== '') { $model = $model->whereBetween($column, $value); } } else { $model = $model->where($column, $value); } } if ($field) { $model = $model->orderBy($field, $order); } return $model; } public function afterQuery($items) { foreach ($items as &$item) { if (!empty($item['order'])) { $orderSheet = OrderSheet::with([ 'goods' => function ($query) { $query->select('goods_id', 'goods_name'); } ])->select('join_sheet_order_id', 'join_sheet_goods_id') ->where('join_sheet_order_id', $item['order']->order_id) ->get() ->toArray(); $goodsName = !empty($orderSheet) ? $orderSheet[0]['goods']['goods_name'] : ''; if (count($orderSheet) > 1) { $goodsName .= ' 等'; } $item['goods_name'] = $goodsName; } if (!empty($item['order_return_apply_json']) && is_json($item['order_return_apply_json'])) { $json = json_decode($item['order_return_apply_json'], true); $item['order_return_apply_json'] = $json['apply'] ?? ''; } if (!empty($item['order_return_recharge_json']) && is_json($item['order_return_recharge_json'])) { $json = json_decode($item['order_return_recharge_json'], true); $item['order_return_recharge_json'] = $json['change'] ?? ''; } if (!empty($item['order_return_refund_json']) && is_json($item['order_return_refund_json'])) { $returnRefundJson = json_decode($item['order_return_refund_json'], true); if (!empty($returnRefundJson['user_id'])) { $item['userName'] = SysUser::where('user_id', $returnRefundJson['user_id'])->value('user_name'); } } } return $items; } protected function updateInput(Request $request): array { $primary_key = $this->model->getKeyName(); $id = $request->post($primary_key); $data = $this->inputFilter($request->post()); $model = $this->model->find($id); if (!$model) { throw new BusinessException('记录不存在', 2); } if (empty($model->order_return_accept_datetimes) && $model->order_return_status == 'PENDING' && $data['order_return_status'] == 'DOING') { $data['order_return_accept_datetimes'] = date('Y-m-d H:i:s'); } if (!empty($data['order_return_apply_json'])) { $data['order_return_apply_json'] = json_encode(['apply' => $data['order_return_apply_json']]); } if (!empty($data['order_return_recharge_json'])) { $data['order_return_recharge_json'] = json_encode(['change' => $data['order_return_recharge_json']]); } unset($data[$primary_key]); return [$id, $data]; } /** * @Desc 订单商品详情 * @Author Gorden * @Date 2024/3/29 8:50 * * @param Request $request * @return Response */ public function sheet(Request $request) { $orderId = $request->get('order_id'); $orderSheet = OrderSheet::with([ 'member' => function ($query) { $query->select('member_id', 'member_mobile'); }, 'goods' => function ($query) { $query->select('goods_id', 'goods_name', 'goods_cover', 'goods_market_price', 'goods_sales_price', 'goods_classify'); }, 'sku' => function ($query) { $query->select('goods_sku_id', 'join_sku_goods_id', 'goods_sku_specs_json', 'goods_sku_sales_price'); }, 'memberInfo', 'cert', 'refund' ])->where('join_sheet_order_id', $orderId) ->get() ->toArray(); $member = []; $cert = []; $memberInfo = []; foreach ($orderSheet as &$item) { if (isset($item['member']) && empty($member)) { $member = $item['member']; } if (isset($item['cert']) && empty($cert)) { $cert = $item['cert']; } if (isset($item['member_info']) && empty($memberInfo)) { $memberInfo = $item['member_info']; } $item['goods']['goods_cover'] = getenv('STORAGE_DOMAIN') . $item['goods']['goods_cover']; if (!empty($item['goods']) && $item['goods']['goods_classify'] == 'PACKAGE') { $components = GoodsComponent::with('goods') ->where('join_component_master_goods_id', $item['goods']['goods_id']) ->select('join_component_master_goods_id', 'join_component_goods_id', 'goods_component_price', 'goods_component_price') ->get() ->toArray(); $goodsArr = []; foreach ($components as $component) { $configJson = !empty($component['goods_component_config_json']) ? json_decode($component['goods_component_config_json'], true) : []; if (!empty($component['goods'])) { $goodsArr[] = [ 'goods_name' => $component['goods']['goods_name'], 'goods_cover' => getenv('STORAGE_DOMAIN') . $component['goods']['goods_cover'], 'nbr' => $configJson['nbr'] ?? 0, ]; } } $item['goods']['components'] = $goodsArr; } if (!empty($item['sku'])) { if (!empty($item['sku']['goods_sku_specs_json'])) { $specsJson = json_decode($item['sku']['goods_sku_specs_json'], true); $skuName = ''; foreach ($specsJson as $specsKey => $skuSpecs) { if (is_array($skuSpecs)) { $skuName = $skuName . ' ' . implode(' ', $skuSpecs) . ';'; } else { $skuName = $skuName . ' ' . $skuSpecs . ';'; } } $item['sku']['goods_sku_title'] = rtrim($skuName, ';'); } } if (!empty($item['refund'])) { if (!empty($item['refund']['order_return_apply_json']) && is_json($item['refund']['order_return_apply_json'])) { $json = json_decode($item['refund']['order_return_apply_json'], true); $item['refund']['order_return_apply_json'] = $json['reason'] ?? ''; } if (!empty($item['refund']['order_return_recharge_json']) && is_json($item['refund']['order_return_recharge_json'])) { $json = json_decode($item['refund']['order_return_recharge_json'], true); $item['refund']['order_return_recharge_json'] = $json['change'] ?? ''; } } } $order = Order::where('order_id', $orderId)->first(); $return = OrderReturn::where('join_return_order_id', $orderId)->first(); if (!empty($return->order_return_refund_json)) { $returnRefundJson = json_decode($return->order_return_refund_json, true); if (!empty($returnRefundJson['user_id'])) { $return->userName = SysUser::where('user_id', $returnRefundJson['user_id'])->value('user_name'); } } $express = OrderExpress::where('join_express_order_id', $orderId)->first(); $data = [ 'member' => $member, 'cert' => $cert, 'member_info' => $memberInfo, 'order' => $order, 'refund' => $return, 'sheet' => $orderSheet, 'express' => $express ]; return json_success('', $data); } public function customRefund(Request $request) { $orderId = $request->post('order_id', ''); $amount = $request->post('refund_amount', 0); $password = $request->post('refund_password', ''); $remark = $request->post('refund_remark', ''); if (!$orderId || !$amount || !$password) { return json_fail('参数异常'); } if ($password != '123456') { return json_fail('支付密码错误'); } $order = Order::where('order_id', $orderId)->where('order_status_payment', 'SUCCESS')->first(); if (!$order) { return json_fail("订单异常"); } if ($amount > $order->order_amount_pay) { return json_fail("退款金额不能超过支付金额"); } $payDetail = PayDetail::where('join_pay_order_id', $order->order_groupby) ->where('pay_status', 'SUCCESS') ->whereIn('pay_category', ['GOODS', 'SERVICE', 'CHNMED', 'CHNNCD', 'MEALS', 'DISHES', 'VIP', 'PACKAGE']) ->get() ->toArray(); if (empty($payDetail)) { return json_fail("支付状态异常"); } $refundPayIds = array_column($payDetail, 'pay_id'); $response = []; Db::beginTransaction(); try { // 全额退款,检查优惠券,恢复可使用 if (sprintf("%.2f", $amount) == sprintf("%.2f", $order->order_amount_pay)) { if (!empty($order->order_discount_json)) { $discountJson = json_decode($order->order_discount_json, true); foreach ($discountJson as $item){ if (empty($item['coupon_id'])){ continue; } // 是否有其他订单一起使用优惠券 if(!Order::where('order_groupby',$order->order_groupby)->where('order_id','<>',$order->order_id)->where('order_is_complete','<>','R')->exists()){ foreach ($item['coupon_detail_id'] as $detailId){ CouponDetail::where('coupon_detail_id',$detailId)->update([ 'coupon_detail_status'=>'ACTIVED', 'coupon_detail_used_datetime'=>'' ]); } } } } } // 主订单,退款作为优惠入库 $this->updateMainOrderByRefund($order, $amount, $remark); // return 表记录 $return = OrderReturn::where('join_return_order_id', $orderId)->first(); if (empty($return)) { $returnId = $this->createReturnRecord($order, $amount, $remark); } else { $returnId = $return->orders_return_id; //['amount' => $amount, 'user_id' => JwtToken::getCurrentId(), 'datetime' => date('Y-m-d H:i:s'), 'remark' => $remark ?? ''] OrderReturn::where('join_return_order_id', $orderId)->update([ 'order_return_status' => 'DONE', 'order_return_refund_json' => json_encode([ 'amount' => $amount, 'user_id' => JwtToken::getCurrentId(), 'datetime' => date('Y-m-d H:i:s'), 'remark' => $remark ?? '' ]) ]); } // 组合支付,退到余额账户 $prepayid = ''; if (count($payDetail) > 1) { $prepayid = $order->join_order_member_id . '-CASH'; $this->refundToCash($order->join_order_member_id, $amount); $response = ['order_id' => $order->order_id, 'member_id' => $order->join_order_member_id]; } else if (count($payDetail) == 1) { $payDetail0 = $payDetail[0]; $payWay = explode('-', $payDetail0['pay_prepayid']); if ($payWay[0] == 'WXPAY') { $prepayid = 'WXPAY'; $response = $this->refundToWx($payDetail0, $returnId, $amount); } elseif ($payWay[0] == 'ALIPAY') { $prepayid = 'ALIPAY'; $response = $this->refundToAlipay($payDetail0, $amount); } elseif ($payWay[0] == 'MONEY') { $prepayid = 'MONEY'; $response = $this->refundToCash($payDetail0, $amount); } elseif (isset($payWay[1]) && $payWay[1] == 'CASH') { $prepayid = $order->join_order_member_id . '-CASH'; $this->refundToCash($order->join_order_member_id, $amount); $response = ['order_id' => $order->order_id, 'member_id' => $order->join_order_member_id]; } elseif (isset($payWay[1]) && $payWay[1] == 'WELFARE') { $prepayid = $order->join_order_member_id . '-WELFARE'; $this->refundToWelfare($order->join_order_member_id, $amount); $response = ['order_id' => $order->order_id, 'member_id' => $order->join_order_member_id]; } elseif (isset($payWay[1]) && $payWay[1] == 'VIP') { $prepayid = $order->join_order_member_id . '-VIP'; $this->refundToVIP($order->join_order_member_id, $amount); $response = ['order_id' => $order->order_id, 'member_id' => $order->join_order_member_id]; } elseif (isset($payWay['1']) && $payWay[1] == 'CARD') { $prepayid = $order->join_order_member_id . '-CARD'; $this->refundToCard($order->join_order_member_id, $amount); $response = ['order_id' => $order->order_id, 'member_id' => $order->join_order_member_id]; } elseif ($payWay[0] == 'OFFLINE') { $prepayid = 'OFFLINE'; $response = []; } elseif ($payWay[0] == 'OFFLINE_ALIPAY') { $prepayid = 'OFFLINE_ALIPAY'; $response = []; } elseif ($payWay[0] == 'OFFLINE_WXPAY') { $prepayid = 'OFFLINE_WXPAY'; $response = []; } } // payDetail 表记录 $this->createReturnPayDetail($order, $refundPayIds, $prepayid, $amount, $response); Db::commit(); return json_success('success'); } catch (BusinessException $e) { Db::rollBack(); dump($e->getMessage()); return json_fail($e->getMessage()); } catch (\Exception $e) { Db::rollBack(); dump($e->getMessage()); return json_fail("退款失败"); } } /** * @Desc 退款到余额 * @Author Gorden * @Date 2024/8/20 11:54 * * @param $memberId * @param $amount * @return void * @throws BusinessException */ private function refundToCash($memberId, $amount) { $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'CASH')->first(); if (!$account) { throw new BusinessException("余额账户异常"); } $account->member_account_surplus = $account->member_account_surplus + $amount; $account->save(); } /** * @Desc 退款到福利账户 * @Author Gorden * @Date 2024/9/20 17:09 * * @param $memberId * @param $amount * @return void * @throws BusinessException */ private function refundToWelfare($memberId, $amount) { $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'WELFARE')->first(); if (!$account) { throw new BusinessException("余额账户异常"); } $account->member_account_surplus = $account->member_account_surplus + $amount; $account->save(); } /** * @Desc 退款到VIP账户 * @Author Gorden * @Date 2024/9/20 17:12 * * @param $memberId * @param $amount * @return void * @throws BusinessException */ private function refundToVIP($memberId, $amount) { $account = MemberAccount::where('join_account_member_id', $memberId)->where('member_account_classify', 'VIP')->first(); if (!$account) { throw new BusinessException("余额账户异常"); } $account->member_account_surplus = $account->member_account_surplus + $amount; $account->save(); } /** * @Desc 退款到储蓄卡 * @Author Gorden * @Date 2024/8/20 14:47 * * @param $memberId * @param $amount * @return void * @throws BusinessException */ private function refundToCard($cardNbr, $amount) { $account = MemberAccount::where('member_account_nbr', $cardNbr)->where('member_account_classify', 'CARD')->first(); if (!$account) { throw new BusinessException("储值卡账户异常"); } $account->member_account_surplus = $account->member_account_surplus + $amount; $account->save(); } /** * @Desc * @Author Gorden * @Date 2024/8/20 14:17 * * @param $params * @param $returnId * @param $amount * @return mixed * @throws BusinessException * @throws \Yansongda\Pay\Exceptions\GatewayException * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException * @throws \Yansongda\Pay\Exceptions\InvalidSignException */ private function refundToWx($params, $returnId, $amount) { try { $data = [ 'type' => 'app', 'out_trade_no' => $params['join_pay_order_id'], 'out_refund_no' => $returnId, 'total_fee' => $params['pay_amount'] * 100, 'refund_fee' => $amount * 100, 'refund_desc' => '退款', ]; $res = Pay::wechat(config('payment.wxpay'))->refund($data); $resArray = json_decode($res, true); if (!$resArray['result_code'] == 'SUCCESS' || !$resArray['return_code'] == 'SUCCESS') { Log::channel('pay')->error('WXPAY_REFUND_FAIL', $resArray); throw new BusinessException("退款失败"); } return $resArray; } catch (\Exception $e) { throw new BusinessException("退款失败"); } } /** * @Desc 支付宝退款 * @Author Gorden * @Date 2024/8/20 14:40 * * @param $params * @param $amount * @return mixed * @throws BusinessException */ private function refundToAlipay($params, $amount) { $data = [ 'out_trade_no' => $params['join_pay_order_id'], 'refund_amount' => $amount, ]; try { $res = Pay::alipay(config('payment.alipay'))->refund($data); $resArray = json_decode($res, true); if ($resArray['fund_change'] != 'Y' || $resArray['msg'] != 'Success') { Log::channel('pay')->error('ALIPAY_REFUND_FAIL', $resArray); throw new BusinessException("退款失败"); } return $resArray; } catch (\Exception $e) { throw new BusinessException("退款失败"); } } /** * @Desc 更新主订单 * @Author Gorden * @Date 2024/8/20 14:56 * * @param $order * @param $amount * @param $remark * @return void * @throws BusinessException */ private function updateMainOrderByRefund($order, $amount, $remark) { $orderDiscountJson = []; if (!empty($order->order_discount_json)) { $orderDiscountJson = json_decode($order->order_discount_json, true); } try { $orderDiscountJson[date('YH:i:s H:i:s')] = [ 'coupon_id' => null, 'coupon_value' => $amount, 'coupon_classify' => '退款', 'coupon_detail_id' => [$remark] ]; $order->order_discount_json = json_encode($orderDiscountJson, JSON_UNESCAPED_UNICODE); $order->order_is_complete = 'R'; // $order->order_amount_pay = $order->order_amount_pay - $amount; $order->save(); } catch (\Exception $e) { throw new BusinessException("退款失败"); } } /** * @Desc * @Author Gorden * @Date 2024/8/20 13:51 * * @param $order * @return int * @throws BusinessException */ private function createReturnRecord($order, $amount, $remark) { try { return OrderReturn::insertGetId([ 'join_order_return_user_id' => JwtToken::getCurrentId(), 'join_return_member_id' => $order->join_order_member_id, 'join_return_order_id' => $order->order_id, 'order_return_status' => 'DONE', 'order_return_category' => '退款', 'order_return_apply_datetime' => date('Y-m-d H:i:s'), 'order_return_apply_json' => json_encode(['reason' => '后台自定义退款']), 'order_return_accept_datetime' => date('Y-m-d H:i:s'), 'order_return_refund_json' => json_encode(['amount' => $amount, 'user_id' => JwtToken::getCurrentId(), 'datetime' => date('Y-m-d H:i:s'), 'remark' => $remark ?? '']), 'order_return_addtimes' => time() ]); } catch (\Exception $e) { throw new BusinessException("创建退款信息失败"); } } private function createReturnPayDetail($order, $refundPayIds, $prepayid, $amount, $response = []) { try { return PayDetail::insert([ 'join_pay_member_id' => $order->join_order_member_id, 'join_pay_order_id' => $order->order_groupby, 'pay_status' => 'SUCCESS', 'pay_category' => 'REFUND', 'pay_amount' => $amount, 'pay_paytimes' => date('Y-m-d H:i:s'), 'pay_prepayid' => $prepayid, 'join_pay_object_json' => json_encode(['order_id' => $order->order_id, 'refund_pay_ids' => $refundPayIds]), 'pay_json_request' => json_encode(['order_id' => $order->order_id]), 'pay_json_response' => json_encode($response), 'pay_addtimes' => time() ]); } catch (\Exception $e) { } } }