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

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