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

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