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(OrderResources::collection($orders)); } public function detail(Request $request) { $request->validate([ 'trade_no' => 'required|string', ]); $order = Order::with('payment') ->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['plan'] = Plan::find($order->plan_id); $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(OrderResources::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); // Validate plan purchase $planService->validatePurchase($user, $request->input('period')); return DB::transaction(function () use ($request, $plan, $user, $userService, $planService) { $period = $request->input('period'); $newPeriod = PlanService::getPeriodKey($period); // Create order $order = new Order([ 'user_id' => $user->id, 'plan_id' => $plan->id, 'period' => $newPeriod, 'trade_no' => Helper::generateOrderNo(), 'total_amount' => optional($plan->prices)[$newPeriod] * 100 ]); // Apply coupon if provided if ($request->input('coupon_code')) { $this->applyCoupon($order, $request->input('coupon_code')); } // Set order attributes $orderService = new OrderService($order); $orderService->setVipDiscount($user); $orderService->setOrderType($user); $orderService->setInvite($user); // Handle user balance if ($user->balance && $order->total_amount > 0) { $this->handleUserBalance($order, $user, $userService); } if (!$order->save()) { throw new ApiException(__('Failed to create order')); } 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 !== 1) 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 = 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() { $methods = Payment::select([ 'id', 'name', 'payment', 'icon', 'handling_fee_fixed', 'handling_fee_percent' ]) ->where('enable', 1) ->orderBy('sort', 'ASC') ->get(); 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); } }