| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 | <?phpnamespace app\common\Service\Pay;use think\Db;use think\Exception;use app\common\model\User;use app\common\exception\BusinessException;use app\common\Enum\OrderEnum;use app\common\Service\OrderService;use addons\epay\library\Service;use Yansongda\Pay\Exceptions\GatewayException;use app\common\Service\Order\OrderGoodsService;use app\common\model\OrderAction;use app\common\model\pay\Index as PayModel;use app\common\Enum\PayEnum;use app\common\model\Order;use app\common\Service\Order\OrderActionService;use app\common\Enum\OrderActionEnum;/** * 支付服务类 * 封装订单创建相关逻辑 */class PayOperService{    protected $user = null;    /**     * 实例化     *     * @param mixed $user     */    public function __construct($user = null)    {        // 优先使用传入的用户        $this->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)            ->order('id', 'desc')            ->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,pay_type,pay_fee,real_fee,transaction_id,paid_time')            ->where('order_id', 'in', $orderIds)            ->select();                return collection($pays);    }} 
 |