PluginProbe
seQura / 3.2.0
seQura v3.2.0
4.3.4 4.3.3 4.3.2 4.3.1 trunk 2.0.0 2.0.10 2.0.11 2.0.12 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0.0 3.0.2 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 4.0.0 All 30 releases
sequra / src / Services / Order / class-order-service.php

class-order-service.php in seQura 3.2.0, at src/Services/Order/class-order-service.php

984 lines 30.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Order service
4 *
5 * @package SeQura/WC
6 * @subpackage SeQura/WC/Services
7 */
8
9 namespace SeQura\WC\Services\Order;
10
11 use Exception;
12 use SeQura\Core\BusinessLogic\Domain\Multistore\StoreContext;
13 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderRequest\Address;
14 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderRequest\Cart;
15 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderRequest\Customer;
16 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderRequest\DeliveryMethod;
17 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderRequest\PreviousOrder;
18 use SeQura\Core\BusinessLogic\Domain\Order\Models\OrderUpdateData;
19 use SeQura\Core\BusinessLogic\Domain\Order\OrderStates;
20 use SeQura\Core\BusinessLogic\Domain\Order\RepositoryContracts\SeQuraOrderRepositoryInterface;
21 use SeQura\Core\BusinessLogic\Domain\Order\Service\OrderService;
22 use SeQura\WC\Core\Extension\BusinessLogic\Domain\OrderStatusSettings\Services\Order_Status_Settings_Service;
23 use SeQura\WC\Core\Extension\Infrastructure\Configuration\Configuration;
24 use SeQura\WC\Dto\Cart_Info;
25 use SeQura\WC\Dto\Payment_Method_Data;
26 use SeQura\WC\Repositories\Interface_Deletable_Repository;
27 use SeQura\WC\Repositories\Interface_Table_Migration_Repository;
28 use SeQura\WC\Services\Cart\Interface_Cart_Service;
29 use SeQura\WC\Services\Interface_Logger_Service;
30 use SeQura\WC\Services\Payment\Interface_Payment_Service;
31 use SeQura\WC\Services\Pricing\Interface_Pricing_Service;
32 use SeQura\WC\Services\Time\Interface_Time_Checker_Service;
33 use Throwable;
34 use WC_Customer;
35 use WC_DateTime;
36 use WC_Order;
37 use WP_User;
38
39 /**
40 * Handle use cases related to Order
41 */
42 class Order_Service implements Interface_Order_Service {
43
44 private const META_KEY_METHOD_TITLE = '_sq_method_title';
45 private const META_KEY_PRODUCT = '_sq_product';
46 private const META_KEY_CAMPAIGN = '_sq_campaign';
47 private const META_KEY_CART_REF = '_sq_cart_ref';
48 private const META_KEY_CART_CREATED_AT = '_sq_cart_created_at';
49 private const META_KEY_SENT_TO_SEQURA = '_sq_sent_to_sequra';
50
51 /**
52 * Payment service
53 *
54 * @var Interface_Payment_Service
55 */
56 private $payment_service;
57
58 /**
59 * Pricing service
60 *
61 * @var Interface_Pricing_Service
62 */
63 private $pricing_service;
64
65 /**
66 * Order status service
67 *
68 * @var Order_Status_Settings_Service
69 */
70 private $order_status_service;
71
72 /**
73 * Configuration
74 *
75 * @var Configuration
76 */
77 private $configuration;
78
79 /**
80 * Cart service
81 *
82 * @var Interface_Cart_Service
83 */
84 private $cart_service;
85
86 /**
87 * Store context
88 *
89 * @var StoreContext
90 */
91 private $store_context;
92
93 /**
94 * Logger
95 *
96 * @var Interface_Logger_Service
97 */
98 private $logger;
99
100 /**
101 * SeQura Order repository
102 *
103 * @var SeQuraOrderRepositoryInterface
104 */
105 private $sequra_order_repository;
106
107 /**
108 * Time checker service
109 *
110 * @var Interface_Time_Checker_Service
111 */
112 private $time_checker_service;
113
114 /**
115 * Deletable repository
116 *
117 * @var Interface_Deletable_Repository
118 */
119 private $deletable_repository;
120
121 /**
122 * Table migration repository
123 *
124 * @var Interface_Table_Migration_Repository
125 */
126 private $table_migration_repository;
127
128 /**
129 * Constructor
130 */
131 public function __construct(
132 SeQuraOrderRepositoryInterface $sequra_order_repository,
133 Interface_Payment_Service $payment_service,
134 Interface_Pricing_Service $pricing_service,
135 Order_Status_Settings_Service $order_status_service,
136 Configuration $configuration,
137 Interface_Cart_Service $cart_service,
138 StoreContext $store_context,
139 Interface_Logger_Service $logger,
140 Interface_Time_Checker_Service $time_checker_service,
141 Interface_Deletable_Repository $deletable_repository,
142 Interface_Table_Migration_Repository $table_migration_repository
143 ) {
144 $this->payment_service = $payment_service;
145 $this->pricing_service = $pricing_service;
146 $this->order_status_service = $order_status_service;
147 $this->configuration = $configuration;
148 $this->cart_service = $cart_service;
149 $this->store_context = $store_context;
150 $this->logger = $logger;
151 $this->sequra_order_repository = $sequra_order_repository;
152 $this->time_checker_service = $time_checker_service;
153 $this->deletable_repository = $deletable_repository;
154 $this->table_migration_repository = $table_migration_repository;
155 }
156
157 /**
158 * Get delivery method
159 */
160 public function get_delivery_method( ?WC_Order $order ): DeliveryMethod {
161
162 if ( ! $order ) {
163 $session = WC()->session;
164 $shipping_methods = $session ? WC()->session->chosen_shipping_methods : array();
165
166 if ( ! $shipping_methods || empty( WC()->shipping->get_packages() ) ) {
167 return new DeliveryMethod( 'default', null, 'default' );
168 }
169 $package = current( WC()->shipping->get_packages() );
170 $shipping_method = current( $shipping_methods );
171
172 if ( ! isset( $package['rates'][ $shipping_method ] ) ) {
173 return new DeliveryMethod( 'default', null, 'default' );
174 }
175
176 $rate = $package['rates'][ $shipping_method ];
177 return new DeliveryMethod( $rate->label, null, $rate->id );
178 }
179
180 $shipping_method = current( $order->get_shipping_methods() );
181
182 return new DeliveryMethod(
183 $shipping_method['name'] ?? 'default',
184 null,
185 $shipping_method['method_id'] ?? 'default'
186 );
187 }
188
189 /**
190 * Get client first name. If the order is null, attempt to retrieve data from the session.
191 */
192 public function get_first_name( ?WC_Order $order, $is_delivery = true ): string {
193 $prefix = $is_delivery ? 'shipping' : 'billing';
194 return $this->get_customer_field( $order, "get_{$prefix}_first_name", 'get_billing_first_name', "{$prefix}_first_name", 'first_name' );
195 }
196
197 /**
198 * Get client last name. If the order is null, attempt to retrieve data from the session.
199 */
200 public function get_last_name( ?WC_Order $order, $is_delivery = true ): string {
201 $prefix = $is_delivery ? 'shipping' : 'billing';
202 return $this->get_customer_field( $order, "get_{$prefix}_last_name", 'get_billing_last_name', "{$prefix}_last_name", 'last_name' );
203 }
204
205 /**
206 * Get client company. If the order is null, attempt to retrieve data from the session.
207 */
208 public function get_company( ?WC_Order $order, $is_delivery = true ): string {
209 $prefix = $is_delivery ? 'shipping' : 'billing';
210 return $this->get_customer_field( $order, "get_{$prefix}_company", 'get_billing_company', "{$prefix}_company", 'company' );
211 }
212
213 /**
214 * Get client address's first line. If the order is null, attempt to retrieve data from the session.
215 */
216 public function get_address_1( ?WC_Order $order, $is_delivery = true ): string {
217 $prefix = $is_delivery ? 'shipping' : 'billing';
218 return $this->get_customer_field( $order, "get_{$prefix}_address_1", 'get_billing_address_1', "{$prefix}_address_1", 'address_1' );
219 }
220
221 /**
222 * Get client address's second line. If the order is null, attempt to retrieve data from the session.
223 */
224 public function get_address_2( ?WC_Order $order, $is_delivery = true ): string {
225 $prefix = $is_delivery ? 'shipping' : 'billing';
226 return $this->get_customer_field( $order, "get_{$prefix}_address_2", 'get_billing_address_2', "{$prefix}_address_2", 'address_2' );
227 }
228
229 /**
230 * Get client postcode. If the order is null, attempt to retrieve data from the session.
231 */
232 public function get_postcode( ?WC_Order $order, $is_delivery = true ): string {
233 $prefix = $is_delivery ? 'shipping' : 'billing';
234 return $this->get_customer_field( $order, "get_{$prefix}_postcode", 'get_billing_postcode', "{$prefix}_postcode", 'postcode' );
235 }
236
237 /**
238 * Get client city. If the order is null, attempt to retrieve data from the session.
239 */
240 public function get_city( ?WC_Order $order, $is_delivery = true ): string {
241 $prefix = $is_delivery ? 'shipping' : 'billing';
242 $city = $this->get_customer_field( $order, "get_{$prefix}_city", 'get_billing_city', "{$prefix}_city", 'city' );
243 if ( ! $city ) {
244 $city = $this->get_customer_field( $order, 'get_city', 'get_city', 'city', 'city' );
245 }
246 return $city;
247 }
248
249 /**
250 * Get client country code. If the order is null, attempt to retrieve data from the session.
251 */
252 public function get_country( ?WC_Order $order, $is_delivery = true ): string {
253 $prefix = $is_delivery ? 'shipping' : 'billing';
254 return $this->get_customer_field( $order, "get_{$prefix}_country", 'get_billing_country', "{$prefix}_country", 'country' );
255 }
256
257 /**
258 * Get client state. If the order is null, attempt to retrieve data from the session.
259 */
260 public function get_state( ?WC_Order $order, $is_delivery = true ): string {
261 $prefix = $is_delivery ? 'shipping' : 'billing';
262 $state_code = $this->get_customer_field( $order, "get_{$prefix}_state", 'get_billing_state', "{$prefix}_state", 'state' );
263 if ( ! $state_code ) {
264 return '';
265 }
266 $states = WC()->countries->get_states( $this->get_country( $order ) );
267 if ( ! $states || ! isset( $states[ $state_code ] ) ) {
268 return '';
269 }
270 return $states[ $state_code ];
271 }
272
273 /**
274 * Get client phone. If the order is null, attempt to retrieve data from the session.
275 */
276 public function get_phone( ?WC_Order $order, $is_delivery = true ): string {
277 $prefix = $is_delivery ? 'shipping' : 'billing';
278 return $this->get_customer_field( $order, "get_{$prefix}_phone", 'get_billing_phone', "{$prefix}_phone", 'phone' );
279 }
280
281 /**
282 * Get client email. If the order is null, attempt to retrieve data from the session.
283 */
284 public function get_email( ?WC_Order $order ): string {
285 return $this->get_customer_field( $order, 'get_billing_email', 'get_billing_email', 'email', 'email' );
286 }
287
288 /**
289 * TODO: Review this with Mikel. I need to know what is the origin of the VAT number field because it is not a default field in WooCommerce.
290 * Get shopper vat number. If the order is null, attempt to retrieve data from the session.
291 */
292 public function get_vat( ?WC_Order $order, $is_delivery = true ): string {
293 $prefix = $is_delivery ? 'shipping' : 'billing';
294 $vat = $this->get_customer_field( $order, "get_{$prefix}_nif", 'get_billing_nif', "{$prefix}_nif", 'nif' );
295 if ( ! $vat ) {
296 $vat = $this->get_customer_field( $order, 'get_nif', 'get_nif', 'nif', 'nif' );
297 }
298 if ( ! $vat ) {
299 $vat = $this->get_customer_field( $order, "get_{$prefix}_vat", 'get_billing_vat', "{$prefix}_vat", 'vat' );
300 }
301
302 return $vat;
303 }
304
305 /**
306 * Get shopper NIN number. If the order is null, attempt to retrieve data from the session.
307 */
308 public function get_nin( ?WC_Order $order ): ?string {
309 /**
310 * Get NIN number
311 *
312 * @since 3.0.0
313 */
314 $nin = \apply_filters( 'sequra_get_nin', null, $order );
315 return is_string( $nin ) || null === $nin ? $nin : null;
316 }
317
318 /**
319 * Get date of birth. If the order is null, attempt to retrieve data from the session.
320 */
321 public function get_dob( ?WC_Order $order ): ?string {
322 /**
323 * Get Date of Birth number
324 *
325 * @since 3.0.0
326 */
327 $dob = \apply_filters( 'sequra_get_dob', null, $order );
328 return is_string( $dob ) || null === $dob ? $dob : null;
329 }
330
331 /**
332 * Get shopper title. If the order is null, attempt to retrieve data from the session.
333 */
334 public function get_shopper_title( ?WC_Order $order ): ?string {
335 /**
336 * Get Shopper title
337 *
338 * @since 3.0.0
339 */
340 $title = \apply_filters( 'sequra_get_shopper_title', null, $order );
341 return is_string( $title ) || null === $title ? $title : null;
342 }
343
344 /**
345 * Get shopper created at date. If the order is null, attempt to retrieve data from the session.
346 */
347 public function get_shopper_created_at( ?WC_Order $order ): ?string {
348 /**
349 * Get Shopper created at date
350 *
351 * @since 3.0.0
352 */
353 $date = \apply_filters( 'sequra_get_shopper_created_at', $this->get_shopper_registration_date( $order ), $order );
354 return is_string( $date ) || null === $date ? $date : null;
355 }
356
357 /**
358 * Get shopper registration date
359 */
360 private function get_shopper_registration_date( ?WC_Order $order ): ?string {
361 if ( ! $order ) {
362 return null;
363 }
364 /**
365 * Order user
366 *
367 * @var WP_User $shopper
368 */
369 $shopper = \get_user_by( 'id', $order->get_customer_id() );
370 if ( ! $shopper instanceof WP_User ) {
371 return null;
372 }
373 $timestamp = strtotime( $shopper->user_registered );
374 return $timestamp ? gmdate( 'c', $timestamp ) : null;
375 }
376
377 /**
378 * Get shopper updated at date. If the order is null, attempt to retrieve data from the session.
379 */
380 public function get_shopper_updated_at( ?WC_Order $order ): ?string {
381 /**
382 * Get Shopper updated at date
383 *
384 * @since 3.0.0
385 */
386 $date = \apply_filters( 'sequra_get_shopper_updated_at', $this->get_shopper_registration_date( $order ), $order );
387 return is_string( $date ) || null === $date ? $date : null;
388 }
389
390 /**
391 * Get shopper rating. If the order is null, attempt to retrieve data from the session.
392 */
393 public function get_shopper_rating( ?WC_Order $order ): ?int {
394 /**
395 * Get Shopper rating. Must return an integer between 0 and 100 or null.
396 *
397 * @since 3.0.0
398 */
399 $rating = \apply_filters( 'sequra_get_shopper_rating', null, $order );
400 return ( is_int( $rating ) && 0 <= $rating && 100 >= $rating ) || null === $rating ? $rating : null;
401 }
402
403 /**
404 * Get previous orders
405 *
406 * @return PreviousOrder[]
407 */
408 public function get_previous_orders( int $customer_id ): array {
409 $previous_orders = array();
410
411 $order_statuses = $this->order_status_service->getOrderStatusSettings();
412
413 if ( ! $order_statuses ) {
414 return $previous_orders;
415 }
416
417 $statuses = $this->order_status_service->get_shop_status_completed();
418 foreach ( $order_statuses as $order_status ) {
419 if ( $order_status->getSequraStatus() === OrderStates::STATE_APPROVED ) {
420 // Use the default status if the shop status is not set.
421 $statuses[] = empty( $order_status->getShopStatus() ) ? 'wc-processing' : $order_status->getShopStatus();
422 }
423 }
424
425 $orders = array();
426 if ( $customer_id ) {
427 /**
428 * Get previous orders
429 *
430 * @var WC_Order[] $previous_orders
431 */
432 $orders = \wc_get_orders(
433 array(
434 'limit' => -1,
435 'customer' => $customer_id,
436 'status' => $statuses,
437 )
438 );
439 }
440
441 if ( is_array( $orders ) ) {
442 foreach ( $orders as $order ) {
443 /**
444 * Order date
445 *
446 * @var WC_DateTime $date
447 */
448 $date = $order->get_date_created( 'edit' );
449 $postcode = $this->get_postcode( $order );
450 $country = $this->get_country( $order );
451
452 $previous_orders[] = new PreviousOrder(
453 $date ? $date->date( 'c' ) : '',
454 $this->pricing_service->to_cents( (float) $order->get_total( 'edit' ) ),
455 $order->get_currency(),
456 $order->get_status( 'edit' ),
457 \wc_get_order_status_name( $order->get_status( 'edit' ) ),
458 $order->get_payment_method( 'edit' ),
459 $order->get_payment_method_title( 'edit' ),
460 $postcode ? $postcode : null,
461 $country ? $country : null
462 );
463 }
464 }
465
466 return $previous_orders;
467 }
468
469 /**
470 * Get customer data from order, session or POST. If not found, return an empty string.
471 */
472 private function get_customer_field(
473 ?WC_Order $order,
474 string $order_func,
475 string $order_alt_func,
476 string $session_key,
477 string $session_alt_key
478 ): string {
479 if ( ! $order ) {
480 $customer = $this->get_customer_from_session();
481 return ! empty( $customer[ $session_key ] ) ? $customer[ $session_key ] : (
482 $session_alt_key !== $session_key && ! empty( $customer[ $session_alt_key ] ) ? $customer[ $session_alt_key ] : ''
483 );
484 }
485 $value = '';
486 if ( method_exists( $order, $order_func ) ) {
487 $value = call_user_func( array( $order, $order_func ) );
488 }
489
490 if ( ! $value && $order_alt_func !== $order_func && method_exists( $order, $order_alt_func ) ) {
491 $value = call_user_func( array( $order, $order_alt_func ) );
492 }
493
494 return $value;
495 }
496
497 /**
498 * Get customer data from session. If not found, return an empty array.
499 */
500 private function get_customer_from_session(): array {
501 $data = array();
502 if ( function_exists( 'WC' ) && WC()->customer ) {
503 /**
504 * Customer instance.
505 *
506 * @var WC_Customer $customer
507 */
508 $customer = WC()->customer;
509 $data = array(
510 'email' => $customer->get_email(),
511 'billing_first_name' => $customer->get_billing_first_name(),
512 'billing_last_name' => $customer->get_billing_last_name(),
513 'billing_company' => $customer->get_billing_company(),
514 'billing_address_1' => $customer->get_billing_address_1(),
515 'billing_address_2' => $customer->get_billing_address_2(),
516 'billing_postcode' => $customer->get_billing_postcode(),
517 'billing_city' => $customer->get_billing_city(),
518 'billing_country' => $customer->get_billing_country(),
519 'billing_state' => $customer->get_billing_state(),
520 'billing_phone' => $customer->get_billing_phone(),
521 'billing_nif' => method_exists( $customer, 'get_billing_nif' ) ? $customer->get_billing_nif() : '',
522 'billing_vat' => method_exists( $customer, 'get_billing_vat' ) ? $customer->get_billing_vat() : '',
523 'shipping_first_name' => $customer->get_shipping_first_name(),
524 'shipping_last_name' => $customer->get_shipping_last_name(),
525 'shipping_company' => $customer->get_shipping_company(),
526 'shipping_address_1' => $customer->get_shipping_address_1(),
527 'shipping_address_2' => $customer->get_shipping_address_2(),
528 'shipping_postcode' => $customer->get_shipping_postcode(),
529 'shipping_city' => $customer->get_shipping_city(),
530 'shipping_country' => $customer->get_shipping_country(),
531 'shipping_state' => $customer->get_shipping_state(),
532 'shipping_phone' => $customer->get_shipping_phone(),
533 'shipping_nif' => method_exists( $customer, 'get_shipping_nif' ) ? $customer->get_shipping_nif() : '',
534 'shipping_vat' => method_exists( $customer, 'get_shipping_vat' ) ? $customer->get_shipping_vat() : '',
535 );
536 }
537 return $data;
538 }
539
540 /**
541 * Get order meta value by key from a seQura order.
542 * If the order is not a seQura order an empty string is returned.
543 */
544 private function get_order_meta( WC_Order $order, $meta_key ): string {
545 if ( $order->get_payment_method() !== $this->payment_service->get_payment_gateway_id() ) {
546 return '';
547 }
548 return strval( $order->get_meta( $meta_key, true ) );
549 }
550
551 /**
552 * Get the seQura payment method title for the order.
553 * If the order is not a seQura order an empty string is returned.
554 */
555 public function get_payment_method_title( WC_Order $order ): string {
556 return $this->get_order_meta( $order, self::META_KEY_METHOD_TITLE );
557 }
558
559 /**
560 * Get the seQura product for the order.
561 * If the value is not found an empty string is returned.
562 */
563 public function get_product( WC_Order $order ): string {
564 return $this->get_order_meta( $order, self::META_KEY_PRODUCT );
565 }
566
567 /**
568 * Get the seQura campaign for the order.
569 * If the value is not found an empty string is returned.
570 */
571 public function get_campaign( WC_Order $order ): string {
572 return $this->get_order_meta( $order, self::META_KEY_CAMPAIGN );
573 }
574
575 /**
576 * Get the seQura cart info for the order.
577 * If the value is not found null is returned.
578 */
579 public function get_cart_info( WC_Order $order ): ?Cart_Info {
580 return Cart_Info::from_array(
581 array(
582 'ref' => $this->get_order_meta( $order, self::META_KEY_CART_REF ),
583 'created_at' => $this->get_order_meta( $order, self::META_KEY_CART_CREATED_AT ),
584 )
585 );
586 }
587
588 /**
589 * Get IPN webhook identifier
590 */
591 public function get_ipn_url( WC_Order $order, string $store_id ): string {
592 return \add_query_arg(
593 array(
594 'order' => strval( $order->get_id() ),
595 'wc-api' => $this->payment_service->get_ipn_webhook(),
596 'store_id' => $store_id,
597 ),
598 \home_url( '/' )
599 );
600 }
601
602 /**
603 * Get payment gateway webhook identifier
604 */
605 public function get_event_url( WC_Order $order, string $store_id ): string {
606 return \add_query_arg(
607 array(
608 'order' => strval( $order->get_id() ),
609 'wc-api' => $this->payment_service->get_event_webhook(),
610 'store_id' => $store_id,
611 ),
612 \home_url( '/' )
613 );
614 }
615
616 /**
617 * Get payment gateway webhook identifier
618 */
619 public function get_return_url( WC_Order $order ): string {
620 return \add_query_arg(
621 array(
622 'order' => strval( $order->get_id() ),
623 'sq_product' => 'SQ_PRODUCT_CODE',
624 'wc-api' => $this->payment_service->get_return_webhook(),
625 ),
626 \home_url( '/' )
627 );
628 }
629
630 /**
631 * Save required metadata for the order.
632 * Returns true if the metadata was saved, false otherwise.
633 */
634 public function set_order_metadata( WC_Order $order, ?Payment_Method_Data $dto, ?Cart_Info $cart_info ): bool {
635 if ( ! $dto ) {
636 return false;
637 }
638
639 if ( ! $cart_info ) {
640 return false;
641 }
642
643 $order->update_meta_data( self::META_KEY_PRODUCT, $dto->product );
644 if ( ! empty( $dto->campaign ) ) {
645 $order->update_meta_data( self::META_KEY_CAMPAIGN, $dto->campaign );
646 } else {
647 $order->delete_meta_data( self::META_KEY_CAMPAIGN );
648 }
649 $order->update_meta_data( self::META_KEY_METHOD_TITLE, $dto->title );
650
651 $this->set_cart_info( $order, $cart_info );
652 return true;
653 }
654
655 /**
656 * Set cart info if it is not already set
657 */
658 public function create_cart_info( WC_Order $order ): ?Cart_Info {
659 $cart_info = $this->get_cart_info( $order );
660 if ( $this->cart_service->is_cart_info_valid( $cart_info ) ) {
661 // Skip if the cart info is already set.
662 return null;
663 }
664
665 $date = $order->get_date_created();
666 $cart_info = new Cart_Info( null, $date ? $date->date( 'c' ) : null );
667 $this->set_cart_info( $order, $cart_info );
668 return $cart_info;
669 }
670
671 /**
672 * Set cart info for the order
673 *
674 * @param Cart_Info $cart_info Cart info
675 */
676 public function set_cart_info( WC_Order $order, $cart_info ): void {
677 if ( $cart_info instanceof Cart_Info ) {
678 $order->update_meta_data( self::META_KEY_CART_REF, $cart_info->ref );
679 $order->update_meta_data( self::META_KEY_CART_CREATED_AT, $cart_info->created_at );
680 $order->save();
681 }
682 }
683
684 /**
685 * Get the meta key used to store the sent to seQura value.
686 */
687 public function get_sent_to_sequra_meta_key(): string {
688 return self::META_KEY_SENT_TO_SEQURA;
689 }
690
691 /**
692 * Set the order as sent to seQura
693 */
694 public function set_as_sent_to_sequra( WC_Order $order ): void {
695 $order->update_meta_data( self::META_KEY_SENT_TO_SEQURA, 1 );
696 $order->save();
697 }
698
699 /**
700 * Get customer for the order
701 */
702 public function get_customer( ?WC_Order $order, string $lang, int $fallback_user_id = 0, string $fallback_ip = '', string $fallback_user_agent = '' ): Customer {
703 $current_user_id = $order ? $order->get_customer_id() : $fallback_user_id;
704 $logged_in = $current_user_id > 0;
705 $ref = $logged_in ? $current_user_id : null;
706 $ip = $order ? $order->get_customer_ip_address( 'edit' ) : $fallback_ip;
707 $user_agent = $order ? $order->get_customer_user_agent( 'edit' ) : $fallback_user_agent;
708
709 return new Customer(
710 $this->get_email( $order ),
711 $lang,
712 $ip,
713 $user_agent,
714 $this->get_first_name( $order, true ),
715 $this->get_last_name( $order, true ),
716 $this->get_shopper_title( $order ), // title.
717 $ref,
718 $this->get_dob( $order ), // dateOfBirth.
719 $this->get_nin( $order ), // nin.
720 $this->get_company( $order, true ),
721 $this->get_vat( $order, true ), // vatNumber.
722 $this->get_shopper_created_at( $order, true ), // createdAt.
723 $this->get_shopper_updated_at( $order, true ), // updatedAt.
724 $this->get_shopper_rating( $order ), // rating.
725 null, // ninControl.
726 $this->get_previous_orders( $current_user_id ),
727 null, // vehicle.
728 $logged_in
729 );
730 }
731
732 /**
733 * Get delivery or invoice address
734 */
735 public function get_address( ?WC_Order $order, bool $is_delivery ): Address {
736 $country = $this->get_country( $order, $is_delivery );
737 return new Address(
738 $this->get_company( $order, $is_delivery ),
739 $this->get_address_1( $order, $is_delivery ),
740 $this->get_address_2( $order, $is_delivery ),
741 $this->get_postcode( $order, $is_delivery ),
742 $this->get_city( $order, $is_delivery ),
743 $country ? $country : 'ES',
744 $this->get_first_name( $order, $is_delivery ),
745 $this->get_last_name( $order, $is_delivery ),
746 null, // phone.
747 $this->get_phone( $order, $is_delivery ), // mobile phone.
748 $this->get_state( $order, $is_delivery ),
749 $order ? $order->get_customer_note( 'edit' ) : null, // extra.
750 $this->get_vat( $order, $is_delivery )
751 );
752 }
753
754 /**
755 * Call the Order Update API to sync the order status with SeQura
756 *
757 * @throws Throwable
758 */
759 public function update_sequra_order_status( WC_Order $order, string $old_store_status, string $new_store_status ): void {
760 if ( $order->get_payment_method( 'edit' ) !== $this->payment_service->get_payment_gateway_id()
761 || ! in_array( $new_store_status, $this->order_status_service->get_shop_status_completed( true ), true )
762 || ! $order->needs_processing() ) {
763 // Prevent updating orders that:
764 // 1. Were not paid with SeQura.
765 // 1. Are not completed.
766 // 2. Contain only virtual & downloadable products.
767 return;
768 }
769
770 $this->set_sequra_order_status_to_shipped( $order );
771 }
772
773 /**
774 * Set the order status to shipped in SeQura
775 *
776 * @throws Throwable
777 */
778 private function set_sequra_order_status_to_shipped( WC_Order $order ): void {
779 $cart_info = $this->get_cart_info( $order );
780 $currency = $order->get_currency( 'edit' );
781 $cart_ref = $cart_info ? $cart_info->ref : null;
782 $created_at = $cart_info ? $cart_info->created_at : null;
783 $updated_at = $order->get_date_completed()->format( 'Y-m-d H:i:s' );
784 $shipped_items = array_merge(
785 $this->cart_service->get_items( $order ),
786 $this->cart_service->get_handling_items( $order ),
787 $this->cart_service->get_discount_items( $order ),
788 $this->cart_service->get_refund_items( $order )
789 );
790
791 try {
792 /**
793 * Filter the order_ref_1.
794 *
795 * @since 2.0.0
796 */
797 $ref_1 = \apply_filters( 'woocommerce_sequra_get_order_ref_1', $order->get_id(), $order );
798
799 $this->call_update_order(
800 new OrderUpdateData(
801 $ref_1, // Order reference.
802 new Cart( $currency, false, $shipped_items, $cart_ref, $created_at, $updated_at ), // Shipped cart.
803 new Cart( $currency ), // Unshipped cart.
804 null, // Delivery address.
805 null // Invoice address.
806 )
807 );
808 } catch ( Throwable $e ) {
809 throw $e;
810 }
811 }
812
813 /**
814 * Get a fresh instance of the core order service
815 *
816 * @param OrderUpdateData $order_data Order data
817 *
818 * @throws Exception
819 */
820 private function call_update_order( $order_data ) {
821 $store_id = $this->configuration->get_store_id();
822 /**
823 * Order service
824 *
825 * @var OrderService $order_service
826 */
827 $order_service = $this->store_context::doWithStore( $store_id, 'SeQura\Core\Infrastructure\ServiceRegister::getService', array( OrderService::class ) );
828 $this->store_context::doWithStore( $store_id, array( $order_service, 'updateOrder' ), array( $order_data ) );
829 }
830
831 /**
832 * Update the order amount in SeQura after a refund
833 *
834 * @throws Throwable
835 */
836 public function handle_refund( WC_Order $order, float $amount ): void {
837 $cart_info = $this->get_cart_info( $order );
838 $currency = $order->get_currency( 'edit' );
839 $cart_ref = $cart_info ? $cart_info->ref : null;
840 $created_at = $cart_info ? $cart_info->created_at : null;
841 $updated_at = $order->get_date_completed()->format( 'Y-m-d H:i:s' );
842 $shipped_items = array();
843 if ( $order->get_total( 'edit' ) > $amount ) {
844 $shipped_items = array_merge(
845 $this->cart_service->get_items( $order ),
846 $this->cart_service->get_handling_items( $order ),
847 $this->cart_service->get_discount_items( $order ),
848 $this->cart_service->get_refund_items( $order )
849 );
850 }
851
852 try {
853 /**
854 * Filter the order_ref_1.
855 *
856 * @since 2.0.0
857 */
858 $ref_1 = \apply_filters( 'woocommerce_sequra_get_order_ref_1', $order->get_id(), $order );
859 $this->call_update_order(
860 new OrderUpdateData(
861 $ref_1, // Order reference.
862 new Cart( $currency, false, $shipped_items, $cart_ref, $created_at, $updated_at ), // Shipped cart.
863 new Cart( $currency ), // Unshipped cart.
864 null, // Delivery address.
865 null // Invoice address.
866 )
867 );
868 } catch ( Throwable $e ) {
869 throw $e;
870 }
871 }
872
873 /**
874 * Get the link to the SeQura back office for the order
875 */
876 public function get_link_to_sequra_back_office( WC_Order $order ): ?string {
877 if ( $order->get_payment_method() !== $this->payment_service->get_payment_gateway_id() ) {
878 return null;
879 }
880
881 switch ( $this->configuration->get_env() ) {
882 case 'sandbox':
883 return 'https://simbox.sequrapi.com/orders/' . $order->get_transaction_id();
884 case 'live':
885 return 'https://simba.sequra.es/orders/' . $order->get_transaction_id();
886 default:
887 return null;
888 }
889 }
890
891 /**
892 * Get the total amount of the order
893 *
894 * @param WC_Order $order
895 * @return float|int
896 */
897 public function get_total( $order, $in_cents = true ) {
898 $total = 0;
899 if ( $order instanceof WC_Order ) {
900 $total = (float) $order->get_total( 'edit' );
901 }
902 return $in_cents ? $this->pricing_service->to_cents( $total ) : $total;
903 }
904
905 /**
906 * Cleanup orders
907 *
908 * @return void
909 */
910 public function cleanup_orders() {
911 $this->deletable_repository->delete_old_and_invalid();
912 }
913
914 /**
915 * Get the Merchant ID
916 *
917 * @param WC_Order $order
918 * @return string|null
919 */
920 public function get_merchant_id( $order ) {
921 if ( ! $order instanceof WC_Order ) {
922 return null;
923 }
924
925 $sq_order = $this->sequra_order_repository->getByShopReference( $order->get_id() );
926 return $sq_order ? (string) $sq_order->getMerchant()->getId() : null;
927 }
928
929
930 /**
931 * Check if the migration process is complete
932 *
933 * @return bool True if they are missing indexes, false otherwise
934 */
935 public function is_migration_complete() {
936 return $this->table_migration_repository->is_migration_complete();
937 }
938
939 /**
940 * Execute the migration process
941 */
942 public function migrate_data() {
943 if ( ! $this->table_migration_repository->prepare_tables_for_migration() ) {
944 $this->logger->log_error( 'An error occurred while preparing the tables for migration.', __FUNCTION__, __CLASS__ );
945 return;
946 }
947
948 /**
949 * Filters the start hour for the migration process.
950 *
951 * @since 3.1.2
952 * @return int The hour to start the migration process, default is 2 (2AM).
953 */
954 $from = (int) apply_filters( 'sequra_migration_from', 2 );
955
956 /**
957 * Filters the end hour for the migration process.
958 *
959 * @since 3.1.2
960 * @return int The hour to end the migration process, default is 6 (6AM).
961 */
962 $to = (int) apply_filters( 'sequra_migration_to', 6 );
963
964 if ( ! $this->time_checker_service->is_current_hour_in_range( $from, $to ) ) {
965 return;
966 }
967
968 /**
969 * Filters the batch size for the migration process.
970 *
971 * @since 3.1.2
972 * @return int The number of rows to process in each batch, default is 100.
973 */
974 $batch_size = (int) apply_filters( 'sequra_migration_batch_size', 100 );
975 for ( $i = 0; $i < $batch_size; $i++ ) {
976 $this->table_migration_repository->migrate_next_row();
977 if ( $this->table_migration_repository->maybe_remove_legacy_table() ) {
978 // Migration is complete.
979 break;
980 }
981 }
982 }
983 }
984