fuc-new/src/Services/Payment.php
2025-10-13 19:47:40 +08:00

480 lines
14 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace App\Services;
use App\Utils\ClassHelper;
use Psr\Http\Message\ResponseInterface;
use voku\helper\AntiXSS;
use App\Models\Invoice;
use App\Models\Order;
use App\Models\PlanPending;
use App\Models\Product;
use App\Models\UserMoneyLog;
use App\Models\UserCoupon;
use App\Services\Auth;
final class Payment
{
public static function getAllPaymentMap(): array
{
$payments = [];
$helper = new ClassHelper();
$class_list = $helper->getClassesByNamespace('\\App\\Services\\Gateway\\');
foreach ($class_list as $class) {
if (get_parent_class($class) === 'App\\Services\\Gateway\\Base') {
$payments[] = $class;
}
}
return $payments;
}
public static function getPaymentsEnabled(): array
{
return array_values(array_filter(Payment::getAllPaymentMap(), static function ($payment) {
return $payment::_enable();
}));
}
public static function getPaymentMap(): array
{
$result = [];
foreach (self::getPaymentsEnabled() as $payment) {
$result[$payment::_name()] = $payment;
}
return $result;
}
public static function getPaymentByName($name)
{
$all = self::getPaymentMap();
return $all[$name];
}
public static function notify($request, $response, $args)
{
$payment = self::getPaymentByName($args['type']);
if ($payment !== null) {
$instance = new $payment();
return $instance->notify($request, $response, $args);
}
return $response->withStatus(404);
}
public static function returnHTML($request, $response, $args): string|ResponseInterface
{
$payment = self::getPaymentByName($args['type']);
if ($payment !== null) {
$instance = new $payment();
return $instance->getReturnHTML($request, $response, $args);
}
return '';
}
public static function getStatus($request, $response, $args)
{
$payment = self::getPaymentByName($args['type']);
if ($payment !== null) {
$instance = new $payment();
return $instance->getStatus($request, $response, $args);
}
return $response->withStatus(404);
}
public static function payBalance($oThisUser, $oThisInvoice)
{
if ($oThisInvoice === null) {
return [
'ret' => 0,
'msg' => '账单不存在',
];
}
if ($oThisUser->is_shadow_banned) {
return [
'ret' => 0,
'msg' => '支付失败,请稍后再试',
];
}
if ($oThisUser->money < $oThisInvoice->price) {
return [
'ret' => 0,
'msg' => '余额不足',
];
}
$money_before = $oThisUser->money;
$oThisUser->money -= $oThisInvoice->price;
$oThisUser->save();
(new UserMoneyLog())->add(
$oThisUser->id,
$money_before,
(float) $oThisUser->money,
-$oThisInvoice->price,
'购买套餐 #' . $oThisInvoice->id
);
$oThisInvoice->status = 'paid_balance';
$oThisInvoice->update_time = time();
$oThisInvoice->pay_time = time();
$oThisInvoice->money_use = $oThisInvoice->price;
$oThisInvoice->save();
return [
'ret' => 1,
'msg' => '支付成功',
];
}
public static function code($request, $response, $args)
{
$oAntiXss = new AntiXSS();
$type_pay = "epay"; // epay, balance=本地钱包
$redir = $oAntiXss->xss_clean($request->getParam('redir'));
$type_onlinePay = $oAntiXss->xss_clean($request->getParam('payType'));
$iAmount = $oAntiXss->xss_clean($request->getParam('amount') ?? 0);
$iAmount = (int) $iAmount;
if ($iAmount < 1) {
return $response->withJson([
'ret' => 0,
'msg' => '金额不能小于1',
]);
}
$user = Auth::getUser();
$order = new Order();
$order->user_id = $user->id;
$order->product_code = "code";
$order->user_product_name = "钱包充值";
$order->user_price = $iAmount;
// $order->user_coupon = "";
$order->status = 'payment_pending';
$order->create_time = time();
$order->update_time = time();
$order->save();
$invoice = new Invoice();
$invoice->user_id = $user->id;
$invoice->order_id = $order->id;
// $invoice->content = json_encode($invoice_content);
$invoice->price = $iAmount;
$invoice->status = 'unpaid';
$invoice->create_time = time();
$invoice->update_time = time();
$invoice->pay_time = 0;
// $invoice->set = $iSet; // 新增
// $invoice->money_use = $iYuezhifu;
$invoice->save();
$args["true_price"] = $iAmount;
$args["true_invoice_id"] = $invoice->id;
$args["type"] = $type_onlinePay;
$args["redir"] = $redir;
$payment = self::getPaymentByName($type_pay);
$instance = new $payment();
return $instance->purchase($request, $response, $args);
// return $response->withJson([
// 'ret' => 1,
// 'msg' => $iAmount,
// ]);
}
public static function purchase($request, $response, $args)
{
$antiXss = new AntiXSS();
$type_pay = $args["type"]; // epay, balance=本地钱包
$coupon_raw = $antiXss->xss_clean($request->getParam('coupon'));
$product_id = $antiXss->xss_clean($request->getParam('shopid'));
// 暂时EPay用
$type_onlinePay = $antiXss->xss_clean($request->getParam('type'));
$redir = $antiXss->xss_clean($request->getParam('redir'));
$user = Auth::getUser();
//test
// tomd("type_pay:".$type_pay);
// tomd("coupon_raw:".$coupon_raw);
// tomd("product_id:".$product_id);
// tomd("autorenew:".$autorenew);
// tomd("type_onlinePay:".$type_onlinePay);
// tomd("redir:".$redir);
// exit;
////// 先创建订单
$product = (new Product())->find($product_id);
if ($product === null || $product->stock === 0) {
return $response->withJson([
'ret' => 0,
'msg' => '商品不存在或库存不足',
]);
}
if ($product->end_time != 0 && $product->end_time < time()) {
return $response->withJson([
'ret' => 0,
'msg' => '商品已过期',
]);
}
// 此时,商品原价
$true_price = $product->price;
// 用户等级折扣最先算
$iUserVipRate = $user->getVipRate();
$true_price = $true_price * ((100 - $iUserVipRate) / 100);
if ($user->is_shadow_banned) {
return $response->withJson([
'ret' => 0,
'msg' => '商品不存在或库存不足',
]);
}
if ($coupon_raw !== '') {
$aPayOrderStatus = ["activated", "pending", "expired"];
$coupon = (new UserCoupon())->where('code', $coupon_raw)->first();
if ($coupon === null || ($coupon->expire_time !== 0 && $coupon->expire_time < time())) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码不存在或已过期',
]);
}
$coupon_limit = json_decode($coupon->limit);
if ($coupon_limit->disabled) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码已被禁用',
]);
}
if ($product->coupon_disable == 1) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码不适用于此商品',
]);
}
if ($coupon_limit->product_id !== '' && ! in_array($product_id, explode(',', $coupon_limit->product_id))) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码不适用于此商品',
]);
}
$coupon_use_limit = $coupon_limit->use_time;
if ($coupon_use_limit > 0) {
$user_use_count = (new Order())->where('user_id', $user->id)->where('user_coupon', $coupon->code)->whereIn("status", $aPayOrderStatus)->count();
if ($user_use_count >= $coupon_use_limit) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码使用次数已达上限',
]);
}
}
if (property_exists($coupon_limit, 'total_use_time')) {
$coupon_total_use_limit = $coupon_limit->total_use_time;
} else {
$coupon_total_use_limit = -1;
}
if ($coupon_total_use_limit > 0 && $coupon->use_count >= $coupon_total_use_limit) {
return $response->withJson([
'ret' => 0,
'msg' => '优惠码使用次数已达上限',
]);
}
$content = json_decode($coupon->content);
if ($content->type === 'percentage') {
$discount = $true_price * $content->value / 100;
} else {
$discount = $content->value;
}
$true_price = $true_price - $discount;
}
$product_limit = json_decode($product->limit);
if ($product_limit->class_required !== '' && $user->class < (int) $product_limit->class_required) {
return $response->withJson([
'ret' => 0,
'msg' => '您的账户等级不足,无法购买此商品',
]);
}
if ($product_limit->node_group_required !== ''
&& $user->node_group !== (int) $product_limit->node_group_required) {
return $response->withJson([
'ret' => 0,
'msg' => '您所在的用户组无法购买此商品',
]);
}
if ($product_limit->new_user_required !== 0) {
$order_count = (new Order())->where('user_id', $user->id)->whereIn("status", $aPayOrderStatus)->count();
if ($order_count > 0) {
return $response->withJson([
'ret' => 0,
'msg' => '此商品仅限新用户购买',
]);
}
}
// 最后余额抵扣
$iYuezhifu = 0;
if ($user->money > 0) { // 如果有余额
$iRengxuzhifu = $true_price - $user->money; // 仍需支付
if ($iRengxuzhifu <= 0) { // 余额能覆盖支付额,强制走钱包
$type_pay = "balance";
} else { // 余额抵付
$iYuezhifu = $user->money;
}
}
$iSet = $user->havePayClass() ? 2 : 1;
if ($product->name_sub) {
$sProductName = $product->name." - ".$product->name_sub;
} else {
$sProductName = $product->name;
}
$order = new Order();
$order->user_id = $user->id;
$order->product_code = $product->code;
$order->user_product_name = $sProductName;
$order->user_price = $true_price;
$order->user_coupon = $coupon_raw;
$order->status = 'payment_pending';
$order->create_time = time();
$order->update_time = time();
$order->save();
// $invoice_content = [];
// $invoice_content[] = [
// 'content_id' => 0,
// 'name' => $product->name,
// 'price' => $product->price,
// ];
//
// if ($coupon_raw !== '') {
// $invoice_content[] = [
// 'content_id' => 1,
// 'name' => '优惠码 ' . $coupon_raw,
// 'price' => '-' . $discount,
// ];
// }
$invoice = new Invoice();
$invoice->user_id = $user->id;
$invoice->order_id = $order->id;
// $invoice->content = json_encode($invoice_content);
$invoice->price = $true_price;
$invoice->status = 'unpaid';
$invoice->create_time = time();
$invoice->update_time = time();
$invoice->pay_time = 0;
// $invoice->set = $iSet; // 新增
$invoice->money_use = $iYuezhifu;
$invoice->save();
if ($type_pay !== "balance" && $iYuezhifu > 0) {
$user->money = 0;
$user->save();
(new UserMoneyLog())->add(
$user->id,
$iYuezhifu,
(float) $user->money,
-$iYuezhifu,
'余额抵付 #账单号:'.$invoice->id
);
}
// if ($product->stock > 0) {
// $product->stock -= 1;
// }
// $product->sale_count += 1;
// $product->save();
if ($coupon_raw !== '') {
$coupon->use_count += 1;
$coupon->save();
}
////// end //////////
// 如果是钱包支付,拦截
if ($type_pay == "balance") {
$result = self::payBalance($user, $invoice);
return $response->withJson($result);
}
$payment = self::getPaymentByName($type_pay);
if ($payment !== null) {
// if ($type_pay == "epay") {
$args["true_price"] = $true_price;
$args["true_invoice_id"] = $invoice->id;
$args["type"] = $type_onlinePay;
$args["redir"] = $redir;
// }
$instance = new $payment();
return $instance->purchase($request, $response, $args);
}
return $response->withStatus(404);
}
}