From 202e83b7eda221a788d4d8c73dfeb8ad6c9f969e Mon Sep 17 00:00:00 2001 From: devil_gong Date: Tue, 28 May 2019 16:07:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E9=80=80=E6=AC=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/Orderaftersale.php | 20 ++ .../view/default/orderaftersale/index.html | 4 +- application/service/OrderAftersaleService.php | 258 +++++++++++++++++- application/service/PayLogService.php | 4 +- application/service/RefundLogService.php | 222 +++++++++++++++ extend/payment/Alipay.php | 125 ++++++++- 6 files changed, 627 insertions(+), 6 deletions(-) create mode 100644 application/service/RefundLogService.php diff --git a/application/admin/controller/Orderaftersale.php b/application/admin/controller/Orderaftersale.php index 823f7b846..0beb2538a 100644 --- a/application/admin/controller/Orderaftersale.php +++ b/application/admin/controller/Orderaftersale.php @@ -137,6 +137,26 @@ class Orderaftersale extends Common return OrderAftersaleService::AftersaleAudit($params); } + /** + * 拒绝 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-23 + * @desc description + */ + public function Refuse() + { + // 是否ajax请求 + if(!IS_AJAX) + { + return $this->error('非法访问'); + } + + $params = input(); + return OrderAftersaleService::AftersaleRefuse($params); + } + /** * 订单取消 * @author Devil diff --git a/application/admin/view/default/orderaftersale/index.html b/application/admin/view/default/orderaftersale/index.html index b5ff29bb2..2787b0157 100644 --- a/application/admin/view/default/orderaftersale/index.html +++ b/application/admin/view/default/orderaftersale/index.html @@ -333,7 +333,7 @@ -
+
{{if !empty($common_order_aftersale_refundment_list)}} @@ -385,7 +385,7 @@ - +
diff --git a/application/service/OrderAftersaleService.php b/application/service/OrderAftersaleService.php index 6d88f2cf2..8bef52d1e 100644 --- a/application/service/OrderAftersaleService.php +++ b/application/service/OrderAftersaleService.php @@ -14,6 +14,7 @@ use think\Db; use think\facade\Hook; use app\service\UserService; use app\service\ResourcesService; +use app\service\RefundLogService; /** * 订单售后服务层 @@ -563,7 +564,7 @@ class OrderAftersaleService * @version 1.0.0 * @date 2019-05-27 * @desc description - * @param [array] $params [输入参数] + * @param [array] $params [输入参数] */ public static function AftersaleConfirm($params = []) { @@ -614,5 +615,260 @@ class OrderAftersaleService } return DataReturn('确认失败', -100); } + + /** + * 审核 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-27 + * @desc description + * @param [array] $params [输入参数] + */ + public static function AftersaleAudit($params = []) + { + // 请求参数 + $p = [ + [ + 'checked_type' => 'empty', + 'key_name' => 'id', + 'error_msg' => '操作id有误', + ], + [ + 'checked_type' => 'in', + 'key_name' => 'refundment', + 'checked_data' => array_column(lang('common_order_aftersale_refundment_list'), 'value'), + 'error_msg' => '退款方式有误', + ], + ]; + $ret = ParamsChecked($params, $p); + if($ret !== true) + { + return DataReturn($ret, -1); + } + + // 售后订单 + $aftersale = Db::name('OrderAftersale')->where(['id' => intval($params['id'])])->find(); + if(empty($aftersale)) + { + return DataReturn('数据不存在或已删除', -1); + } + + // 状态校验 + if($aftersale['status'] != 2) + { + $status_list = lang('common_order_aftersale_status_list'); + return DataReturn('状态不可操作['.$status_list[$aftersale['status']]['name'].']', -1); + } + + // 获取订单数据 + $order = self::OrdferGoodsRow($aftersale['order_id'], $aftersale['goods_id'], $aftersale['user_id']); + if($order['code'] != 0) + { + return $order; + } + + // 获取历史申请售后条件 + $where = [ + ['order_id', '=', $aftersale['order_id']], + ['status', '=', 3], + ['id', '<>', $aftersale['id']], + ]; + + // 历史退款金额 + $history_price = PriceNumberFormat(Db::name('OrderAftersale')->where($where)->sum('price')); + if($aftersale['price']+$history_price > $order['data']['pay_price']) + { + return DataReturn('退款金额大于支付金额[ 历史退款 '.$history_price.' ]', -1); + } + + // 历史退货数量 + $where[] = ['goods_id', '=', $aftersale['goods_id']]; + $history_number = (int) Db::name('OrderAftersale')->where($where)->sum('number'); + if($aftersale['type'] == 1) + { + if($aftersale['number']+$history_number > $order['data']['items']['buy_number']) + { + return DataReturn('退货数量大于购买数量[ 历史退货数量 '.$history_number.' ]', -1); + } + } + + // 订单支付方式校验 + $pay_log = Db::name('PayLog')->where(['order_id'=>$order['data']['id'], 'business_type'=>1])->find(); + + // 手动处理不校验支付日志 + if($params['refundment'] != 2) + { + if(empty($pay_log)) + { + return DataReturn('支付日志不存在,请使用手动处理方式', -1); + } + } + + // 原路退回 + if($params['refundment'] == 0) + { + if(in_array($pay_log['payment'], config('shopxo.under_line_list'))) + { + return DataReturn('线下支付方式不能原路退回[ '.$pay_log['payment_name'].' ]', -1); + } else { + $payment = 'payment\\'.$pay_log['payment']; + if(class_exists($payment)) + { + if(!method_exists((new $payment()), 'Refund')) + { + return DataReturn('支付插件没退款功能[ '.$pay_log['payment'].' ]', -1); + } + } else { + return DataReturn('支付插件不存在[ '.$pay_log['payment'].' ]', -1); + } + } + } + + // 钱包校验 + if($params['refundment'] == 1) + { + $wallet = Db::name('Plugins')->where(['plugins'=>'wallet'])->find(); + if(empty($wallet)) + { + return DataReturn('请先安装钱包插件[ Wallet ]', -1); + } + } + + // 退款方式 + $ret = DataReturn('退款方式未定义', -100); + switch($params['refundment']) + { + // 原路退回 + case 0 : + $ret = self::OriginalRoadRefundment($params, $aftersale, $order['data'], $pay_log); + break; + + // 退至钱包 + case 1 : + $ret = self::WalletRefundment($params, $aftersale, $order['data'], $pay_log); + break; + + // 手动处理 + case 2 : + $ret = DataReturn('操作成功', 0); + break; + } + return $ret; + } + + /** + * 原路退回 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-27 + * @desc description + * @param [array] $params [输入参数] + * @param [array] $aftersale [售后订单数据] + * @param [array] $order [订单数据] + * @param [array] $pay_log [订单支付日志] + */ + private static function OriginalRoadRefundment($params, $aftersale, $order, $pay_log) + { + // 支付方式 + $payment = PaymentService::PaymentList(['where'=>['payment'=>$pay_log['payment']]]); + if(empty($payment[0])) + { + return DataReturn('支付方式有误', -1); + } + + // 操作退款 + $pay_name = 'payment\\'.$pay_log['payment']; + $pay_params = [ + 'order_no' => $order['order_no'], + 'trade_no' => $pay_log['trade_no'], + 'refund_amount' => $aftersale['price'], + 'refund_reason' => $order['order_no'].'订单退款'.$aftersale['price'].'元', + ]; + $ret = (new $pay_name($payment[0]['config']))->Refund($pay_params); + if(!isset($ret['code'])) + { + return DataReturn('支付插件退款处理有误', -1); + } + if($ret['code'] != 0) + { + return DataReturn('支付插件退款失败', -50); + } + + // 写入退款日志 + $refund_log = [ + 'user_id' => $order['user_id'], + 'order_id' => $order['id'], + 'total_price' => $order['total_price'], + 'trade_no' => isset($ret['data']['trade_no']) ? $ret['data']['trade_no'] : '', + 'buyer_user' => isset($ret['data']['buyer_user']) ? $ret['data']['buyer_user'] : '', + 'refund_price' => $aftersale['price'], + 'msg' => $pay_params['refund_reason'], + 'payment' => $pay_log['payment'], + 'payment_name' => $pay_log['payment_name'], + 'business_type' => 1, + 'return_params' => $ret['data'], + ]; + RefundLogService::RefundLogInsert($refund_log); + + // 更新退款状态 + return self::OrderAftersaleSuccess($aftersale['id']); + } + + /** + * 订单售后审核完成 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-28 + * @desc description + * @param [int] $aftersale_id [订单售后id] + */ + private static function OrderAftersaleSuccess($aftersale_id) + { + // 更新退款状态 + $upd_data = [ + 'status' => 3, + 'audit_time' => time(), + 'upd_time' => time(), + ]; + if(Db::name('OrderAftersale')->where(['id'=>$aftersale_id])->update($upd_data)) + { + return DataReturn('退款成功', 0); + } + return DataReturn('退款失败', -100); + } + + /** + * 退至钱包 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-27 + * @desc description + * @param [array] $params [输入参数] + * @param [array] $aftersale [售后订单数据] + * @param [array] $order [订单数据] + * @param [array] $pay_log [订单支付日志] + */ + private static function WalletRefundment($params, $aftersale, $order, $pay_log) + { + return DataReturn('开发中', -10); + } + + /** + * 拒绝 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-27 + * @desc description + * @param [array] $params [输入参数] + */ + public static function AftersaleRefuse($params = []) + { + return DataReturn('开发中', -10); + } } ?> \ No newline at end of file diff --git a/application/service/PayLogService.php b/application/service/PayLogService.php index 534d58c5d..6348df5b2 100755 --- a/application/service/PayLogService.php +++ b/application/service/PayLogService.php @@ -112,7 +112,7 @@ class PayLogService } /** - * 后台消息总数 + * 总数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 @@ -126,7 +126,7 @@ class PayLogService } /** - * 后台消息列表条件 + * 列表条件 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 diff --git a/application/service/RefundLogService.php b/application/service/RefundLogService.php new file mode 100644 index 000000000..2509c1417 --- /dev/null +++ b/application/service/RefundLogService.php @@ -0,0 +1,222 @@ + isset($params['user_id']) ? intval($params['user_id']) : 0, + 'order_id' => isset($params['order_id']) ? intval($params['order_id']) : 0, + 'total_price' => isset($params['total_price']) ? PriceNumberFormat($params['total_price']) : 0.00, + 'trade_no' => isset($params['trade_no']) ? $params['trade_no'] : '', + 'buyer_user' => isset($params['buyer_user']) ? $params['buyer_user'] : '', + 'refund_price' => isset($params['refund_price']) ? PriceNumberFormat($params['refund_price']) : 0.00, + 'msg' => isset($params['msg']) ? $params['msg'] : '', + 'payment' => isset($params['payment']) ? $params['payment'] : '', + 'payment_name' => isset($params['payment_name']) ? $params['payment_name'] : '', + 'business_type' => isset($params['business_type']) ? intval($params['business_type']) : 0, + 'return_params' => empty($params['return_params']) ? '' : json_encode($params['return_params'], 'JSON_UNESCAPED_UNICODE'), + 'add_time' => time(), + ]; + return Db::name('RefundLog')->insertGetId($data) > 0; + } + + /** + * 获取支付日志类型 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @datetime 2018-12-23T02:22:03+0800 + * @param [array] $params [输入参数] + */ + public static function PayLogTypeList($params = []) + { + $data = Db::name('RefundLog')->field('payment AS id, payment_name AS name')->group('payment')->select(); + return DataReturn('处理成功', 0, $data); + } + + /** + * 后台管理员列表 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2018-09-29 + * @desc description + * @param [array] $params [输入参数] + */ + public static function AdminRefundLogList($params = []) + { + $where = empty($params['where']) ? [] : $params['where']; + $m = isset($params['m']) ? intval($params['m']) : 0; + $n = isset($params['n']) ? intval($params['n']) : 10; + $field = 'p.*,u.username,u.nickname,u.mobile,u.gender'; + $order_by = empty($params['order_by']) ? 'p.id desc' : $params['order_by']; + + // 获取数据列表 + $data = Db::name('RefundLog')->alias('p')->join(['__USER__'=>'u'], 'u.id=p.user_id')->where($where)->field($field)->limit($m, $n)->order($order_by)->select(); + if(!empty($data)) + { + $common_business_type_list = lang('common_business_type_list'); + $common_gender_list = lang('common_gender_list'); + foreach($data as &$v) + { + // 业务类型 + $v['business_type_name'] = $common_business_type_list[$v['business_type']]['name']; + + // 性别 + $v['gender_text'] = $common_gender_list[$v['gender']]['name']; + + // 时间 + $v['add_time_time'] = date('Y-m-d H:i:s', $v['add_time']); + $v['add_time_date'] = date('Y-m-d', $v['add_time']); + } + } + return DataReturn('处理成功', 0, $data); + } + + /** + * 后台总数 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2018-09-29 + * @desc description + * @param [array] $where [条件] + */ + public static function AdminRefundLogTotal($where = []) + { + return (int) Db::name('RefundLog')->alias('p')->join(['__USER__'=>'u'], 'u.id=p.user_id')->where($where)->count(); + } + + /** + * 后台列表条件 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2018-09-29 + * @desc description + * @param [array] $params [输入参数] + */ + public static function AdminRefundLogListWhere($params = []) + { + $where = []; + + // 关键字 + if(!empty($params['keywords'])) + { + $where[] = ['p.trade_no|u.username|u.nickname|u.mobile', 'like', '%'.$params['keywords'].'%']; + } + + // 是否更多条件 + if(isset($params['is_more']) && $params['is_more'] == 1) + { + // 等值 + if(isset($params['business_type']) && $params['business_type'] > -1) + { + $where[] = ['p.business_type', '=', intval($params['business_type'])]; + } + if(!empty($params['pay_type'])) + { + $where[] = ['p.payment', '=', $params['pay_type']]; + } + if(isset($params['gender']) && $params['gender'] > -1) + { + $where[] = ['u.gender', '=', intval($params['gender'])]; + } + + if(!empty($params['price_start'])) + { + $where[] = ['p.pay_price', '>', PriceNumberFormat($params['price_start'])]; + } + if(!empty($params['price_end'])) + { + $where[] = ['p.pay_price', '<', PriceNumberFormat($params['price_end'])]; + } + + if(!empty($params['time_start'])) + { + $where[] = ['p.add_time', '>', strtotime($params['time_start'])]; + } + if(!empty($params['time_end'])) + { + $where[] = ['p.add_time', '<', strtotime($params['time_end'])]; + } + } + + return $where; + } + + /** + * 删除 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2018-12-18 + * @desc description + * @param [array] $params [输入参数] + */ + public static function PayLogDelete($params = []) + { + // 请求参数 + $p = [ + [ + 'checked_type' => 'empty', + 'key_name' => 'id', + 'error_msg' => '操作id有误', + ], + ]; + $ret = ParamsChecked($params, $p); + if($ret !== true) + { + return DataReturn($ret, -1); + } + + // 删除操作 + if(Db::name('PayLog')->where(['id'=>$params['id']])->delete()) + { + return DataReturn('删除成功'); + } + + return DataReturn('删除失败或资源不存在', -100); + } +} +?> \ No newline at end of file diff --git a/extend/payment/Alipay.php b/extend/payment/Alipay.php index 973deb6f1..a2fd53078 100755 --- a/extend/payment/Alipay.php +++ b/extend/payment/Alipay.php @@ -213,7 +213,6 @@ class Alipay exit($this->BuildRequestForm($parameter)); } - /** * 支付回调处理 @@ -274,6 +273,85 @@ class Alipay return $data; } + /** + * 退款处理 + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @date 2019-05-28 + * @desc description + * @param [array] $params [输入参数] + */ + public function Refund($params = []) + { + // 参数 + $p = [ + [ + 'checked_type' => 'empty', + 'key_name' => 'order_no', + 'error_msg' => '订单号不能为空', + ], + [ + 'checked_type' => 'empty', + 'key_name' => 'trade_no', + 'error_msg' => '交易平台订单号不能为空', + ], + [ + 'checked_type' => 'empty', + 'key_name' => 'refund_amount', + 'error_msg' => '退款金额不能为空', + ], + ]; + $ret = ParamsChecked($params, $p); + if($ret !== true) + { + return DataReturn($ret, -1); + } + + // 退款原因 + $refund_reason = empty($params['refund_reason']) ? $params['order_no'].'订单退款'.$params['refund_amount'].'元' : $params['refund_reason']; + + // 退款参数 + $parameter = array( + 'app_id' => $this->config['appid'], + 'method' => 'alipay.trade.refund', + 'format' => 'JSON', + 'charset' => 'utf-8', + 'sign_type' => 'RSA2', + 'timestamp' => date('Y-m-d H:i:s'), + 'version' => '1.0', + ); + $biz_content = array( + 'out_trade_no' => $params['order_no'], + 'trade_no' => $params['trade_no'], + 'refund_amount' => $params['refund_amount'], + 'refund_reason' => $refund_reason, + ); + $parameter['biz_content'] = json_encode($biz_content, JSON_UNESCAPED_UNICODE); + + // 生成签名参数+签名 + $parameter['sign'] = $this->MyRsaSign($this->GetSignContent($parameter)); + + // 执行请求 + $result = $this->HttpRequest('https://openapi.alipay.com/gateway.do', $parameter); + $key = str_replace('.', '_', $parameter['method']).'_response'; + + // 验证签名 + if(!$this->SyncRsaVerify($result, $key)) + { + return DataReturn('签名验证错误', -1); + } + + // 状态 + if(isset($result[$key]['code']) && $result[$key]['code'] == 10000) + { + return DataReturn('退款成功', 0, $result[$key]); + } + + // 直接返回支付信息 + return DataReturn($result[$key]['sub_msg'].'['.$result[$key]['sub_code'].']', -1000); + } + /** * 建立请求,以表单HTML形式构造(默认) * @author Devil @@ -304,6 +382,51 @@ class Alipay return $html; } + /** + * [HttpRequest 网络请求] + * @author Devil + * @blog http://gong.gg/ + * @version 1.0.0 + * @datetime 2017-09-25T09:10:46+0800 + * @param [string] $url [请求url] + * @param [array] $data [发送数据] + * @return [mixed] [请求返回数据] + */ + private function HttpRequest($url, $data) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + + $body_string = ''; + if(is_array($data) && 0 < count($data)) + { + foreach($data as $k => $v) + { + $body_string .= $k.'='.urlencode($v).'&'; + } + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body_string); + } + $headers = array('content-type: application/x-www-form-urlencoded;charset=UTF-8'); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + $reponse = curl_exec($ch); + if(curl_errno($ch)) + { + return false; + } else { + $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if(200 !== $httpStatusCode) + { + return false; + } + } + curl_close($ch); + return json_decode($reponse, true); + } + /** * 获取签名内容 * @author Devil -- GitLab