PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.2
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.2
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.2, at app/Http/Controllers/FrontendControllers/CustomerOrderController.php

591 lines 22.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 $formattedOrderData = [
244 'fulfillment_type' => $order->fulfillment_type,
245 'type' => $order->type,
246 'created_at' => DateTime::gmtToTimezone($order->created_at, Arr::get($order->config, 'user_tz', wp_timezone_string()))->format('Y-m-d H:i:s'),
247 'invoice_no' => $order->invoice_no,
248 'currency' => $order->currency,
249 'uuid' => $order->uuid,
250 'order_items' => $orderItems,
251 'status' => $order->status,
252 'payment_status' => $order->payment_status,
253 'shipping_status' => $order->shipping_status,
254 'fee_total' => $order->fee_total,
255
256 'billing_address_text' => $order->billing_address ? $order->billing_address->getAddressAsText(true, false) : '',
257 'shipping_address_text' => $order->shipping_address ? $order->shipping_address->getAddressAsText(true, false) : '',
258
259 'subtotal' => $order->subtotal,
260 'total_amount' => $order->total_amount,
261 'total_paid' => $order->total_paid,
262 'total_refund' => $order->total_refund,
263 'shipping_total' => $order->shipping_total,
264 'shipping_method_title' => (string) Arr::get($order->config, 'shipping_method_title', ''),
265 'coupon_discount_total' => $order->coupon_discount_total,
266 'manual_discount_total' => $order->manual_discount_total,
267 'prorate_credit' => (int) Arr::get($order->config, 'prorate_credit', 0),
268 'tax_total' => $order->tax_total,
269 'tax_behavior' => $order->tax_behavior,
270 'shipping_tax' => $order->shipping_tax,
271 'display_tax_lines' => $order->getDisplayTaxLines(),
272 'display_shipping_tax_lines' => $order->getDisplayShippingTaxLines(),
273 'is_reverse_charge_tax_order' => $order->isReverseChargeTaxOrder(),
274 'reverse_charge_price_mode' => $order->getOrderRcMode(),
275 'is_b2b_order' => $order->isB2BOrder(),
276 'tax_summary' => TaxSummaryHelper::computeTaxSummary($order),
277 'payment_method' => $order->payment_method,
278 'custom_payment_link' => $this->getCustomPaymentLink($order),
279 ];
280
281 $formattedOrderData['subscriptions'] = $order->subscriptions
282 ->filter(function ($subscription) {
283 return !in_array($subscription->status, [Status::SUBSCRIPTION_PENDING, Status::SUBSCRIPTION_INTENDED]);
284 })
285 ->map(function ($subscription) {
286 return OrderService::transformSubscription($subscription);
287 });
288
289 $formattedOrderData['downloads'] = ProductDownload::query()->whereIn('post_id', $productIds)->get()->filter(function ($download) use ($variationIds) {
290 $ids = $download->product_variation_id;
291 return empty($ids) || array_intersect($variationIds, $ids);
292 })->map(function ($download) use ($order) {
293 return [
294 'file_size' => $download->file_size,
295 'title' => $download->title,
296 'download_url' => Helper::generateDownloadFileLink(
297 $download,
298 $order->id
299 ),
300 ];
301 })->toArray();
302
303
304 // Let's find all the transactions related to this order
305 $orderIds = array_filter([$order->id, $order->parent_id]);
306 if ($order->type === Status::ORDER_TYPE_SUBSCRIPTION) {
307 $renewalOrderIds = Order::query()
308 ->where('parent_id', $order->id)
309 ->where('type', Status::ORDER_TYPE_RENEWAL)
310 ->get()->pluck('id')->toArray();
311
312 $orderIds = array_merge($orderIds, $renewalOrderIds);
313 $orderIds = array_values(array_unique($orderIds));
314 }
315
316 $transactions = OrderTransaction::query()
317 ->whereIn('order_id', $orderIds)
318 ->whereIn('status', [
319 Status::TRANSACTION_SUCCEEDED,
320 Status::TRANSACTION_REFUNDED,
321 Status::TRANSACTION_FAILED,
322 Status::TRANSACTION_PENDING,
323 ])
324 ->orderBy('id', 'DESC')
325 ->with(['order'])
326 ->get();
327
328 $formattedOrderData['transactions'] = $transactions->map(function ($transaction) {
329 $transformedTransaction = OrderService::transformTransaction($transaction);
330 if ($transaction->status === Status::TRANSACTION_PENDING
331 && !empty($transformedTransaction['custom_checkout_url'])
332 ) {
333 $transformedTransaction['show_pay_now'] = true;
334 }
335 return $transformedTransaction;
336 });
337
338 $formattedOrderData = apply_filters('fluent_cart/customer/order_data', $formattedOrderData, [
339 'order' => $order,
340 'customer' => $customer
341 ]);
342
343 $formattedOrderData['id'] = $order->id;
344
345 $hooksContents = apply_filters('fluent_cart/customer/order_details_section_parts', [
346 'before_summary' => '',
347 'after_summary' => '',
348 'after_licenses' => '',
349 'after_subscriptions' => '',
350 'after_downloads' => '',
351 'after_transactions' => '',
352 'end_of_order' => ''
353 ], [
354 'order' => $order,
355 'formattedData' => $formattedOrderData
356 ]);
357
358 return $this->sendSuccess([
359 'order' => $formattedOrderData,
360 'section_parts' => $hooksContents
361 ]);
362 }
363
364 public function downloadableProducts($order_uuid): \WP_REST_Response
365 {
366
367
368 // Call the method and store the response
369 $errorResponse = $this->checkUserLoggedIn();
370
371 // Check if there is an error response and return it if exists
372 if ($errorResponse !== null) {
373 // Return error if user is not logged in
374 return $errorResponse;
375 }
376
377
378 $order = Order::query()
379 ->where('uuid', $order_uuid)
380 ->with('customer')
381 // ->where(function (Builder $query) {
382 // $query->where('payment_status', Status::PAYMENT_PAID)
383 // ->whereIn('status', [Status::ORDER_COMPLETED, Status::ORDER_PROCESSING]);
384 // })
385 ->first();
386
387
388 if (empty($order) || empty($order->customer) || wp_get_current_user()->ID != $order->customer->user_id) {
389
390 return $this->sendSuccess([
391 'message' => __('Success', 'fluent-cart'),
392 'data' => []
393 ]);
394 }
395
396 return $this->sendSuccess([
397 'message' => __('Success', 'fluent-cart'),
398 'data' => $order->getDownloads('customer-profile')
399 ]);
400 }
401
402 /**
403 * @param $order
404 * @return bool|string
405 */
406 public function getCustomPaymentLink($order)
407 {
408 if (!intval($order->total_amount - $order->total_paid)) {
409 return false;
410 }
411
412 if ($order->parent_id) {
413 $subscription = Subscription::query()->where('parent_order_id', $order->parent_id)->first();
414 if ($subscription && $subscription->isSystem() && !SystemChargeService::isExhausted($subscription, $order)) {
415 return false;
416 }
417 }
418
419 return PaymentHelper::getCustomPaymentLink($order->uuid);
420 }
421
422 public function getTransactionBillingAddress(Request $request, $transaction_uuid)
423 {
424 // get current customer
425 $customer = CustomerResource::getCurrentCustomer();
426
427 if (empty($customer)) {
428 return $this->sendError([
429 'message' => __('Customer not found', 'fluent-cart')
430 ]);
431 }
432
433 $transaction = OrderTransaction::query()->where('uuid', $transaction_uuid)->first();
434
435 if (empty($transaction)) {
436 return $this->sendError([
437 'message' => __('Transaction not found', 'fluent-cart')
438 ]);
439 }
440
441 $order = Order::query()->where('customer_id', $customer->id)->find($transaction->order_id);
442
443 if (empty($order)) {
444 return $this->sendError([
445 'message' => __('Order not found', 'fluent-cart')
446 ]);
447 }
448
449 $vatTaxId = $order->getMeta('vat_tax_id', '');
450
451 // get order_address by $order->id
452 $billingAddress = OrderAddress::query()
453 ->where('order_id', $order->id)
454 ->where('type', 'billing');
455 if (empty($billingAddress)) {
456 $billingAddress = $billingAddress->where('order_id', $order->parent_id);
457 }
458 $billingAddress = $billingAddress->first();
459
460 $formatData = [
461 'address_1' => '',
462 'address_2' => '',
463 'city' => '',
464 'state' => '',
465 'postcode' => '',
466 'country' => '',
467 'name' => '',
468 'vat_tax_id' => '',
469 'address_id' => ''
470 ];
471
472 if (empty($billingAddress)) {
473 return $this->sendSuccess([
474 'message' => '',
475 'data' => $formatData
476 ]);
477 }
478
479 $formatData = [
480 'address_1' => $billingAddress->address_1,
481 'address_2' => $billingAddress->address_2,
482 'city' => $billingAddress->city,
483 'state' => $billingAddress->state,
484 'postcode' => $billingAddress->postcode,
485 'country' => $billingAddress->country,
486 'name' => $billingAddress->name,
487 'vat_tax_id' => $vatTaxId,
488 'address_id' => $billingAddress->id
489 ];
490
491 return $this->sendSuccess([
492 'message' => __('Success', 'fluent-cart'),
493 'data' => $formatData
494 ]);
495 }
496
497 public function saveTransactionBillingAddress(Request $request, $transaction_uuid)
498 {
499 $transactionUuid = sanitize_text_field($transaction_uuid);
500 $address_id = sanitize_text_field(Arr::get($request->all(), 'address_id'));
501 $transaction = OrderTransaction::query()->where('uuid', $transactionUuid)->first();
502
503 $vatTaxId = sanitize_text_field(Arr::get($request->all(), 'vat_tax_id'));
504 $orderId = $transaction->order_id;
505
506 // get order by $transaction->order_id
507 $customer = CustomerResource::getCurrentCustomer();
508
509 $order = Order::query()->where('customer_id', $customer->id)->find($orderId);
510
511 if (empty($order)) {
512 return $this->sendError([
513 'message' => __('Order not found', 'fluent-cart')
514 ]);
515 }
516
517 $address = null;
518
519 if ($address_id) {
520 $address = OrderAddress::query()
521 ->where('id', $address_id)
522 ->where('order_id', $order->id)
523 ->where('type', 'billing')
524 ->first();
525 }
526
527
528 $rules = App::localization()->getValidationRule($request->all());
529
530 // Validate input
531 $validated = $request->validate($rules);
532 // Sanitize
533 $sanitized = $request->getSafe([
534 'name' => 'sanitize_text_field',
535 'address_1' => 'sanitize_text_field',
536 'address_2' => 'sanitize_text_field',
537 'city' => 'sanitize_text_field',
538 'state' => 'sanitize_text_field',
539 'postcode' => 'sanitize_text_field',
540 'country' => 'sanitize_text_field',
541 ]);
542
543 $sanitized['order_id'] = $orderId;
544 $sanitized['type'] = 'billing';
545
546 OrderMeta::query()->updateOrCreate(
547 [
548 'order_id' => $order->id,
549 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
550 'meta_key' => 'vat_tax_id'
551 ],
552 [
553 //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
554 'meta_value' => $vatTaxId // replace with your value variable
555 ]
556 );
557
558 if (empty($address)) {
559 // create new order address with $request data
560 $isCreated = OrderAddress::create($sanitized);
561
562 if (is_wp_error($isCreated)) {
563 return $this->sendError([
564 'message' => $isCreated->get_error_message()
565 ]);
566 }
567
568 return $this->sendSuccess([
569 'message' => __('Billing address created successfully', 'fluent-cart'),
570 'address_id' => $isCreated
571 ]);
572 }
573
574 // update address
575 $address->fill($sanitized);
576
577 if (!$address->save()) {
578 return $this->sendError([
579 'message' => __('Failed to update billing address', 'fluent-cart')
580 ]);
581 }
582
583
584 return $this->sendSuccess([
585 'message' => __('Billing address updated successfully', 'fluent-cart'),
586 'formatted_address' => $address->getAddressAsText()
587 ]);
588 }
589
590 }
591