user = $user ? $user:User::get($user); } /** * 微信预付款 * * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function wechat($order, $money, $order_type = 'order') { $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'wechat', 'pay_fee' => $money, 'real_fee' => $money, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_UNPAID ]); return $pay; } /** * 抖音预付款 * * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function douyin($order, $money, $order_type = 'order') { $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'douyin', 'pay_fee' => $money, 'real_fee' => $money, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_UNPAID ]); return $pay; } /** * 支付宝预付款 * * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function alipay($order, $money, $order_type = 'order') { $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'alipay', 'pay_fee' => $money, 'real_fee' => $money, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_UNPAID ]); return $pay; } /** * 余额付款 * * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function money($order, $money, $order_type = 'order') { // 余额支付金额,传入金额和剩余支付金额最大值 $money = $order->remain_pay_fee > $money ? $money : $order->remain_pay_fee; // 混合支付不能超过订单应支付总金额 // 扣除用户余额 WalletService::change($this->user, 'money', -$money, 'order_pay', [ 'order_id' => $order->id, 'order_sn' => $order->order_sn, 'order_type' => $order_type, ]); // 添加支付记录 $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'money', 'pay_fee' => $money, 'real_fee' => $money, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_PAID ]); // 余额直接支付成功,更新订单剩余应付款金额,并检测订单状态 return $this->checkAndPaid($order, $order_type); } /** * 积分支付 * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function score($order, $score, $order_type = 'order') { if ($order_type == 'order') { if ($order['type'] == 'score') { $log_type = 'score_shop_pay'; $real_fee = $score; // 积分商城真实抵扣,就是积分 } else { $log_type = 'order_pay'; // $real_fee = ; // 积分商城真实抵扣,就是积分 error_stop('缺少积分抵扣金额'); // 支持积分抵扣时补全 } } WalletService::change($this->user, 'score', -$score, $log_type, [ 'order_id' => $order->id, 'order_sn' => $order->order_sn, 'order_type' => $order_type, ]); // 添加支付记录 $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'score', 'pay_fee' => $score, 'real_fee' => $real_fee, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_PAID ]); // 积分直接支付成功,更新订单剩余应付款金额,并检测订单状态 return $this->checkAndPaid($order, $order_type); } /** * 线下支付(货到付款) * * @param think\Model $order * @param float $money * @param string $order_type * @return think\Model */ public function offline($order, $money, $order_type = 'order') { // 添加支付记录 $pay = $this->addPay($order, [ 'order_type' => $order_type, 'pay_type' => 'offline', 'pay_fee' => $money, 'real_fee' => $money, 'transaction_id' => null, 'payment_json' => [], 'status' => PayEnum::PAY_STATUS_PAID ]); // 更新订单剩余应付款金额,并检测订单状态 return $this->checkAndPaid($order, $order_type, 'offline'); } /** * 微信支付宝、支付回调、 抖音通用方法 * * @param \think\Model $pay * @param array $notify * @return void */ public function notify($pay, $notify) { $pay->status = PayEnum::PAY_STATUS_PAID; $pay->transaction_id = $notify['transaction_id']; $pay->buyer_info = $notify['buyer_info']; $pay->payment_json = $notify['payment_json']; $pay->paid_time = time(); $pay->save(); $order = new Order(); $order = $order->where('id', $pay->order_id)->find(); if (!$order) { // 订单未找到,非正常情况,这里记录日志 \think\Log::write('pay-notify-error:order notfound;pay:' . json_encode($pay) . ';notify:' . json_encode($notify)); return false; } if(!$order->isPayStatus()){ // 未支付,检测支付状态 $order = $this->checkAndPaid($order, $pay->order_type); } return $order; } /** * 更新订单剩余应支付金额,并且检测订单状态 * * @param think\Model $order * @param string $order_type * @return think\Model */ public function checkAndPaid($order, $order_type) { // 获取订单已支付金额 $payed_fee = $this->getPayedFee($order, $order_type); $payRemainAmount = bcsub($order->pay_amount, (string)$payed_fee, 2); $order->pay_remain_amount = $payRemainAmount; if ($payRemainAmount <= 0) { $order->pay_remain_amount = 0; $order->pay_time = time(); $order->order_status = OrderEnum::STATUS_PAY; } else { // if ($pay_mode == 'offline') { // // 订单未支付成功,并且是线下支付(货到付款),将订单状态改为 pending // $order->status = $order::STATUS_PENDING; // $order->ext = array_merge($order->ext, ['pending_time' => time()]); // 货到付款下单时间 // $order->pay_mode = 'offline'; // } } $order->save(); if ($order->order_status == OrderEnum::STATUS_PAY) { // 订单支付完成 $user = User::where('id', $order->user_id)->find(); if ($order_type == OrderEnum::TYPE_NORMAL) { // 支付类型 $pay_type = $order->pay_type; $pay_type_text = PayEnum::getPayMethodText($pay_type); OrderActionService::recordUserAction( $order->order_sn, OrderActionEnum::ACTION_PAY, $user->nickname, $pay_type_text.'支付成功', $user->id ); // 减库存增加 销量 、 OrderGoodsService::setGoodsSalesInc($order->order_sn); //OrderGoodsService::setGoodsStocksDec($order->order_sn); //处理发票审核改为等待开具 、 // 处理活动,加入拼团,完成拼团,添加赠品记录等 、 将订单参与活动信息改为已支付 // todo 支付成功后续使用异步队列处理 } } // else if 货到付款的逻辑、处理 return $order; } /** * 获取订单已支付金额,商城订单 计算 积分抵扣金额 * * @param \think\Model $order * @param string $order_type * @return string */ public function getPayedFee($order, $order_type) { // 锁定读取所有已支付的记录,判断已支付金额 $pays = PayModel::where('order_type', $order_type) ->where('status', PayEnum::PAY_STATUS_PAID) ->where('order_id', $order->id) ->lock(true) ->select(); // 商城或者积分商城订单 $payedAmount = '0'; // bc函数需要字符串类型 foreach ($pays as $key => $pay) { //支付类型是积分 if ($pay->pay_type == PayEnum::METHOD_SCORE) { if ($order_type == OrderEnum::TYPE_NORMAL && $order->type == OrderEnum::TYPE_NORMAL) { // 商城类型订单,并且不是积分商城订单,加上积分抵扣真实金额 $payedAmount = bcadd($payedAmount, $pay->real_fee, 2); } else { // 其他类型,需要计算积分抵扣的金额时 } } else { $payedAmount = bcadd($payedAmount, $pay->real_fee, 2); } } return $payedAmount; } /** * 获取剩余可退款的pays 记录(不含积分抵扣) * * @param integer $order_id * @param string $sort 排序:money=优先退回余额支付的钱 * @return \think\Collection */ public function getCanRefundPays($order_id, $sort = 'money') { // 商城订单,已支付的 pay 记录, 这里只查 钱的支付记录,不查积分 $pays = PayModel::typeOrder()->paid()->isMoney()->where('order_id', $order_id)->lock(true)->order('id', 'asc')->select(); $pays = collection($pays); if ($sort == 'money') { // 对 pays 进行排序,优先退 money 的钱 $pays = $pays->sort(function ($a, $b) { if ($a['pay_type'] == 'money' && $b['pay_type'] == 'money') { return 0; } else if ($a['pay_type'] == 'money' && $b['pay_type'] != 'money') { return -1; } else if ($a['pay_type'] != 'money' && $b['pay_type'] == 'money') { return 1; } else { return 0; } }); $pays = $pays->values(); } return $pays; } /** * 获取剩余可退款金额,不含积分相关支付 * @param mixed $pays * @return string */ public function getRemainRefundMoney($pays) { // 拿到 所有可退款的支付记录 $pays = ($pays instanceof \think\Collection) ? $pays : $this->getCanRefundPays($pays); // 支付金额,除了已经退完款的金额 (这里不退积分) $payed_money = (string)array_sum($pays->column('pay_fee')); // 已经退款金额 (这里不退积分) $refunded_money = (string)array_sum($pays->column('refund_fee')); // 当前剩余的最大可退款金额,支付金额 - 已退款金额 $remain_max_refund_money = bcsub($payed_money, $refunded_money, 2); return $remain_max_refund_money; } function get_sn($id, $type = '') { $id = (string)$id; $rand = $id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999); $sn = date('Yhis') . $rand; $id = str_pad($id, (24 - strlen($sn)), '0', STR_PAD_BOTH); return $type . $sn . $id; } /** * 添加 pay 记录 * * @param think\Model $order * @param array $params * @return think\Model */ public function addPay($order, $params) { $payModel = new PayModel(); $payModel->order_type = $params['order_type']; $payModel->order_id = $order->id; $payModel->pay_sn = $this->get_sn($this->user->id, 'P'); $payModel->user_id = $this->user->id; $payModel->pay_type = $params['pay_type']; $payModel->pay_fee = $params['pay_fee']; $payModel->real_fee = $params['real_fee']; $payModel->transaction_id = $params['transaction_id']; $payModel->payment_json = $params['payment_json']; $payModel->paid_time = $params['status'] == PayEnum::PAY_STATUS_PAID ? time() : null; $payModel->status = $params['status']; $payModel->refund_fee = 0; $payModel->save(); return $payModel; } // 根据订单id 获取支付信息 /** * 根据订单id获取支付信息 * * @param int $orderId 订单ID * @param int $orderType 订单类型,默认为 1 * @return \think\Collection */ public static function getPayInfoByOrderId($orderId = 0, $orderType = 1) { $pays = PayModel::where('order_type', $orderType) ->field('id,pay_type,pay_fee,real_fee,transaction_id,paid_time') ->where('order_id', $orderId) ->find(); return $pays; } /** * 根据订单ids批量获取支付信息 * * @param array $orderIds 订单ID数组 * @param int $orderType 订单类型,默认为 1 * @return \think\Collection */ public static function getPayInfoByOrderIds($orderIds =[], $orderType = 1) { if (empty($orderIds) || !is_array($orderIds)) { return collection([]); } $pays = PayModel::where('order_type', $orderType) ->field('id,order_id,pay_type,pay_fee,real_fee,transaction_id,paid_time') ->where('order_id', 'in', $orderIds) ->select(); return collection($pays); } }