PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Http / Controllers / FrontendControllers / CustomerOrderController.php

CustomerOrderController.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.5, at app/Http/Controllers/FrontendControllers/CustomerOrderController.php

613 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Http\Controllers\FrontendControllers;
4
5 //use FluentCart\Api\Resource\FrontendResource\CustomerResource;
6 use FluentCart\Api\Resource\CustomerAddressResource;
7 use FluentCart\Api\Resource\CustomerResource;
8 use FluentCart\Api\Resource\OrderDownloadPermissionResource;
9 use FluentCart\App\App;
10 use FluentCart\App\Helpers\Helper;
11 use FluentCart\App\Helpers\Status;
12 use FluentCart\App\Http\Requests\FrontendRequests\CustomerRequests\CustomerProfileAccountDetailsRequest;
13 use FluentCart\App\Http\Requests\FrontendRequests\CustomerRequests\CustomerProfileRequest;
14 use FluentCart\App\Models\Customer;
15 use FluentCart\App\Models\Meta;
16 use FluentCart\App\Models\Order;
17 use FluentCart\App\Models\OrderAddress;
18 use FluentCart\App\Models\OrderItem;
19 use FluentCart\App\Models\OrderMeta;
20 use FluentCart\App\Models\OrderTransaction;
21 use FluentCart\App\Models\ProductDownload;
22 use FluentCart\App\Models\ProductVariation;
23 use FluentCart\App\Models\Subscription;
24 use FluentCart\App\Modules\Subscriptions\Services\SystemChargeService;
25 use FluentCart\App\Services\FileSystem\FileManager;
26 use FluentCart\App\Services\Localization\LocalizationManager;
27 use FluentCart\App\Services\OrderService;
28 use FluentCart\App\Services\Payments\PaymentHelper;
29 use FluentCart\App\Services\Renderer\Receipt\TaxSummaryHelper;
30 use FluentCart\Framework\Database\Orm\Builder;
31 use FluentCart\Framework\Database\Orm\Relations\HasMany;
32 use FluentCart\Framework\Http\Request\Request;
33 use FluentCart\Framework\Support\Arr;
34 use FluentCart\Framework\Support\Collection;
35 use FluentCart\Framework\Validator\Validator;
36 use FluentCartPro\App\Modules\Licensing\Models\License;
37 use FluentCartPro\App\Modules\Licensing\Models\LicenseActivation;
38 use FluentCartPro\App\Modules\Licensing\Services\LicenseHelper;
39
40
41 class CustomerOrderController extends BaseFrontendController
42 {
43
44 public function getOrders(Request $request): \WP_REST_Response
45 {
46
47 $customer = CustomerResource::getCurrentCustomer();
48
49 if (!$customer) {
50 return $this->sendSuccess([
51 'orders' => [
52 'data' => [],
53 'total' => 0,
54 'per_page' => 10,
55 'current_page' => 1,
56 'last_page' => 1
57 ]
58 ]);
59 }
60
61 $perPage = (int)$request->get('per_page', 10);
62 $page = (int)$request->get('page', 1);
63 $search = $request->getSafe('search', 'sanitize_text_field');
64
65 $orders = Order::query()
66 ->select(['invoice_no', 'id', 'parent_id', 'total_amount', 'fee_total', 'currency', 'uuid', 'type', 'status', 'created_at'])
67 ->with(['order_items' => function ($query) {
68 $query->select('id', 'order_id', 'object_id', 'post_title', 'title', 'quantity', 'payment_type', 'line_meta', 'other_info');
69 }])
70 ->where('customer_id', $customer->id)
71 ->where(function ($query) {
72 $query
73 ->where(function ($query) {
74 $query->where('parent_id', '')->orWhereNull('parent_id');
75 })
76 ->orWhere('type', '!=', 'renewal');
77 })
78 ->searchBy($search)
79 ->withCount('renewals')
80 ->orderBy('created_at', 'DESC')
81 ->paginate($perPage, ['*'], 'page', $page);
82
83 $orders->transform(function ($order) {
84 return [
85 'created_at' => $order->created_at->format('Y-m-d H:i:s'),
86 'invoice_no' => $order->invoice_no,
87 'total_amount' => $order->total_amount,
88 'currency' => $order->currency,
89 'uuid' => $order->uuid,
90 'type' => $order->type,
91 'status' => $order->status,
92 'renewals_count' => $order->renewals_count,
93 'order_items' => $order->order_items->map(function ($item) {
94 return [
95 'id' => $item->id,
96 'post_title' => $item->post_title,
97 'title' => $item->title,
98 'variation_display_title' => $item->variation_display_title,
99 'quantity' => $item->quantity,
100 'payment_type' => $item->payment_type,
101 'line_meta' => [
102 'bundle_parent_item_id' => Arr::get($item, 'line_meta.bundle_parent_item_id', null),
103 ]
104 ];
105 }),
106 ];
107 });
108
109 return $this->sendSuccess([
110 'orders' => $orders
111 ]);
112 }
113
114 public function orderDetails($order_uuid): \WP_REST_Response
115 {
116 // Call the method and store the response
117 $errorResponse = $this->checkUserLoggedIn();
118
119 // Check if there is an error response and return it if exists
120 if ($errorResponse !== null) {
121 // Return error if user is not logged in
122 return $errorResponse;
123 }
124
125 $customer = CustomerResource::getCurrentCustomer();
126
127 if (!$customer) {
128 return $this->sendError([
129 'message' => __('Customer not found', 'fluent-cart')
130 ]);
131 }
132
133 $order = Order::query()
134 ->where('uuid', $order_uuid)
135 ->where('customer_id', $customer->id)
136 ->with(['customer', 'transactions', 'shipping_address', 'billing_address', 'orderTaxRates.tax_rate'])
137 ->with(['order_items' => function ($query) {
138 $query->with([
139 'variantImages',
140 'product' => function ($productQuery) {
141 $productQuery->addAppends(['view_url']);
142 }
143 ]);
144 }])
145 ->first();
146
147 if (!$order) {
148 return $this->sendError([
149 'message' => __('Order not found', 'fluent-cart')
150 ]);
151 }
152
153 if ($order->type == Status::ORDER_TYPE_RENEWAL) {
154 // We will redirect to the parent order details
155 $parentOrder = Order::query()
156 ->where('id', $order->parent_id)
157 ->first();
158
159 if ($parentOrder && $parentOrder->type == Status::ORDER_TYPE_SUBSCRIPTION) {
160 return $this->sendError([
161 'message' => __('This is a renewal order. Please check the parent order details.', 'fluent-cart'),
162 'parent_order' => [
163 'uuid' => $parentOrder->uuid,
164 ]
165 ]);
166 }
167 }
168
169 $orderItems = [];
170 $variationIds = [];
171 $productIds = [];
172
173 foreach ($order->order_items as $item) {
174 if ($item->payment_type === 'signup_fee') {
175 $orderItems[] = [
176 'id' => $item->id,
177 'payment_type' => $item->payment_type,
178 'object_id' => $item->object_id,
179 'line_meta' => $item->line_meta,
180 ];
181 continue;
182 }
183
184 // Fee items: include with minimal data for frontend display
185 if ($item->payment_type === 'fee') {
186 $orderItems[] = [
187 'id' => $item->id,
188 'post_title' => '',
189 'title' => $item->title,
190 'quantity' => 1,
191 'unit_price' => $item->unit_price,
192 'subtotal' => $item->subtotal,
193 'payment_type' => 'fee',
194 'meta_lines' => [],
195 'extra_amount' => 0,
196 'image' => '',
197 'variant_image' => '',
198 'url' => '',
199 'line_meta' => [],
200 ];
201 continue;
202 }
203
204 $metaLines = [];
205 $extraAmount = 0;
206
207 if ($item->payment_type == 'subscription' && $signupFee = Arr::get($item->other_info, 'signup_fee')) {
208 $metaLines[] = [
209 'label' => Arr::get($item->other_info, 'signup_fee_name', __('Signup Fee', 'fluent-cart')),
210 'value' => html_entity_decode(Helper::toDecimal($signupFee, true, $order->currency))
211 ];
212 $extraAmount = (int)$signupFee;
213 }
214
215 $orderItems[] = [
216 'variation_id' => $item->object_id,
217 'product_id' => $item->post_id,
218 'post_title' => $item->post_title,
219 'title' => $item->title,
220 'variation_display_title' => $item->variation_display_title,
221 'quantity' => $item->quantity,
222 'unit_price' => $item->unit_price,
223 'subtotal' => $item->subtotal,
224 'payment_type' => $item->payment_type,
225 'meta_lines' => $metaLines,
226 'extra_amount' => $extraAmount,
227 'image' => Arr::get($item, 'productImage.meta_value.0.url', ''),
228 'variant_image' => Arr::get($item, 'variantImages.meta_value.0.url', ''),
229 'url' => $item->is_custom
230 ? ($item->view_url ?? '') : ($item->product->view_url ?? ''),
231 'line_meta' => $item->line_meta,
232 'id' => $item->id,
233 'coupon_discount' => (int) $item->coupon_discount,
234 'discount_total' => (int) $item->discount_total,
235 'line_total' => (int) $item->line_total,
236
237 ];
238 $variationIds[] = $item->object_id;
239 $productIds[] = $item->post_id;
240 }
241
242 $upgradableVariationIds = [];
243 $isUpgradeEligibleOrder = in_array($order->payment_status, [
244 Status::PAYMENT_PAID,
245 Status::PAYMENT_PARTIALLY_PAID,
246 Status::PAYMENT_PARTIALLY_REFUNDED
247 ]);
248
249 if ($isUpgradeEligibleOrder && $variationIds) {
250 $upgradableVariationIds = Meta::query()
251 ->where('meta_key', 'variant_upgrade_path')
252 ->where('object_type', 'variant_upgrade')
253 ->whereIn('object_id', $variationIds)
254 ->pluck('object_id')
255 ->toArray();
256 }
257
258 foreach ($orderItems as &$orderItem) {
259 $orderItem['has_upgrade_paths'] = Arr::get($orderItem, 'payment_type') === 'onetime'
260 && !Arr::get($orderItem, 'line_meta.parent_item_id')
261 && in_array(Arr::get($orderItem, 'variation_id'), $upgradableVariationIds);
262 }
263 unset($orderItem);
264
265 $formattedOrderData = [
266 'fulfillment_type' => $order->fulfillment_type,
267 'type' => $order->type,
268 'created_at' => $order->created_at->format('Y-m-d H:i:s'),
269 'invoice_no' => $order->invoice_no,
270 'currency' => $order->currency,
271 'uuid' => $order->uuid,
272 'order_items' => $orderItems,
273 'status' => $order->status,
274 'payment_status' => $order->payment_status,
275 'shipping_status' => $order->shipping_status,
276 'fee_total' => $order->fee_total,
277
278 'billing_address_text' => $order->billing_address ? $order->billing_address->getAddressAsText(true, false) : '',
279 'shipping_address_text' => $order->shipping_address ? $order->shipping_address->getAddressAsText(true, false) : '',
280
281 'subtotal' => $order->subtotal,
282 'total_amount' => $order->total_amount,
283 'total_paid' => $order->total_paid,
284 'total_refund' => $order->total_refund,
285 'shipping_total' => $order->shipping_total,
286 'shipping_method_title' => (string) Arr::get($order->config, 'shipping_method_title', ''),
287 'coupon_discount_total' => $order->coupon_discount_total,
288 'manual_discount_total' => $order->manual_discount_total,
289 'prorate_credit' => (int) Arr::get($order->config, 'prorate_credit', 0),
290 'tax_total' => $order->tax_total,
291 'tax_behavior' => $order->tax_behavior,
292 'shipping_tax' => $order->shipping_tax,
293 'display_tax_lines' => $order->getDisplayTaxLines(),
294 'display_shipping_tax_lines' => $order->getDisplayShippingTaxLines(),
295 'is_reverse_charge_tax_order' => $order->isReverseChargeTaxOrder(),
296 'reverse_charge_price_mode' => $order->getOrderRcMode(),
297 'is_b2b_order' => $order->isB2BOrder(),
298 'tax_summary' => TaxSummaryHelper::computeTaxSummary($order),
299 'payment_method' => $order->payment_method,
300 'custom_payment_link' => $this->getCustomPaymentLink($order),
301 ];
302
303 $formattedOrderData['subscriptions'] = $order->subscriptions
304 ->filter(function ($subscription) {
305 return !in_array($subscription->status, [Status::SUBSCRIPTION_PENDING, Status::SUBSCRIPTION_INTENDED]);
306 })
307 ->map(function ($subscription) {
308 return OrderService::transformSubscription($subscription);
309 });
310
311 $formattedOrderData['downloads'] = ProductDownload::query()->whereIn('post_id', $productIds)->get()->filter(function ($download) use ($variationIds) {
312 $ids = $download->product_variation_id;
313 return empty($ids) || array_intersect($variationIds, $ids);
314 })->map(function ($download) use ($order) {
315 return [
316 'file_size' => $download->file_size,
317 'title' => $download->title,
318 'download_url' => Helper::generateDownloadFileLink(
319 $download,
320 $order->id
321 ),
322 ];
323 })->toArray();
324
325
326 // Let's find all the transactions related to this order
327 $orderIds = array_filter([$order->id, $order->parent_id]);
328 if ($order->type === Status::ORDER_TYPE_SUBSCRIPTION) {
329 $renewalOrderIds = Order::query()
330 ->where('parent_id', $order->id)
331 ->where('type', Status::ORDER_TYPE_RENEWAL)
332 ->get()->pluck('id')->toArray();
333
334 $orderIds = array_merge($orderIds, $renewalOrderIds);
335 $orderIds = array_values(array_unique($orderIds));
336 }
337
338 $transactions = OrderTransaction::query()
339 ->whereIn('order_id', $orderIds)
340 ->whereIn('status', [
341 Status::TRANSACTION_SUCCEEDED,
342 Status::TRANSACTION_REFUNDED,
343 Status::TRANSACTION_FAILED,
344 Status::TRANSACTION_PENDING,
345 ])
346 ->orderBy('id', 'DESC')
347 ->with(['order'])
348 ->get();
349
350 $formattedOrderData['transactions'] = $transactions->map(function ($transaction) {
351 $transformedTransaction = OrderService::transformTransaction($transaction);
352 if ($transaction->status === Status::TRANSACTION_PENDING
353 && !empty($transformedTransaction['custom_checkout_url'])
354 ) {
355 $transformedTransaction['show_pay_now'] = true;
356 }
357 return $transformedTransaction;
358 });
359
360 $formattedOrderData = apply_filters('fluent_cart/customer/order_data', $formattedOrderData, [
361 'order' => $order,
362 'customer' => $customer
363 ]);
364
365 $formattedOrderData['id'] = $order->id;
366
367 $hooksContents = apply_filters('fluent_cart/customer/order_details_section_parts', [
368 'before_summary' => '',
369 'after_summary' => '',
370 'after_licenses' => '',
371 'after_subscriptions' => '',
372 'after_downloads' => '',
373 'after_transactions' => '',
374 'end_of_order' => ''
375 ], [
376 'order' => $order,
377 'formattedData' => $formattedOrderData
378 ]);
379
380 return $this->sendSuccess([
381 'order' => $formattedOrderData,
382 'section_parts' => $hooksContents
383 ]);
384 }
385
386 public function downloadableProducts($order_uuid): \WP_REST_Response
387 {
388
389
390 // Call the method and store the response
391 $errorResponse = $this->checkUserLoggedIn();
392
393 // Check if there is an error response and return it if exists
394 if ($errorResponse !== null) {
395 // Return error if user is not logged in
396 return $errorResponse;
397 }
398
399
400 $order = Order::query()
401 ->where('uuid', $order_uuid)
402 ->with('customer')
403 // ->where(function (Builder $query) {
404 // $query->where('payment_status', Status::PAYMENT_PAID)
405 // ->whereIn('status', [Status::ORDER_COMPLETED, Status::ORDER_PROCESSING]);
406 // })
407 ->first();
408
409
410 if (empty($order) || empty($order->customer) || wp_get_current_user()->ID != $order->customer->user_id) {
411
412 return $this->sendSuccess([
413 'message' => __('Success', 'fluent-cart'),
414 'data' => []
415 ]);
416 }
417
418 return $this->sendSuccess([
419 'message' => __('Success', 'fluent-cart'),
420 'data' => $order->getDownloads('customer-profile')
421 ]);
422 }
423
424 /**
425 * @param $order
426 * @return bool|string
427 */
428 public function getCustomPaymentLink($order)
429 {
430 if (!intval($order->total_amount - $order->total_paid)) {
431 return false;
432 }
433
434 if ($order->parent_id) {
435 $subscription = Subscription::query()->where('parent_order_id', $order->parent_id)->first();
436 if ($subscription && $subscription->isSystem() && !SystemChargeService::isExhausted($subscription, $order)) {
437 return false;
438 }
439 }
440
441 return PaymentHelper::getCustomPaymentLink($order->uuid);
442 }
443
444 public function getTransactionBillingAddress(Request $request, $transaction_uuid)
445 {
446 // get current customer
447 $customer = CustomerResource::getCurrentCustomer();
448
449 if (empty($customer)) {
450 return $this->sendError([
451 'message' => __('Customer not found', 'fluent-cart')
452 ]);
453 }
454
455 $transaction = OrderTransaction::query()->where('uuid', $transaction_uuid)->first();
456
457 if (empty($transaction)) {
458 return $this->sendError([
459 'message' => __('Transaction not found', 'fluent-cart')
460 ]);
461 }
462
463 $order = Order::query()->where('customer_id', $customer->id)->find($transaction->order_id);
464
465 if (empty($order)) {
466 return $this->sendError([
467 'message' => __('Order not found', 'fluent-cart')
468 ]);
469 }
470
471 $vatTaxId = $order->getMeta('vat_tax_id', '');
472
473 // get order_address by $order->id
474 $billingAddress = OrderAddress::query()
475 ->where('order_id', $order->id)
476 ->where('type', 'billing');
477 if (empty($billingAddress)) {
478 $billingAddress = $billingAddress->where('order_id', $order->parent_id);
479 }
480 $billingAddress = $billingAddress->first();
481
482 $formatData = [
483 'address_1' => '',
484 'address_2' => '',
485 'city' => '',
486 'state' => '',
487 'postcode' => '',
488 'country' => '',
489 'name' => '',
490 'vat_tax_id' => '',
491 'address_id' => ''
492 ];
493
494 if (empty($billingAddress)) {
495 return $this->sendSuccess([
496 'message' => '',
497 'data' => $formatData
498 ]);
499 }
500
501 $formatData = [
502 'address_1' => $billingAddress->address_1,
503 'address_2' => $billingAddress->address_2,
504 'city' => $billingAddress->city,
505 'state' => $billingAddress->state,
506 'postcode' => $billingAddress->postcode,
507 'country' => $billingAddress->country,
508 'name' => $billingAddress->name,
509 'vat_tax_id' => $vatTaxId,
510 'address_id' => $billingAddress->id
511 ];
512
513 return $this->sendSuccess([
514 'message' => __('Success', 'fluent-cart'),
515 'data' => $formatData
516 ]);
517 }
518
519 public function saveTransactionBillingAddress(Request $request, $transaction_uuid)
520 {
521 $transactionUuid = sanitize_text_field($transaction_uuid);
522 $address_id = sanitize_text_field(Arr::get($request->all(), 'address_id'));
523 $transaction = OrderTransaction::query()->where('uuid', $transactionUuid)->first();
524
525 $vatTaxId = sanitize_text_field(Arr::get($request->all(), 'vat_tax_id'));
526 $orderId = $transaction->order_id;
527
528 // get order by $transaction->order_id
529 $customer = CustomerResource::getCurrentCustomer();
530
531 $order = Order::query()->where('customer_id', $customer->id)->find($orderId);
532
533 if (empty($order)) {
534 return $this->sendError([
535 'message' => __('Order not found', 'fluent-cart')
536 ]);
537 }
538
539 $address = null;
540
541 if ($address_id) {
542 $address = OrderAddress::query()
543 ->where('id', $address_id)
544 ->where('order_id', $order->id)
545 ->where('type', 'billing')
546 ->first();
547 }
548
549
550 $rules = App::localization()->getValidationRule($request->all());
551
552 // Validate input
553 $validated = $request->validate($rules);
554 // Sanitize
555 $sanitized = $request->getSafe([
556 'name' => 'sanitize_text_field',
557 'address_1' => 'sanitize_text_field',
558 'address_2' => 'sanitize_text_field',
559 'city' => 'sanitize_text_field',
560 'state' => 'sanitize_text_field',
561 'postcode' => 'sanitize_text_field',
562 'country' => 'sanitize_text_field',
563 ]);
564
565 $sanitized['order_id'] = $orderId;
566 $sanitized['type'] = 'billing';
567
568 OrderMeta::query()->updateOrCreate(
569 [
570 'order_id' => $order->id,
571 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
572 'meta_key' => 'vat_tax_id'
573 ],
574 [
575 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
576 'meta_value' => $vatTaxId // replace with your value variable
577 ]
578 );
579
580 if (empty($address)) {
581 // create new order address with $request data
582 $isCreated = OrderAddress::create($sanitized);
583
584 if (is_wp_error($isCreated)) {
585 return $this->sendError([
586 'message' => $isCreated->get_error_message()
587 ]);
588 }
589
590 return $this->sendSuccess([
591 'message' => __('Billing address created successfully', 'fluent-cart'),
592 'address_id' => $isCreated
593 ]);
594 }
595
596 // update address
597 $address->fill($sanitized);
598
599 if (!$address->save()) {
600 return $this->sendError([
601 'message' => __('Failed to update billing address', 'fluent-cart')
602 ]);
603 }
604
605
606 return $this->sendSuccess([
607 'message' => __('Billing address updated successfully', 'fluent-cart'),
608 'formatted_address' => $address->getAddressAsText()
609 ]);
610 }
611
612 }
613