PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.0
Booktics – Appointment Booking Calendar for Service Businesses v1.0.0
1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 All 27 releases
booktics / core / order / controllers / order-controller.php

order-controller.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.0, at core/order/controllers/order-controller.php

914 lines 33.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Order\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Abstracts\Booktics_Database;
7 use Booktics\Abstracts\User_Model;
8 use Booktics\Cart\Cart_Manager;
9 use Booktics\Models\Appointment_Model;
10 use Booktics\Models\Customer_Model;
11 use Booktics\Models\Order_Model;
12 use Booktics\Models\Payment_Model;
13 use Booktics\Models\Service_Model;
14 use Booktics\Models\Team_Member_Model;
15 use Booktics\Order\Order_Validator;
16 use Booktics\Payment\Payment_Method_Manager;
17 use Booktics\Services\Time_Zone_Handler;
18 use Booktics_Stripe\Admin\Controllers\Booktics_Stripe_Controller;
19 use Exception;
20 use WP_Error;
21 use WP_HTTP_Response;
22 use WP_REST_Request;
23 use WP_REST_Server;
24
25 /**
26 * Order controller
27 *
28 * @package Booktics/Order
29 */
30 class Order_Controller extends Base_Rest_Controller {
31
32 /**
33 * Endpoint namespace
34 *
35 * @var string
36 */
37 protected $namespace = 'booktics/v1';
38
39 /**
40 * Route name
41 *
42 * @var string
43 */
44 protected $base = 'orders';
45
46 /**
47 * Register routes
48 *
49 * @return void
50 */
51 public function register_routes(): void {
52 register_rest_route(
53 $this->namespace, $this->base, array(
54 array(
55 'methods' => WP_REST_Server::CREATABLE,
56 'callback' => array( $this, 'create_item' ),
57 'permission_callback' => array(
58 $this,
59 'create_order_permission',
60 ),
61 ),
62 array(
63 'methods' => WP_REST_Server::READABLE,
64 'callback' => array( $this, 'get_items' ),
65 'permission_callback' => array( $this, 'get_items_permission' ),
66 'args' => array(
67 'page' => array( 'default' => 1 ),
68 'per_page' => array( 'default' => 10 ),
69 ),
70 ),
71 array(
72 'methods' => WP_REST_Server::DELETABLE,
73 'callback' => array( $this, 'bulk_delete_items' ),
74 'permission_callback' => array(
75 $this,
76 'bulk_delete_items_permission',
77 ),
78 ),
79 )
80 );
81
82 register_rest_route(
83 $this->namespace, $this->base . '/(?P<id>[\d]+)', array(
84 array(
85 'methods' => WP_REST_Server::EDITABLE,
86 'callback' => array( $this, 'update_item' ),
87 'permission_callback' => array( $this, 'update_item_permission' ),
88 'args' => array(
89 'id' => array( 'required' => true ),
90 'status' => array( 'type' => 'string' ),
91 ),
92 ),
93 array(
94 'methods' => WP_REST_Server::READABLE,
95 'callback' => array( $this, 'get_item' ),
96 'permission_callback' => array( $this, 'get_item_permission' ),
97 'args' => array(
98 'id' => array( 'required' => true ),
99 ),
100 ),
101 array(
102 'methods' => WP_REST_Server::DELETABLE,
103 'callback' => array( $this, 'delete_item' ),
104 'permission_callback' => array( $this, 'delete_item_permission' ),
105 'args' => array(
106 'id' => array( 'required' => true ),
107 ),
108 ),
109 )
110 );
111
112 register_rest_route(
113 $this->namespace, $this->base . '/(?P<id>[\d]+)/payment', array(
114 array(
115 'methods' => WP_REST_Server::EDITABLE,
116 'callback' => array( $this, 'update_payment_status' ),
117 'permission_callback' => array(
118 $this,
119 'update_payment_status_permission',
120 ),
121 ),
122 )
123 );
124
125 register_rest_route(
126 $this->namespace, $this->base . '/(?P<id>[\d]+)/refund', array(
127 array(
128 'methods' => WP_REST_Server::EDITABLE,
129 'callback' => array( $this, 'refund_payment' ),
130 'permission_callback' => array(
131 $this,
132 'update_payment_status_permission',
133 ),
134 ),
135 )
136 );
137 }
138
139 /**
140 * Check if the current user has permission to create orders
141 *
142 * @param WP_REST_Request $request
143 *
144 * @return bool
145 */
146 public function create_order_permission( $request ) {
147 return true;
148 }
149
150 /**
151 * Prepare and validate order data before creation
152 *
153 * @param array $data
154 *
155 * @return array|WP_Error
156 */
157 private function prepare_item_for_order_create( array $data ) {
158 if ( ! isset( $data['timezone'] ) || ! Time_Zone_Handler::validate_time_zone( $data['timezone'] ) ) {
159 $data['timezone'] = 'UTC';
160 }
161
162 if ( isset( $data['pay_now'] ) && $data['pay_now'] ) {
163 $validate = booktics_validate(
164 $data, array(
165 'payment_method' => array( 'required' ),
166 )
167 );
168
169 if ( is_wp_error( $validate ) ) {
170 return $validate;
171 }
172 }
173
174 $error = ( new \WP_Error() );
175 if ( ! is_array( $data['items'] ) || count( $data['items'] ) == 0 ) {
176 $error->add( 'items', __( 'Items are required', 'booktics' ) );
177
178 return $error;
179 }
180
181 foreach ( $data['items'] as $key => $item ) {
182 $item_validate = booktics_validate(
183 $item, array(
184 'date' => array( 'required' ),
185 'start_time' => array( 'required' ),
186 'end_time' => array( 'required' ),
187 'team_member_id' => array( 'required' ),
188 'service_id' => array( 'required' ),
189 )
190 );
191
192 if ( is_wp_error( $item_validate ) ) {
193 return $item_validate;
194 }
195
196 $converted_start_time = Time_Zone_Handler::convert_to_utc(
197 $item['start_time'],
198 $item['date'],
199 $data['timezone']
200 );
201 $converted_end_time = Time_Zone_Handler::convert_to_utc(
202 $item['end_time'],
203 $item['date'],
204 $data['timezone']
205 );
206 $item['start_time'] = $converted_start_time['time'];
207 $item['end_time'] = $converted_end_time['time'];
208
209 $start_time = strtotime( $item['start_time'] );
210 $end_time = strtotime( $item['end_time'] );
211
212 if ( ! $start_time || ! $end_time || $start_time >= $end_time ) {
213 $error->add( 'time_invalid', __( 'Invalid time range provided', 'booktics' ) );
214
215 return $error;
216 }
217
218 $data['items'][ $key ]['start_time'] = $converted_start_time['time'];
219 $data['items'][ $key ]['end_time'] = $converted_end_time['time'];
220 $data['items'][ $key ]['date'] = gmdate( 'Y-m-d', strtotime( $converted_start_time['date'] ) );
221 $data['items'][ $key ]['customer_id'] = absint( $item['customer_id'] );
222 $data['items'][ $key ]['team_member_id'] = absint( $item['team_member_id'] );
223 $data['items'][ $key ]['service_id'] = absint( $item['service_id'] );
224 $data['items'][ $key ]['additional_duration_id'] = $item['additional_duration_id'];
225 $data['items'][ $key ]['price'] = floatval( $item['price'] );
226 $data['items'][ $key ]['subtotal'] = floatval( $item['subtotal'] );
227 $data['items'][ $key ]['quantity'] = absint( $item['quantity'] ?? 1 );
228 $data['items'][ $key ]['group_booking'] = $item['group_booking'] ?? [];
229 }
230
231 return $data;
232 }
233
234
235 /**
236 * Create a new order with the provided data
237 *
238 * @param WP_REST_Request $request
239 *
240 * @return WP_Error|WP_HTTP_Response
241 * @throws Exception
242 */
243 public function create_item( $request ) {
244 $input_data = json_decode( $request->get_body(), true );
245 $validator = new Order_Validator();
246 $validation_result = $validator->validate( $input_data );
247
248 if ( is_wp_error( $validation_result ) ) {
249 return $this->error(
250 __( 'Order validation failed', 'booktics' ),
251 422,
252 'Validation',
253 $validation_result->get_error_messages()
254 );
255 }
256
257 $db = new Booktics_Database();
258 $validation = $this->prepare_item_for_order_create( $input_data );
259
260 if ( is_wp_error( $validation ) ) {
261 return $this->error( $validation->get_error_message() );
262 }
263
264 if ( ! isset( $input_data['customer_id'] ) ) {
265 $customer_id = $this->find_or_create_customer( $input_data );
266 if ( is_wp_error( $customer_id ) ) {
267 return $this->error( $customer_id->get_error_message() );
268 }
269 $input_data['customer_id'] = $customer_id;
270 }
271 try {
272 $db->begin_transaction();
273 // Order creation logic
274 $input_data['currency'] = $input_data['currency'] ?? 'usd';
275 $order_model = new Order_Model( $db );
276 $order_model = $order_model->create(
277 array(
278 'customer_id' => $input_data['customer_id'],
279 'payment_status' => 'pending',
280 'price_breakdown' => $input_data['price_breakdown'] ?? array(),
281 'tax_total' => $input_data['tax_total'] ?? 0,
282 'coupon_code' => $input_data['coupon_code'] ?? '',
283 'coupon_discount' => $input_data['coupon_discount'] ?? 0,
284 'subtotal' => $input_data['subtotal'],
285 'timezone' => $input_data['timezone'] ?? 'UTC',
286 'total' => $input_data['total'],
287 'payment_method' => $input_data['payment_method'] ?? null,
288 )
289 );
290
291 foreach ( $input_data['items'] as $item ) {
292 if ( isset( $item['uuid'] ) ) {
293 ( new Cart_Manager() )->remove_cart_item( $item['uuid'] );
294 }
295 $total_duration_min = $this->calculate_total_duration( $item );
296 $appointment_data = array(
297 'start_time' => $item['start_time'] ?? '',
298 'end_time' => $item['end_time'] ?? '',
299 'date' => gmdate( 'Y-m-d', strtotime( $item['date'] ) ),
300 'customer_id' => $input_data['customer_id'] ?? '',
301 'team_member_id' => $item['team_member_id'] ?? '',
302 'service_id' => $item['service_id'] ?? '',
303 'additional_duration_id' => $item['additional_duration_id'] ?? '',
304 'duration' => $total_duration_min,
305 'order_id' => $order_model->id,
306 'quantity' => $item['quantity'] ?? 1,
307 'price' => $item['price'] ?? 0,
308 'subtotal' => $item['subtotal'] ?? 0,
309 'total' => $item['total'] ?? 0,
310 'status' => 'pending',
311 );
312 $appointment_data = apply_filters( 'booktics_appointment_pro_format', $appointment_data, $item );
313 Appointment_Model::create( $appointment_data );
314 }
315
316 if ( $input_data['pay_now'] ) {
317 $payment_method = ( new Payment_Method_Manager() )->get_payment_method( $input_data['payment_method'] );
318 if ( $payment_method ) {
319 $payment_intent = $payment_method->process_payment( $order_model );
320 if ( $payment_intent['status'] === 'error' ) {
321 return $this->error( $payment_intent['message'] );
322 }
323 if ( $payment_intent['status'] === 'success' ) {
324 ( new Order_Model( $db ) )->update(
325 $order_model->id, array(
326 'payment_intent_id' => $payment_intent['payment_intent'],
327 )
328 );
329
330 ( new Payment_Model( $db ) )->create(
331 array(
332 'order_id' => $order_model->id,
333 'customer_id' => $input_data['customer_id'],
334 'amount' => $input_data['total'] * 100,
335 'currency' => $input_data['currency'],
336 'intent_id' => $payment_intent['payment_intent'],
337 'transaction_id' => null,
338 'payment_method' => $input_data['payment_method'],
339 'status' => 'pending',
340 'date' => gmdate( 'Y-m-d' ),
341 'date_time' => gmdate( 'Y-m-d H:i:s' ),
342 )
343 );
344 }
345 } else {
346 return $this->error( __( 'Payment method can not found!', 'booktics' ) );
347 }
348 }
349
350 $db->commit();
351 } catch ( Exception $e ) {
352 $db->roll_back();
353
354 return $this->error( $e->getMessage() );
355 }
356 do_action( 'booktics_order_created', $order_model );
357
358 $customer = ( new Customer_Model() )->find( $input_data['customer_id'] );
359 $appointments = ( new Appointment_Model() )->where( 'order_id', $order_model->id );
360 $appointment_details = array();
361 foreach ( $appointments as $appointment ) {
362 $appointment = $appointment->to_array();
363 $team_member = ( new Team_Member_Model() )->find( $appointment['team_member_id'] );
364 $service = ( new Service_Model() )->find( $appointment['service_id'] );
365 $service = $service ? $service->to_array() : array();
366 $additional_durations = $service['additional_durations'] ?? array();
367 $additional_durations = ! empty( $additional_durations )
368 ? array_filter(
369 $additional_durations, function ( $duration ) use ( $appointment ) {
370 return $duration['id'] === $appointment['additional_duration_id'];
371 }
372 ) : array();
373 $additional_duration = ! empty( $additional_durations ) ? reset( $additional_durations ) : array();
374 $formatted_appointment = array(
375 'id' => $appointment['id'],
376 'date' => $appointment['date'],
377 'start_time' => $appointment['start_time'],
378 'end_time' => $appointment['end_time'],
379 'additional_duration_id' => $appointment['additional_duration_id'],
380 'team' => array(
381 'id' => $team_member->id,
382 'display_name' => $team_member->display_name ?? '',
383 'image' => $team_member->image ?? '',
384 ),
385 'service' => array(
386 'id' => $appointment['service_id'],
387 'title' => $service['title'] ?? '',
388 'additional_duration' => $additional_duration,
389 ),
390 'duration' => $appointment['duration'],
391 'quantity' => $appointment['quantity'],
392 'price' => $appointment['price'],
393 'subtotal' => $appointment['subtotal'],
394 'total' => $appointment['total'],
395 'status' => $appointment['status'],
396 );
397 $formatted_appointment = apply_filters( 'booktics_appointment_pro_format', $formatted_appointment, $appointment );
398 $appointment_details[] = $formatted_appointment;
399 }
400
401 do_action(
402 'global_notification_hook', 'order_created', array(
403 'order_no' => $order_model->order_no,
404 'order_date' => booktics_datetime( 'M j, Y, g:ia', time(), 'UTC' ),
405 'order_total' => $input_data['total'],
406 'user_email' => $customer->user_email,
407 )
408 );
409
410 $response = array(
411 'id' => $order_model->id,
412 'order_no' => $order_model->order_no,
413 'customer_email' => $customer->user_email,
414 'subtotal' => $order_model->subtotal,
415 'total' => $order_model->total,
416 'payment_status' => $order_model->payment_status,
417 'items' => $appointment_details,
418 'payment_intent_id' => $payment_intent['payment_intent'] ?? '',
419 'client_secret' => $payment_intent['client_secret'] ?? '',
420 );
421
422 return $this->response( $response, __( 'Order created successfully', 'booktics' ), 201 );
423 }
424
425 /**
426 * Find existing customer by email or create new customer
427 *
428 * @param array $data
429 *
430 * @return int|WP_Error
431 */
432 private function find_or_create_customer( array $data ) {
433 $customer = get_user_by( 'email', $data['customer_email'] );
434
435 if ( $customer ) {
436 return $customer->ID;
437 }
438
439 $userdata = array(
440 'user_login' => User_Model::generate_username( $data['customer_email'] ),
441 'user_email' => $data['customer_email'],
442 'user_pass' => wp_generate_password(),
443 'first_name' => $data['customer_name'] ?? '',
444 'display_name' => $data['customer_name'] ?? '',
445 'phone' => $data['customer_phone'] ?? '',
446 );
447
448 $customer = new Customer_Model();
449
450 return $customer->save( $userdata );
451 }
452
453 /**
454 * Check if user has permission to get orders
455 *
456 * @param WP_REST_Request $request
457 *
458 * @return bool
459 */
460 public function get_items_permission( $request ) {
461 return true;
462 }
463
464 /**
465 * Get paginated list of orders
466 *
467 * @param WP_REST_Request $request
468 *
469 * @return WP_Error|WP_HTTP_Response
470 */
471 public function get_items( $request ) {
472 $page = (int) $request->get_param( 'paged' );
473 $per_page = (int) $request->get_param( 'per_page' );
474 $payment_status = $request->get_param( 'payment_status' );
475 $payment_method = $request->get_param( 'payment_method' );
476 $order_date = $request->get_param( 'order_date' );
477 $team_member = (int) $request->get_param( 'team_member' );
478 $service = (int) $request->get_param( 'service' );
479 $search = $request->get_param( 'search' );
480
481 $db = new Booktics_Database();
482 $orderModel = new Order_Model( $db );
483 $conditions = array();
484
485 if ( $payment_status ) {
486 $conditions['payment_status'] = $payment_status;
487 }
488
489 if ( $payment_method ) {
490 $conditions['payment_method'] = $payment_method;
491 }
492
493 if ( $order_date ) {
494 $conditions['DATE(created_at)'] = array( '=', $order_date );
495 }
496
497 if ( $search ) {
498 $conditions['order_no'] = array( 'LIKE', '%' . $search . '%' );
499 }
500
501 $orders = $orderModel->get(
502 $conditions, $page, $per_page, array(
503 'created_at',
504 'DESC',
505 )
506 );
507
508 if ( $team_member || $service ) {
509 foreach ( $orders['items'] as $key => $order ) {
510 $appointments = ( new Appointment_Model() )->where( 'order_id', $order->id );
511 $keep_order = false;
512
513 foreach ( $appointments as $appointment ) {
514 if ( $team_member && $appointment->team_member_id == $team_member ) {
515 $keep_order = true;
516 }
517 if ( $service && $appointment->service_id == $service ) {
518 $keep_order = true;
519 }
520 }
521
522 if ( ! $keep_order && ( $team_member || $service ) ) {
523 unset( $orders['items'][ $key ] );
524 --$orders['total'];
525 }
526 }
527
528 $orders['items'] = array_values( $orders['items'] );
529 $orders['total_pages'] = ceil( $orders['total'] / $per_page );
530 }
531
532 foreach ( $orders['items'] as &$order ) {
533 $customer = ( new Customer_Model() )->find( $order->customer_id );
534 $order->customer = array(
535 'display_name' => $customer ? $customer->display_name : '',
536 'email' => $customer ? $customer->user_email : '',
537 );
538 $order->total_services = ( new Appointment_Model() )->get_appointments_count_by_order_id( $order->id );
539 }
540
541 return $this->response( $orders, __( 'Orders fetched successfully', 'booktics' ) );
542 }
543
544 /**
545 * Check if user has permission to update order
546 *
547 * @param WP_REST_Request $request
548 *
549 * @return bool
550 */
551 public function update_item_permission( $request ) {
552 return true;
553 }
554
555 /**
556 * Update an existing order
557 *
558 * @param WP_REST_Request $request
559 *
560 * @return WP_Error|WP_HTTP_Response
561 */
562 public function update_item( $request ) {
563 $order_id = (int) $request->get_param( 'id' );
564 $data = json_decode( $request->get_body(), true );
565 $db = new Booktics_Database();
566 $order_model = new Order_Model( $db );
567 $order = $order_model->find( array( 'id' => $order_id ) );
568
569 if ( ! $order ) {
570 return $this->error( __( 'Order not found', 'booktics' ) );
571 }
572
573 $order_model->update(
574 $order_id, array(
575 'status' => $data['status'] ?? $order->status,
576 'payment_status' => $data['payment_status'] ?? $order->payment_status,
577 )
578 );
579
580 return $this->response( $order_model->find( array( 'id' => $order_id ) ), __( 'Order updated successfully', 'booktics' ) );
581 }
582
583 /**
584 * Check if user has permission to get order
585 *
586 * @param WP_REST_Request $request
587 *
588 * @return bool
589 */
590 public function get_item_permission( $request ) {
591 return true;
592 }
593
594 /**
595 * Get single order
596 *
597 * @param WP_REST_Request $request
598 *
599 * @return WP_Error|WP_HTTP_Response
600 */
601 public function get_item( $request ) {
602 $order_id = (int) $request->get_param( 'id' );
603 $db = new Booktics_Database();
604 $order_model = new Order_Model( $db );
605 $order = $order_model->find( array( 'id' => $order_id ) );
606
607 if ( ! $order ) {
608 return $this->error( __( 'Order not found', 'booktics' ) );
609 }
610
611 $customer = ( new Customer_Model() )->find( $order->customer_id );
612 $appointments = ( new Appointment_Model() )->where( 'order_id', $order->id );
613 $appointment_details = array();
614
615 foreach ( $appointments as $appointment ) {
616 $appointment = $appointment->to_array();
617 $team_member = ( new Team_Member_Model() )->find( $appointment['team_member_id'] );
618 $service = ( new Service_Model() )->find( $appointment['service_id'] );
619 $service = $service ? $service->to_array() : array();
620 $additional_durations = $service['additional_durations'] ?? array();
621 $additional_durations = ! empty( $additional_durations )
622 ? array_filter(
623 $additional_durations, function ( $duration ) use ( $appointment ) {
624 return $duration['id'] === $appointment['additional_duration_id'];
625 }
626 ) : array();
627 $additional_duration = ! empty( $additional_durations ) ? reset( $additional_durations ) : array();
628 $formatted_appointment = array(
629 'id' => $appointment['id'],
630 'date' => $appointment['date'],
631 'start_time' => $appointment['start_time'],
632 'end_time' => $appointment['end_time'],
633 'team' => array(
634 'id' => $team_member->id,
635 'display_name' => $team_member->display_name ?? '',
636 'image' => $team_member->image ?? '',
637 ),
638 'service' => array(
639 'id' => $appointment['service_id'],
640 'title' => $service['title'] ?? '',
641 'additional_duration' => $additional_duration,
642 ),
643 'quantity' => $appointment['quantity'],
644 'price' => $appointment['price'],
645 'subtotal' => $appointment['subtotal'],
646 'total' => $appointment['total'],
647 'status' => $appointment['status'],
648 );
649 $formatted_appointment = apply_filters( 'booktics_appointment_pro_format', $formatted_appointment, $appointment );
650 $appointment_details[] = $formatted_appointment;
651 }
652
653 $response = array(
654 'id' => $order->id,
655 'order_no' => $order->order_no,
656 'customer' => array(
657 'display_name' => $customer->display_name,
658 'email' => $customer->user_email,
659 ),
660 'subtotal' => $order->subtotal,
661 'total' => $order->total,
662 'payment_status' => $order->payment_status,
663 'status' => $order->status,
664 'items' => $appointment_details,
665 'created_at' => $order->created_at,
666 'payment_gateway' => $order->payment_method,
667 );
668
669 return $this->response( $response, __( 'Order fetched successfully', 'booktics' ) );
670 }
671
672 /**
673 * Check if user has permission to update payment status
674 *
675 * @param $request
676 *
677 * @return bool
678 */
679 public function update_payment_status_permission( $request ) {
680 return true;
681 }
682
683 /**
684 * Update payment status for an order
685 *
686 * @param $request
687 */
688 public function update_payment_status( $request ) {
689 $data = json_decode( $request->get_body(), true );
690 $order_id = ! empty( $data['order_id'] ) ? intval( $data['order_id'] ) : 0;
691 $payment_status = ! empty( $data['payment_status'] ) ? $data['payment_status'] : 0;
692 $payment_method = ! empty( $data['payment_method'] ) ? $data['payment_method'] : null;
693
694 $db = new Booktics_Database();
695 $order_model = new Order_Model( $db );
696 $order = $order_model->find( array( 'id' => $order_id ) );
697
698 if ( ! $order ) {
699 return $this->error( __( 'Order not found', 'booktics' ) );
700 }
701
702 if ( ! in_array( $payment_method, array( 'stripe', 'paypal' ) ) ) {
703 return $this->error( __( 'Unauthorized payment method', 'booktics' ) );
704 }
705
706 try {
707 $payment_handler = ( new Payment_Method_Manager() )->get_payment_method( $payment_method );
708 if ( ! $payment_handler ) {
709 return $this->error( __( 'Payment method not found', 'booktics' ) );
710 }
711
712 $status = $payment_handler->get_payment_status( $order );
713 if ( is_wp_error( $status ) ) {
714 return $this->error( $status->get_error_message() );
715 }
716
717 $order_model->update(
718 $order_id, array(
719 'payment_status' => $status,
720 'transaction_id' => $data['transaction_id'] ?? null,
721 )
722 );
723
724 // Payment model status and transaction id update
725 return $this->response(
726 array(
727 'payment_status' => $status,
728 ), __( 'Payment status updated successfully', 'booktics' )
729 );
730 } catch ( \Exception $e ) {
731 return $this->error( $e->getMessage() );
732 }
733 }
734
735 /**
736 * Check if user has permission to delete order
737 *
738 * @param WP_REST_Request $request
739 *
740 * @return bool
741 */
742 public function delete_item_permission( $request ) {
743 return true;
744 }
745
746 /**
747 * Delete an order
748 *
749 * @param $request
750 */
751 public function delete_item( $request ) {
752 $order_id = (int) $request->get_param( 'id' );
753 $db = new Booktics_Database();
754 $order_model = new Order_Model( $db );
755 $order = $order_model->find( array( 'id' => $order_id ) );
756
757 if ( ! $order ) {
758 return $this->error( __( 'Order not found', 'booktics' ) );
759 }
760
761 try {
762 $db->begin_transaction();
763
764 // Delete related appointments
765 $appointmentModel = new Appointment_Model();
766 $appointments = $appointmentModel->where( 'order_id', $order_id );
767 foreach ( $appointments as $appointment ) {
768 $appointment->delete();
769 }
770
771 // Delete related payments
772 $paymentModel = new Payment_Model( $db );
773 $payments = $paymentModel->get( array( 'order_id' => $order_id ) );
774 foreach ( $payments['items'] as $payment ) {
775 ( new Payment_Model( $db ) )->delete( $payment->id );
776 }
777 // Delete the order
778 $order_model->delete( $order_id );
779
780 $db->commit();
781
782 return $this->response( array(), __( 'Order deleted successfully', 'booktics' ) );
783 } catch ( Exception $e ) {
784 $db->roll_back();
785
786 return $this->error( $e->getMessage() );
787 }
788 }
789
790 /**
791 * Check if user has permission to bulk delete orders
792 *
793 * @param WP_REST_Request $request
794 *
795 * @return bool
796 */
797 public function bulk_delete_items_permission( $request ) {
798 return true;
799 }
800
801 /**
802 * Bulk delete orders
803 *
804 * @param WP_REST_Request $request
805 *
806 * @return WP_Error|WP_HTTP_Response
807 */
808 public function bulk_delete_items( $request ) {
809 $order_ids = array_map( 'intval', $request->get_param( 'ids' ) );
810 $db = new Booktics_Database();
811
812 try {
813 $db->begin_transaction();
814
815 foreach ( $order_ids as $order_id ) {
816 $order_model = new Order_Model( $db );
817 $order = $order_model->find( array( 'id' => $order_id ) );
818
819 if ( ! $order ) {
820 continue;
821 }
822
823 // Delete related appointments
824 $appointmentModel = new Appointment_Model();
825 $appointments = $appointmentModel->where( 'order_id', $order_id );
826 foreach ( $appointments as $appointment ) {
827 $appointment->delete();
828 }
829
830 // Delete related payments
831 $paymentModel = new Payment_Model( $db );
832 $payments = $paymentModel->get( array( 'order_id' => $order_id ) );
833 foreach ( $payments['items'] as $payment ) {
834 ( new Payment_Model( $db ) )->delete( $payment->id );
835 }
836
837 // Delete the order
838 $order_model->delete( $order_id );
839 }
840
841 $db->commit();
842
843 return $this->response( array(), __( 'Orders deleted successfully', 'booktics' ) );
844 } catch ( Exception $e ) {
845 $db->roll_back();
846
847 return $this->error( $e->getMessage() );
848 }
849 }
850
851 /**
852 * Process payment refund
853 *
854 * @param $request
855 */
856 public function refund_payment( $request ) {
857 $order_id = (int) $request->get_param( 'id' );
858 $db = new Booktics_Database();
859 $order_model = new Order_Model( $db );
860 $order = $order_model->find( array( 'id' => $order_id ) );
861
862 $payment_method = ( new Payment_Method_Manager() )->get_payment_method( $order->payment_method );
863 if ( ! $payment_method ) {
864 return $this->error( __( 'Invalid payment method!', 'booktics' ) );
865 }
866 $response = $payment_method->process_refund( $order );
867 if ( $response['status'] === 'error' ) {
868 return $this->error( $response['message'] );
869 }
870
871 $order_model->update(
872 $order_id, array(
873 'payment_status' => 'refunded',
874 )
875 );
876
877 return $this->response(
878 $order_model->find( array( 'id' => $order_id ) ),
879 __( 'Refund successful', 'booktics' )
880 );
881 }
882
883 /**
884 * Calculate total duration in minutes for a service appointment
885 *
886 * @param array $item
887 *
888 * @return int
889 */
890 private function calculate_total_duration( $item ) {
891 $service_model = Service_Model::find( $item['service_id'] );
892 if ( ! $service_model ) {
893 return 0;
894 }
895 $service_duration = array_filter(
896 $service_model->additional_durations, function ( $duration ) use ( $item ) {
897 return $item['additional_duration_id'] === $duration['id'];
898 }
899 );
900 if ( empty( $service_duration ) ) {
901 return 0;
902 }
903 $service_duration = reset( $service_duration );
904 $total_duration_min = $service_duration['duration_unit'] === 'min'
905 ? $service_duration['duration_time']
906 : ( $service_duration['duration_time'] * 60 );
907 if ( ! empty( $item['extra_services'] ) ) {
908 $total_duration_min = apply_filters( 'booktics_extra_service_duration', $total_duration_min, $item['extra_services'] );
909 }
910
911 return $total_duration_min;
912 }
913 }
914