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

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

614 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
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' => $order->created_at->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 $subscription->isVisibleToCustomer();
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