PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.1
Booktics – Appointment Booking Calendar for Service Businesses v1.0.1
1.0.29 1.0.28 1.0.27 1.0.26 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 All 31 releases
booktics / core / order / controllers / order-controller.php

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

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