userModel = new User(); $this->userWalletModel = new Wallet(); $this->userBillModel = new Bill(); $this->orderModel = new Order(); $this->thirdPayService = new ThirdPayService(); $this->thirdPayLogModel = new ThirdPayLog(); $this->userVoucherModel = new Voucher(); } public function fetchRechargeOption() { $cfg = config("site.balance_recharge"); return array_map(function ($data) use ($cfg) { return [ "amount" => (float)$data, "give" => (float)$cfg[$data] ]; }, array_keys($cfg)); } public function fetchVoucher($user_id, $status, $page = 1, $size = 10) { $this->userVoucherModel->passVoucher($user_id); $paginate = $this->userVoucherModel->fetchVoucher($user_id, $status, $page, $size); return [ $paginate->items(), $paginate->total() ]; } // 余额充值 public function rechargeBalance($user_id, $payment_type, $amount, $platform): \SResult { $user = $this->userModel->findById($user_id); if (!$user) return $this->fail("用户不存在!"); $walletLock = new RedLock(); $wLock = $walletLock->lock(Wallet::UWKey($user_id)); if (!is_array($wLock)) return $this->fail("请稍后再试!"); try { $give = 0; foreach ($this->fetchRechargeOption() as $item) { if ($amount >= $item["amount"]) $give = $item["give"]; } $no = "RBU" . time() . rand(10000, 99999); $log = [ "no" => $no, "user_id" => $user_id, "platform" => $payment_type, "type" => null, "value" => json_encode([ "user_id" => $user_id, "amount" => $amount, "give" => $give ]), "reason" => null, "status" => \E_BASE_STATUS::Default, "relation_no" => $no, "createtime" => time(), "updatetime" => time() ]; if (\E_ORDER_PAY_TYPE::Wechat === $payment_type) { $openid = null; if ($platform == "app") $openid = $user["app_openid"]; if ($platform == "web") $openid = $user["web_openid"]; if ($platform == "applet") $openid = $user["applet_openid"]; if (is_null($openid) || mb_strlen($openid) === 0) throw new Exception("请先绑定微信再进行支付!"); $user = $this->userModel->findById($user_id); if (!$user) throw new Exception("用户异常!"); $res = $this->thirdPayService->payWechat($platform, $openid, $log["no"], $amount, "充值余额"); $log["type"] = \E_USER_BILL_CHANGE_TYPE::BalanceWxRecharge[0]; $log["reason"] = \E_USER_BILL_CHANGE_TYPE::BalanceWxRecharge[1]; } else { $log["type"] = \E_USER_BILL_CHANGE_TYPE::BalanceAliRecharge[0]; $log["reason"] = \E_USER_BILL_CHANGE_TYPE::BalanceAliRecharge[1]; $res = $this->thirdPayService->payAli([]); } if ($res->code()) $this->thirdPayLogModel->save($log); return $res; } catch (Exception $e) { return $this->fail($e->getMessage()); } finally { $walletLock->unlock($wLock); } } // 余额支付 public function payBalance($order) { $walletLock = new RedLock(); $wLock = $walletLock->lock(Wallet::UWKey($order->user_id)); if (!is_array($wLock)) return $this->fail("请稍后再试"); $massager = (new \app\api\model\massager\Massager())->findById($order["massager_id"] ?? -1); if ($order["massager_id"] > 0) { if (!$massager) return $this->fail("助教不存在!"); } $wallet = $this->userWalletModel->getUserWallet($order->user_id); $change = $order->total_real_amount + $order->membership_amount; $w_money = $wallet->money; $w_g_money = $wallet->give_money; $w_total_money = $w_money + $w_g_money; $membership_no = "RBM" . time() . rand(10000, 99999); if ($order["membership_config_id"] > 0 && $order["membership_amount"] > 0) { $res = $this->thirdPayLogModel->save([ "no" => $membership_no, "user_id" => $order["user_id"], "platform" => $order["pay_platform"], "type" => \E_USER_BILL_CHANGE_TYPE::MemberCharge[0], "reason" => \E_USER_BILL_CHANGE_TYPE::MemberCharge[1], "value" => null, "membership_config_id" => $order["membership_config_id"], "amount" => $order["membership_amount"], "status" => \E_BASE_STATUS::Default, "relation_no" => $order["no"], "createtime" => time(), "updatetime" => time() ]); if (!$res) { return $this->fail("会员订单保存异常"); } } if ($w_total_money - $change < 0) return $this->fail("账户余额不足"); Db::startTrans(); try { if ($order["membership_config_id"] > 0 && $order["membership_amount"] > 0) { $r = (new CallbackService())->rechargeMembershipSuccess($membership_no); if (0 === $r->code()) { throw new Exception($r->msg()); } } $bill = [ "user_id" => $order->user_id, "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money, "change_type" => \E_USER_BILL_CHANGE_TYPE::BalanceOrderConsumption[0], "change" => -$change, "before" => $w_total_money, "after" => $w_total_money - $change, "reason" => \E_USER_BILL_CHANGE_TYPE::BalanceOrderConsumption[1], "money" => 0, "give_money" => 0, "relation_no" => $order->no, "createtime" => time() ]; $update = [ "score" => $wallet["score"] + $change, "updatetime" => time() ]; // 优先减扣充值的钱 if ($w_money - $change > 0) { $update["money"] = fixed2Float($w_money - $change); $bill["money"] = -$change; } else { $update["money"] = 0; $update["give_money"] = $w_g_money - ($change - $w_money); $bill["money"] = -$w_money; $bill["give_money"] = -($change - $w_money); } $this->userWalletModel->update($update, ["id" => $wallet->id]); $this->orderModel->update([ "status" => \E_ORDER_STATUS::Purchase, "balance_deduction" => 0, "pay_time" => time(), "updatetime" => time() ], ["id" => $order->id]); $this->userBillModel->insertAll([$bill, [ "user_id" => $order->user_id, "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Score, "change_type" => \E_USER_BILL_CHANGE_TYPE::ScoreOrderGive[0], "change" => $change, "before" => $wallet["score"], "after" => $wallet["score"] + $change, "reason" => \E_USER_BILL_CHANGE_TYPE::ScoreOrderGive[1], "money" => 0, "give_money" => 0, "relation_no" => $order->no, "createtime" => time() ]]); $progress = []; $types = $order["store_id"] > 0 ? [ \E_ORDER_PROGRESS_TYPE::Start, \E_ORDER_PROGRESS_TYPE::Over, ] : ALL_ORDER_PROGRESS_TYPE; if ($order["reorder"] == 1) { $types = [ \E_ORDER_PROGRESS_TYPE::Take, \E_ORDER_PROGRESS_TYPE::Over, ]; } foreach ($types as $key => $value) { $progress[] = [ "order_id" => $order->id, "order_no" => $order->no, "type" => $value, "clock_in" => 0, "index" => $key + 1, "clock_in_time" => null, "lng" => null, "lat" => null, "card_image" => null, "createtime" => time(), "updatetime" => time() ]; } (new Progress())->insertAll($progress); if ($order["massager_id"] > 0 && $massager["mobile"]) { $r = TencentCloudService::tencent_cloud_vms_send($massager["mobile"], "1478969", [$order["no"]]); Log::custom_log("Vms", [$r->code(), $r->msg(), $r->data()], "Vms"); } if ($order["voucher_id"] > 0) { $this->userVoucherModel->update([ "status" => "use" ], ["id" => $order["voucher_id"]]); } if ($order["store_id"] > 0) { StoreService::inform($order["no"], $order["store_id"], $order["city_code"], $order["service_start_date"]); } if ($massager) { Chat::init(1) ->massager([$massager["id"]]) ->send("您有新订单,请及时处理"); } Db::commit(); return $this->ok(true); } catch (Exception $e) { Db::rollback(); return $this->fail($e->getMessage()); } finally { UserService::updateGrouping($order["user_id"]); $walletLock->unlock($wLock); } } // 退款 public function refundByBalance($order, $order_status = \E_ORDER_STATUS::Reject): \SResult { if (!in_array($order["status"], [\E_ORDER_STATUS::Purchase, \E_ORDER_STATUS::Proceed, \E_ORDER_STATUS::WaitFeedback])) { return $this->fail("订单的状态不支持退款!"); } $wallet = $this->userWalletModel->getUserWallet($order->user_id); if (!$wallet) return $this->fail("钱包数据异常,无法取消订单!"); $walletLock = new RedLock(); $wLock = $walletLock->lock(Wallet::UWKey($order->user_id)); if (!is_array($wLock)) return $this->fail("请稍后再试"); $bill = $this->userBillModel->getBill($order->user_id, \E_USER_BILL_CURRENCY_TYPE::Money, \E_USER_BILL_CHANGE_TYPE::BalanceOrderConsumption[0], $order->no); if (!$bill || (0 == $bill->money && 0 == $bill->give_money)) return $this->fail("账单数据异常,无法取消订单!"); $trip_amount = 0; if (\E_ORDER_STATUS::Purchase !== $order["status"]) { $trip_amount = $order["trip_amount"]; } // 订单实际支付总额 - 出行费 出行费不退 $change = abs($bill->change) - $trip_amount; $money = abs($bill->money); $give_money = abs($bill->give_money); if ($money - $trip_amount > 0) { $money -= $trip_amount; } else { $money = 0; $give_money -= ($money + $trip_amount); } Db::startTrans(); try { $save_bill = [ "user_id" => $order->user_id, "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money, "change_type" => \E_USER_BILL_CHANGE_TYPE::BalanceOrderRefund[0], "change" => $change, "before" => ($wallet->money + $wallet->give_money), "after" => ($wallet->money + $wallet->give_money) + $change, "reason" => \E_USER_BILL_CHANGE_TYPE::BalanceOrderRefund[1], "money" => $money, "give_money" => $give_money, "relation_no" => $order->no, "createtime" => time() ]; $this->orderModel->update([ "status" => $order_status, "cancel_time" => time(), "updatetime" => time() ], ["id" => $order->id]); $this->userWalletModel->update([ "money" => $wallet->money + $money, "give_money" => $wallet->give_money + $give_money, "updatetime" => time() ], ["id" => $wallet->id]); if ($order["massager_id"] > 0 && $trip_amount > 0) { $mWalletModel = new \app\api\model\massager\Wallet(); // 出行费到账助教账户 $mWallet = $mWalletModel->getWallet($order["massager_id"]); $mWalletModel->where("id", $mWallet->id)->setInc("profit_amount", $trip_amount); (new \app\api\model\massager\Bill())->insert([ "massager_id" => $order["massager_id"], "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money, "change_type" => \E_M_BILL_CHANGE_TYPE::Travel, "change" => $trip_amount, "before" => fixed2Float($mWallet->profit_amount), "after" => fixed2Float($mWallet->profit_amount + $trip_amount), "reason" => "出行费用", "relation_no" => $order->no, "createtime" => time() ]); } // 优惠券|积分 退还 if ($order["voucher_id"] > 0) { $this->userVoucherModel->update(["status" => \E_BASE_STATUS::Normal], ["id" => $order["voucher_id"]]); } $this->userWalletModel->where("id", $wallet["id"])->setDec("score", $order["total_real_amount"]); $this->userBillModel->saveAll([ $save_bill, [ "user_id" => $order->user_id, "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Score, "change_type" => \E_USER_BILL_CHANGE_TYPE::ScoreOrderRefund[0], "change" => -$order["total_real_amount"], "before" => $wallet["score"], "after" => $wallet["score"] - $order["total_real_amount"], "reason" => \E_USER_BILL_CHANGE_TYPE::ScoreOrderRefund[1], "money" => 0, "give_money" => 0, "relation_no" => $order->no, "createtime" => time() ]]); Db::commit(); return $this->ok(true); } catch (Exception $e) { Db::rollback(); return $this->fail($e->getMessage()); } finally { UserService::updateGrouping($order["user_id"]); $walletLock->unlock($wLock); } } // 退单 public function chargeback($order, $identity_type, $chargeback_type) { if (!in_array($order["status"], [\E_ORDER_STATUS::Purchase, \E_ORDER_STATUS::Proceed, \E_ORDER_STATUS::WaitFeedback])) { return $this->fail("订单的状态不支持退款!"); } } // 获取账单 public function fetchBill($user_id, $currency_type, $change_types, $page = 1, $size = 10) { $paginate = $this->userBillModel->fetchBill($user_id, $currency_type, $change_types, $page, $size); return [ $paginate->items(), $paginate->total() ]; } public function delBills($user_id, array $bill_ids) { $this->userBillModel ->where("user_id", $user_id) ->where("id", "in", $bill_ids) ->update([ "is_del" => 1, ]); return $this->ok(true); } /** * 兑换优惠券 * @param $user_id * @param $voucher_id */ public function exchangeVoucher($user_id, $voucher_id) { $systemVoucher = (new \app\api\model\Voucher)->findById($voucher_id); if (!$systemVoucher || $systemVoucher->exchange_score <= 0) return $this->fail("该优惠券不可兑换"); $walletLock = new RedLock(); $wLock = $walletLock->lock(Wallet::UWKey($user_id)); if (!is_array($wLock)) return $this->fail("请稍后再试"); $wallet = $this->userWalletModel->getUserWallet($user_id); if ($wallet->score - $systemVoucher->exchange_score < 0) return $this->fail("账户积分余额不足"); Db::startTrans(); try { $this->userWalletModel->where("id", $wallet->id)->setDec("score", $systemVoucher->exchange_score); $this->userBillModel->save([ "user_id" => $user_id, "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Score, "change_type" => \E_USER_BILL_CHANGE_TYPE::ScoreExchangeVoucher[0], "change" => -$systemVoucher->exchange_score, "before" => $wallet->score, "after" => $wallet->score - $systemVoucher->exchange_score, "reason" => \E_USER_BILL_CHANGE_TYPE::ScoreExchangeVoucher[1], "createtime" => time() ]); $this->userVoucherModel->save([ "user_id" => $user_id, "voucher_id" => $voucher_id, "takeAmount" => $systemVoucher->takeAmount, "fullAmount" => $systemVoucher->fullAmount, "status" => \E_BASE_STATUS::Normal, "expiretime" => time() + (24 * 60 * 60) * $systemVoucher->indate_day, "createtime" => time(), "updatetime" => time() ]); Db::commit(); return $this->ok(true); } catch (Exception $e) { Db::rollback(); return $this->fail($e->getMessage()); } finally { $walletLock->unlock($wLock); } } public function deposit($user_id, $platform, $amount) { $user = $this->userModel->get($user_id); if (!$user) return $this->fail("用户不存在"); if ($platform == \E_ORDER_PAY_TYPE::Wechat) return $this->fail("微信提现功能暂未审核通过!"); if ($platform == \E_ORDER_PAY_TYPE::Wechat) { if (null == $user["applet_openid"]) return $this->fail("未绑定微信,无法发起提现!", 10001); } else if ($platform == \E_ORDER_PAY_TYPE::Bank) { if (mb_strlen($user["opening_bank_name"] ?? '') == 0 || mb_strlen($user["bank_real_name"] ?? '') == 0 || mb_strlen($user["bank_no"] ?? '') == 0 ) return $this->fail("未绑定银行卡信息,无法发起提现", 10002); } $wallet = $this->userWalletModel->getUserWallet($user_id); $deposit_min_amount = config("site.user_deposit_min_amount"); if ($wallet["money"] < $amount) return $this->fail("数额不足,无法发起提现"); if ($amount < $deposit_min_amount) return $this->fail("提现最小金额为:{$deposit_min_amount}"); $record = (new Record())->where([ "apply_status" => \E_BASE_STATUS::Default, "deposit_status" => \E_BASE_STATUS::Default, "identity_type" => \E_IDENTITY_TYPE::User, "user_id" => $user_id ])->find(); if ($record) { return $this->fail("您上一笔提现未通过审核,等待上一笔提现后再次发起"); } (new Record())->save([ "no" => "TXU" . $user_id . time(), "platform" => $platform, "identity_type" => \E_IDENTITY_TYPE::User, "user_id" => $user_id, "deposit_amount" => $amount, "service_charge_rate" => config("site.service_charge_rate"), "apply_status" => \E_BASE_STATUS::Default, "deposit_status" => \E_BASE_STATUS::Default, "operation_id" => $user_id, "createtime" => time(), "updatetime" => time(), "opening_bank_name" => $user["opening_bank_name"], "bank_real_name" => $user["bank_real_name"], "bank_no" => $user["bank_no"], ]); return $this->ok(true, "申请提现成功,请耐心等待管理员审核!"); } }