421844895e
Filter user-visible payment methods to only include supported providers and fail fast when a payment record or plugin cannot be resolved. This prevents invalid payment options from being returned by the API and avoids constructing an undefined fallback payment class.
218 lines
7.4 KiB
PHP
Executable File
218 lines
7.4 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Http\Controllers\V1\User;
|
|
|
|
use App\Exceptions\ApiException;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\User\OrderSave;
|
|
use App\Http\Resources\OrderResource;
|
|
use App\Models\Order;
|
|
use App\Models\Payment;
|
|
use App\Models\Plan;
|
|
use App\Models\User;
|
|
use App\Services\CouponService;
|
|
use App\Services\OrderService;
|
|
use App\Services\PaymentService;
|
|
use App\Services\PlanService;
|
|
use App\Services\UserService;
|
|
use Illuminate\Http\Request;
|
|
|
|
class OrderController extends Controller
|
|
{
|
|
public function fetch(Request $request)
|
|
{
|
|
$request->validate([
|
|
'status' => 'nullable|integer|in:0,1,2,3',
|
|
]);
|
|
$orders = Order::with('plan')
|
|
->where('user_id', $request->user()->id)
|
|
->when($request->input('status') !== null, function ($query) use ($request) {
|
|
$query->where('status', $request->input('status'));
|
|
})
|
|
->orderBy('created_at', 'DESC')
|
|
->get();
|
|
|
|
return $this->success(OrderResource::collection($orders));
|
|
}
|
|
|
|
public function detail(Request $request)
|
|
{
|
|
$request->validate([
|
|
'trade_no' => 'required|string',
|
|
]);
|
|
$order = Order::with(['payment', 'plan'])
|
|
->where('user_id', $request->user()->id)
|
|
->where('trade_no', $request->input('trade_no'))
|
|
->first();
|
|
if (!$order) {
|
|
return $this->fail([400, __('Order does not exist or has been paid')]);
|
|
}
|
|
$order['try_out_plan_id'] = (int) admin_setting('try_out_plan_id');
|
|
if (!$order->plan) {
|
|
return $this->fail([400, __('Subscription plan does not exist')]);
|
|
}
|
|
if ($order->surplus_order_ids) {
|
|
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
|
|
}
|
|
return $this->success(OrderResource::make($order));
|
|
}
|
|
|
|
public function save(OrderSave $request)
|
|
{
|
|
$request->validate([
|
|
'plan_id' => 'required|exists:App\Models\Plan,id',
|
|
'period' => 'required|string'
|
|
]);
|
|
|
|
$user = User::findOrFail($request->user()->id);
|
|
$userService = app(UserService::class);
|
|
|
|
if ($userService->isNotCompleteOrderByUserId($user->id)) {
|
|
throw new ApiException(__('You have an unpaid or pending order, please try again later or cancel it'));
|
|
}
|
|
|
|
$plan = Plan::findOrFail($request->input('plan_id'));
|
|
$planService = new PlanService($plan);
|
|
|
|
$planService->validatePurchase($user, $request->input('period'));
|
|
|
|
$order = OrderService::createFromRequest(
|
|
$user,
|
|
$plan,
|
|
$request->input('period'),
|
|
$request->input('coupon_code')
|
|
);
|
|
|
|
return $this->success($order->trade_no);
|
|
}
|
|
|
|
protected function applyCoupon(Order $order, string $couponCode): void
|
|
{
|
|
$couponService = new CouponService($couponCode);
|
|
if (!$couponService->use($order)) {
|
|
throw new ApiException(__('Coupon failed'));
|
|
}
|
|
$order->coupon_id = $couponService->getId();
|
|
}
|
|
|
|
protected function handleUserBalance(Order $order, User $user, UserService $userService): void
|
|
{
|
|
$remainingBalance = $user->balance - $order->total_amount;
|
|
|
|
if ($remainingBalance > 0) {
|
|
if (!$userService->addBalance($order->user_id, -$order->total_amount)) {
|
|
throw new ApiException(__('Insufficient balance'));
|
|
}
|
|
$order->balance_amount = $order->total_amount;
|
|
$order->total_amount = 0;
|
|
} else {
|
|
if (!$userService->addBalance($order->user_id, -$user->balance)) {
|
|
throw new ApiException(__('Insufficient balance'));
|
|
}
|
|
$order->balance_amount = $user->balance;
|
|
$order->total_amount = $order->total_amount - $user->balance;
|
|
}
|
|
}
|
|
|
|
public function checkout(Request $request)
|
|
{
|
|
$tradeNo = $request->input('trade_no');
|
|
$method = $request->input('method');
|
|
$order = Order::where('trade_no', $tradeNo)
|
|
->where('user_id', $request->user()->id)
|
|
->where('status', 0)
|
|
->first();
|
|
if (!$order) {
|
|
return $this->fail([400, __('Order does not exist or has been paid')]);
|
|
}
|
|
// free process
|
|
if ($order->total_amount <= 0) {
|
|
$orderService = new OrderService($order);
|
|
if (!$orderService->paid($order->trade_no))
|
|
return $this->fail([400, '支付失败']);
|
|
return response([
|
|
'type' => -1,
|
|
'data' => true
|
|
]);
|
|
}
|
|
$payment = Payment::find($method);
|
|
if (!$payment || !$payment->enable) {
|
|
return $this->fail([400, __('Payment method is not available')]);
|
|
}
|
|
$paymentService = new PaymentService($payment->payment, $payment->id);
|
|
$order->handling_amount = NULL;
|
|
if ($payment->handling_fee_fixed || $payment->handling_fee_percent) {
|
|
$order->handling_amount = (int) round(($order->total_amount * ($payment->handling_fee_percent / 100)) + $payment->handling_fee_fixed);
|
|
}
|
|
$order->payment_id = $method;
|
|
if (!$order->save())
|
|
return $this->fail([400, __('Request failed, please try again later')]);
|
|
$result = $paymentService->pay([
|
|
'trade_no' => $tradeNo,
|
|
'total_amount' => isset($order->handling_amount) ? ($order->total_amount + $order->handling_amount) : $order->total_amount,
|
|
'user_id' => $order->user_id,
|
|
'stripe_token' => $request->input('token')
|
|
]);
|
|
return response([
|
|
'type' => $result['type'],
|
|
'data' => $result['data']
|
|
]);
|
|
}
|
|
|
|
public function check(Request $request)
|
|
{
|
|
$tradeNo = $request->input('trade_no');
|
|
$order = Order::where('trade_no', $tradeNo)
|
|
->where('user_id', $request->user()->id)
|
|
->first();
|
|
if (!$order) {
|
|
return $this->fail([400, __('Order does not exist')]);
|
|
}
|
|
return $this->success($order->status);
|
|
}
|
|
|
|
public function getPaymentMethod()
|
|
{
|
|
$availableMethods = array_flip(PaymentService::getAllPaymentMethodNames());
|
|
|
|
$methods = Payment::select([
|
|
'id',
|
|
'name',
|
|
'payment',
|
|
'icon',
|
|
'handling_fee_fixed',
|
|
'handling_fee_percent'
|
|
])
|
|
->where('enable', 1)
|
|
->orderBy('sort', 'ASC')
|
|
->get()
|
|
->filter(function ($payment) use ($availableMethods) {
|
|
return isset($availableMethods[$payment->payment]);
|
|
})
|
|
->values();
|
|
|
|
return $this->success($methods);
|
|
}
|
|
|
|
public function cancel(Request $request)
|
|
{
|
|
if (empty($request->input('trade_no'))) {
|
|
return $this->fail([422, __('Invalid parameter')]);
|
|
}
|
|
$order = Order::where('trade_no', $request->input('trade_no'))
|
|
->where('user_id', $request->user()->id)
|
|
->first();
|
|
if (!$order) {
|
|
return $this->fail([400, __('Order does not exist')]);
|
|
}
|
|
if ($order->status !== 0) {
|
|
return $this->fail([400, __('You can only cancel pending orders')]);
|
|
}
|
|
$orderService = new OrderService($order);
|
|
if (!$orderService->cancel()) {
|
|
return $this->fail([400, __('Cancel failed')]);
|
|
}
|
|
return $this->success(true);
|
|
}
|
|
}
|