2020-08-28 08:13:45 +03:00
< ? php
/**
* Processes orders for the gateways .
*
2020-09-11 14:11:10 +03:00
* @ package WooCommerce\PayPalCommerce\WcGateway\Processor
2020-08-28 08:13:45 +03:00
*/
declare ( strict_types = 1 );
2020-09-11 14:11:10 +03:00
namespace WooCommerce\PayPalCommerce\WcGateway\Processor ;
2021-02-22 14:08:43 +02:00
use Psr\Log\LoggerInterface ;
2022-11-28 12:34:28 +01:00
use WC_Order ;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint ;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order ;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus ;
2022-11-28 12:34:28 +01:00
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException ;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory ;
2022-06-02 15:11:01 +02:00
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper ;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure ;
2021-10-06 17:44:41 +03:00
use WooCommerce\PayPalCommerce\Onboarding\Environment ;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\Session\SessionHandler ;
2021-12-22 12:18:17 +01:00
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper ;
2021-09-17 17:11:36 +02:00
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository ;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway ;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings ;
2020-08-28 08:13:45 +03:00
/**
* Class OrderProcessor
*/
class OrderProcessor {
2021-02-22 13:47:32 +02:00
2021-10-14 18:33:16 +03:00
use OrderMetaTrait , PaymentsStatusHandlingTrait , TransactionIdHandlingTrait ;
2021-10-06 17:44:41 +03:00
2021-02-22 13:47:32 +02:00
/**
2021-10-06 17:44:41 +03:00
* The environment .
2021-02-22 13:47:32 +02:00
*
2021-10-06 17:44:41 +03:00
* @ var Environment
2021-02-22 13:47:32 +02:00
*/
2021-10-06 17:44:41 +03:00
protected $environment ;
2020-08-28 08:13:45 +03:00
2021-05-10 17:12:46 +02:00
/**
* The payment token repository .
*
* @ var PaymentTokenRepository
*/
protected $payment_token_repository ;
2020-08-28 08:13:45 +03:00
/**
* The Session Handler .
*
* @ var SessionHandler
*/
private $session_handler ;
/**
* The Order Endpoint .
*
* @ var OrderEndpoint
*/
private $order_endpoint ;
/**
* The Order Factory .
*
* @ var OrderFactory
*/
private $order_factory ;
/**
* The helper for 3 d secure .
*
* @ var ThreeDSecure
*/
private $threed_secure ;
/**
* The processor for authorized payments .
*
* @ var AuthorizedPaymentsProcessor
*/
private $authorized_payments_processor ;
/**
* The settings .
*
* @ var Settings
*/
private $settings ;
/**
* The last error .
*
* @ var string
*/
private $last_error = '' ;
2021-02-22 14:40:02 +02:00
2021-02-22 14:08:43 +02:00
/**
2021-02-22 14:40:02 +02:00
* A logger .
*
2021-02-22 14:08:43 +02:00
* @ var LoggerInterface
*/
private $logger ;
2020-08-28 08:13:45 +03:00
2021-12-22 12:18:17 +01:00
/**
2022-03-04 12:13:41 +01:00
* The subscription helper .
*
2021-12-22 12:18:17 +01:00
* @ var SubscriptionHelper
*/
private $subscription_helper ;
2022-06-02 15:11:01 +02:00
/**
* The order helper .
*
* @ var OrderHelper
*/
private $order_helper ;
2023-08-16 11:48:56 +01:00
/**
* Array to store temporary order data changes to restore after processing .
*
* @ var array
*/
private $restore_order_data = array ();
2021-02-22 13:47:32 +02:00
/**
* OrderProcessor constructor .
*
* @ param SessionHandler $session_handler The Session Handler .
* @ param OrderEndpoint $order_endpoint The Order Endpoint .
* @ param OrderFactory $order_factory The Order Factory .
* @ param ThreeDSecure $three_d_secure The ThreeDSecure Helper .
* @ param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor .
* @ param Settings $settings The Settings .
2021-02-22 14:08:43 +02:00
* @ param LoggerInterface $logger A logger service .
2021-10-06 17:44:41 +03:00
* @ param Environment $environment The environment .
2022-03-04 12:13:41 +01:00
* @ param SubscriptionHelper $subscription_helper The subscription helper .
2022-06-02 15:11:01 +02:00
* @ param OrderHelper $order_helper The order helper .
2021-02-22 13:47:32 +02:00
*/
2020-08-28 08:13:45 +03:00
public function __construct (
SessionHandler $session_handler ,
OrderEndpoint $order_endpoint ,
OrderFactory $order_factory ,
ThreeDSecure $three_d_secure ,
AuthorizedPaymentsProcessor $authorized_payments_processor ,
2021-02-19 16:02:59 +02:00
Settings $settings ,
2021-02-22 14:08:43 +02:00
LoggerInterface $logger ,
2021-12-22 12:18:17 +01:00
Environment $environment ,
2022-06-02 15:11:01 +02:00
SubscriptionHelper $subscription_helper ,
OrderHelper $order_helper
2020-08-28 08:13:45 +03:00
) {
$this -> session_handler = $session_handler ;
$this -> order_endpoint = $order_endpoint ;
$this -> order_factory = $order_factory ;
$this -> threed_secure = $three_d_secure ;
$this -> authorized_payments_processor = $authorized_payments_processor ;
$this -> settings = $settings ;
2021-10-06 17:44:41 +03:00
$this -> environment = $environment ;
2021-02-22 14:08:43 +02:00
$this -> logger = $logger ;
2022-03-04 12:13:41 +01:00
$this -> subscription_helper = $subscription_helper ;
2022-06-02 15:11:01 +02:00
$this -> order_helper = $order_helper ;
2021-02-22 13:47:32 +02:00
}
2020-08-28 08:13:45 +03:00
/**
2020-09-03 07:05:50 +03:00
* Processes a given WooCommerce order and captured / authorizes the connected PayPal orders .
2020-08-28 08:13:45 +03:00
*
2021-03-17 13:59:18 +02:00
* @ param \WC_Order $wc_order The WooCommerce order .
2020-08-28 08:13:45 +03:00
*
* @ return bool
*/
2022-12-20 16:04:11 +02:00
public function process ( \WC_Order $wc_order ) : bool {
// phpcs:ignore WordPress.Security.NonceVerification
$order_id = $wc_order -> get_meta ( PayPalGateway :: ORDER_ID_META_KEY ) ? : wc_clean ( wp_unslash ( $_POST [ 'paypal_order_id' ] ? ? '' ) );
$order = $this -> session_handler -> order ();
2023-07-04 15:31:19 +03:00
if ( ! $order && is_string ( $order_id ) && $order_id ) {
2022-12-20 16:04:11 +02:00
$order = $this -> order_endpoint -> order ( $order_id );
}
2020-09-02 08:43:41 +03:00
if ( ! $order ) {
2022-11-28 12:34:28 +01:00
$order_id = $wc_order -> get_meta ( PayPalGateway :: ORDER_ID_META_KEY );
if ( ! $order_id ) {
2022-12-08 11:09:06 +01:00
$this -> logger -> warning (
sprintf (
'No PayPal order ID found in order #%d meta.' ,
$wc_order -> get_id ()
)
);
2023-07-04 15:32:41 +03:00
$this -> last_error = __ ( 'Could not retrieve order. Maybe it was already completed or this browser is not supported. Please check your email or try again with a different browser.' , 'woocommerce-paypal-payments' );
2022-11-28 12:34:28 +01:00
return false ;
}
try {
$order = $this -> order_endpoint -> order ( $order_id );
} catch ( RuntimeException $exception ) {
$this -> last_error = __ ( 'Could not retrieve PayPal order.' , 'woocommerce-paypal-payments' );
return false ;
}
2020-09-01 14:50:01 +03:00
}
2021-10-06 17:44:41 +03:00
$this -> add_paypal_meta ( $wc_order , $order , $this -> environment );
2020-08-28 08:13:45 +03:00
$error_message = null ;
2022-06-14 10:59:47 +02:00
if ( $this -> order_helper -> contains_physical_goods ( $order ) && ! $this -> order_is_ready_for_process ( $order ) ) {
2020-08-28 08:13:45 +03:00
$error_message = __ (
2022-06-07 12:17:35 +02:00
'The payment is not ready for processing yet.' ,
2020-10-08 20:03:07 -03:00
'woocommerce-paypal-payments'
2020-08-28 08:13:45 +03:00
);
}
if ( $error_message ) {
$this -> last_error = sprintf (
// translators: %s is the message of the error.
2020-10-08 20:03:07 -03:00
__ ( 'Payment error: %s' , 'woocommerce-paypal-payments' ),
2020-08-28 08:13:45 +03:00
$error_message
);
return false ;
}
$order = $this -> patch_order ( $wc_order , $order );
2021-10-06 17:44:41 +03:00
2020-08-28 08:13:45 +03:00
if ( $order -> intent () === 'CAPTURE' ) {
$order = $this -> order_endpoint -> capture ( $order );
}
if ( $order -> intent () === 'AUTHORIZE' ) {
$order = $this -> order_endpoint -> authorize ( $order );
2021-10-07 09:21:12 +03:00
2021-10-14 15:45:57 +02:00
$wc_order -> update_meta_data ( AuthorizedPaymentsProcessor :: CAPTURED_META_KEY , 'false' );
2021-12-22 12:18:17 +01:00
2022-03-04 12:13:41 +01:00
if ( $this -> subscription_helper -> has_subscription ( $wc_order -> get_id () ) ) {
$wc_order -> update_meta_data ( '_ppcp_captured_vault_webhook' , 'false' );
2021-12-22 12:18:17 +01:00
}
2020-08-28 08:13:45 +03:00
}
2021-02-22 13:47:32 +02:00
$transaction_id = $this -> get_paypal_order_transaction_id ( $order );
2021-02-19 13:54:41 +02:00
2021-10-14 16:34:03 +03:00
if ( $transaction_id ) {
2021-10-14 18:45:09 +03:00
$this -> update_transaction_id ( $transaction_id , $wc_order );
2021-02-22 13:47:32 +02:00
}
2021-02-19 13:54:41 +02:00
2021-10-07 09:21:12 +03:00
$this -> handle_new_order_status ( $order , $wc_order );
2020-08-28 08:13:45 +03:00
2022-09-28 11:45:00 +03:00
if ( $this -> capture_authorized_downloads ( $order ) ) {
$this -> authorized_payments_processor -> capture_authorized_payment ( $wc_order );
2020-08-28 08:13:45 +03:00
}
$this -> last_error = '' ;
return true ;
}
/**
* Returns if an order should be captured immediately .
*
* @ param Order $order The PayPal order .
*
* @ return bool
*/
private function capture_authorized_downloads ( Order $order ) : bool {
if (
! $this -> settings -> has ( 'capture_for_virtual_only' )
|| ! $this -> settings -> get ( 'capture_for_virtual_only' )
) {
return false ;
}
if ( $order -> intent () === 'CAPTURE' ) {
return false ;
}
/**
* We fetch the order again as the authorize endpoint ( from which the Order derives )
* drops the item ' s category , making it impossible to check , if purchase units contain
* physical goods .
*/
$order = $this -> order_endpoint -> order ( $order -> id () );
2020-09-01 09:00:45 +03:00
foreach ( $order -> purchase_units () as $unit ) {
if ( $unit -> contains_physical_goods () ) {
2020-08-28 08:13:45 +03:00
return false ;
}
}
return true ;
}
/**
* Returns the last error .
*
* @ return string
*/
public function last_error () : string {
return $this -> last_error ;
}
/**
2020-09-03 07:05:50 +03:00
* Patches a given PayPal order with a WooCommerce order .
2020-08-28 08:13:45 +03:00
*
2020-09-03 07:05:50 +03:00
* @ param \WC_Order $wc_order The WooCommerce order .
2020-08-28 08:13:45 +03:00
* @ param Order $order The PayPal order .
*
* @ return Order
*/
public function patch_order ( \WC_Order $wc_order , Order $order ) : Order {
2023-08-16 11:48:56 +01:00
$this -> apply_outbound_order_filters ( $wc_order );
2020-09-01 09:00:45 +03:00
$updated_order = $this -> order_factory -> from_wc_order ( $wc_order , $order );
2023-08-16 11:48:56 +01:00
$this -> restore_order_from_filters ( $wc_order );
$order = $this -> order_endpoint -> patch_order_with ( $order , $updated_order );
2020-08-28 08:13:45 +03:00
return $order ;
}
/**
2022-06-07 12:17:35 +02:00
* Whether a given order is ready for processing .
2020-08-28 08:13:45 +03:00
*
* @ param Order $order The order .
*
* @ return bool
*/
2022-06-07 12:17:35 +02:00
private function order_is_ready_for_process ( Order $order ) : bool {
2020-08-28 08:13:45 +03:00
2022-06-07 12:17:35 +02:00
if ( $order -> status () -> is ( OrderStatus :: APPROVED ) || $order -> status () -> is ( OrderStatus :: CREATED ) ) {
2020-08-28 08:13:45 +03:00
return true ;
}
2020-09-01 09:00:45 +03:00
if ( ! $order -> payment_source () || ! $order -> payment_source () -> card () ) {
2020-08-28 08:13:45 +03:00
return false ;
}
2022-06-07 12:17:35 +02:00
return in_array (
2020-08-31 11:12:46 +03:00
$this -> threed_secure -> proceed_with_order ( $order ),
2020-08-28 08:13:45 +03:00
array (
ThreeDSecure :: NO_DECISION ,
ThreeDSecure :: PROCCEED ,
),
true
);
}
2023-08-16 11:48:56 +01:00
/**
* Applies filters to the WC_Order , so they are reflected only on PayPal Order .
*
* @ param WC_Order $wc_order The WoocOmmerce Order .
* @ return void
*/
private function apply_outbound_order_filters ( WC_Order $wc_order ) : void {
$items = $wc_order -> get_items ();
$this -> restore_order_data [ 'names' ] = array ();
foreach ( $items as $item ) {
if ( ! $item instanceof \WC_Order_Item ) {
continue ;
}
$original_name = $item -> get_name ();
$new_name = apply_filters ( 'woocommerce_paypal_payments_order_line_item_name' , $original_name , $item -> get_id (), $wc_order -> get_id () );
if ( $new_name !== $original_name ) {
$this -> restore_order_data [ 'names' ][ $item -> get_id () ] = $original_name ;
$item -> set_name ( $new_name );
}
}
}
/**
* Restores the WC_Order to it ' s state before filters .
*
* @ param WC_Order $wc_order The WooCommerce Order .
* @ return void
*/
private function restore_order_from_filters ( WC_Order $wc_order ) : void {
2023-08-16 14:07:01 +01:00
if ( is_array ( $this -> restore_order_data [ 'names' ] ? ? null ) ) {
2023-08-16 11:48:56 +01:00
foreach ( $this -> restore_order_data [ 'names' ] as $wc_item_id => $original_name ) {
$wc_item = $wc_order -> get_item ( $wc_item_id , false );
if ( $wc_item ) {
$wc_item -> set_name ( $original_name );
}
}
}
}
2020-08-28 08:13:45 +03:00
}