PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
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 1.2.0 All 47 releases
fluent-cart / app / Http / Controllers / FrontendControllers / CustomerOrderController.php

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

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