PluginProbe
seQura / 3.1.1
seQura v3.1.1
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.1.1, at src/Services/Order/class-order-service.php

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