2020-09-14 14:17:39 +03:00
< ? php
/**
* The process_payment functionality for the both gateways .
*
* @ package WooCommerce\PayPalCommerce\WcGateway\Gateway
*/
declare ( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\WcGateway\Gateway ;
2021-10-05 17:30:00 +02:00
use Exception ;
2021-10-14 15:45:57 +02:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization ;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus ;
2021-03-25 16:11:45 +01:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus ;
2020-09-14 14:17:39 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException ;
2020-11-20 15:40:01 +01:00
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException ;
2021-10-06 17:44:41 +03:00
use WooCommerce\PayPalCommerce\Onboarding\Environment ;
2021-10-14 15:45:57 +02:00
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor ;
2021-10-06 17:44:41 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait ;
2021-10-07 09:21:12 +03:00
use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait ;
2020-09-14 14:17:39 +03:00
/**
* Trait ProcessPaymentTrait
*/
trait ProcessPaymentTrait {
2021-10-06 17:44:41 +03:00
2021-10-07 09:21:12 +03:00
use OrderMetaTrait , PaymentsStatusHandlingTrait ;
2021-10-06 17:44:41 +03:00
2020-09-14 14:17:39 +03:00
/**
* Process a payment for an WooCommerce order .
*
* @ param int $order_id The WooCommerce order id .
*
2021-03-15 16:43:39 +02:00
* @ return array
2021-10-06 15:57:57 +02:00
*
* @ throws RuntimeException When processing payment fails .
2020-09-14 14:17:39 +03:00
*/
public function process_payment ( $order_id ) {
2021-03-15 16:43:39 +02:00
$failure_data = array (
'result' => 'failure' ,
'redirect' => wc_get_checkout_url (),
);
2020-09-14 14:17:39 +03:00
$wc_order = wc_get_order ( $order_id );
if ( ! is_a ( $wc_order , \WC_Order :: class ) ) {
2021-03-15 17:33:20 +02:00
wc_add_notice (
2021-03-17 13:59:18 +02:00
__ ( 'Couldn\'t find order to process' , 'woocommerce-paypal-payments' ),
2021-03-15 17:33:20 +02:00
'error'
);
2021-03-15 16:43:39 +02:00
return $failure_data ;
2020-09-14 14:17:39 +03:00
}
2021-03-25 16:57:42 +01:00
/**
2021-05-10 17:12:46 +02:00
* If customer has chosen a saved credit card payment .
2021-03-25 16:57:42 +01:00
*/
$saved_credit_card = filter_input ( INPUT_POST , 'saved_credit_card' , FILTER_SANITIZE_STRING );
2021-05-10 17:12:46 +02:00
$pay_for_order = filter_input ( INPUT_GET , 'pay_for_order' , FILTER_SANITIZE_STRING );
if ( $saved_credit_card && ! isset ( $pay_for_order ) ) {
2021-03-25 16:11:45 +01:00
2021-03-25 16:57:42 +01:00
$user_id = ( int ) $wc_order -> get_customer_id ();
$customer = new \WC_Customer ( $user_id );
$tokens = $this -> payment_token_repository -> all_for_user_id ( ( int ) $customer -> get_id () );
2021-03-25 16:11:45 +01:00
2021-03-25 16:57:42 +01:00
$selected_token = null ;
foreach ( $tokens as $token ) {
if ( $token -> id () === $saved_credit_card ) {
$selected_token = $token ;
break ;
}
}
2021-03-25 16:11:45 +01:00
2021-03-25 16:57:42 +01:00
if ( ! $selected_token ) {
return null ;
}
2021-03-25 16:11:45 +01:00
2021-03-25 16:57:42 +01:00
$purchase_unit = $this -> purchase_unit_factory -> from_wc_order ( $wc_order );
$payer = $this -> payer_factory -> from_customer ( $customer );
try {
$order = $this -> order_endpoint -> create (
array ( $purchase_unit ),
$payer ,
$selected_token
);
2021-03-25 16:11:45 +01:00
2021-10-06 17:44:41 +03:00
$this -> add_paypal_meta ( $wc_order , $order , $this -> environment () );
2021-03-25 16:11:45 +01:00
2021-10-07 09:21:12 +03:00
if ( ! $order -> status () -> is ( OrderStatus :: COMPLETED ) ) {
$this -> logger -> warning ( " Unexpected status for order { $order -> id () } using a saved credit card: " . $order -> status () -> name () );
return null ;
2021-03-25 16:57:42 +01:00
}
2021-09-27 17:49:08 +02:00
2021-10-07 09:21:12 +03:00
if ( ! in_array (
$order -> intent (),
array ( 'CAPTURE' , 'AUTHORIZE' ),
true
) ) {
$this -> logger -> warning ( " Could neither capture nor authorize order { $order -> id () } using a saved credit card: " . 'Status: ' . $order -> status () -> name () . ' Intent: ' . $order -> intent () );
return null ;
2021-03-25 16:57:42 +01:00
}
2021-09-27 17:49:08 +02:00
2021-10-07 09:21:12 +03:00
if ( $order -> intent () === 'AUTHORIZE' ) {
$order = $this -> order_endpoint -> authorize ( $order );
2021-10-14 15:45:57 +02:00
$wc_order -> update_meta_data ( AuthorizedPaymentsProcessor :: CAPTURED_META_KEY , 'false' );
2021-09-27 17:49:08 +02:00
}
2021-10-07 09:21:12 +03:00
$this -> handle_new_order_status ( $order , $wc_order );
2021-09-27 17:49:08 +02:00
2021-03-25 16:57:42 +01:00
$this -> session_handler -> destroy_session_data ();
2021-10-07 09:21:12 +03:00
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
2021-03-25 16:57:42 +01:00
} catch ( RuntimeException $error ) {
2021-10-06 16:03:38 +03:00
$this -> handle_failure ( $wc_order , $error );
2021-03-25 16:57:42 +01:00
return null ;
}
}
2021-03-25 16:11:45 +01:00
2021-05-17 11:00:57 +02:00
/**
* If customer has chosen change Subscription payment .
*/
2021-05-19 15:39:33 +02:00
if ( $this -> subscription_helper -> has_subscription ( $order_id ) && $this -> subscription_helper -> is_subscription_change_payment () ) {
if ( 'ppcp-credit-card-gateway' === $this -> id && $saved_credit_card ) {
2021-05-17 11:00:57 +02:00
update_post_meta ( $order_id , 'payment_token_id' , $saved_credit_card );
$this -> session_handler -> destroy_session_data ();
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
}
2021-05-19 15:39:33 +02:00
$saved_paypal_payment = filter_input ( INPUT_POST , 'saved_paypal_payment' , FILTER_SANITIZE_STRING );
if ( 'ppcp-gateway' === $this -> id && $saved_paypal_payment ) {
update_post_meta ( $order_id , 'payment_token_id' , $saved_paypal_payment );
2021-05-17 11:00:57 +02:00
2021-05-19 15:39:33 +02:00
$this -> session_handler -> destroy_session_data ();
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
2021-05-17 11:00:57 +02:00
}
}
2020-09-14 14:17:39 +03:00
/**
* If the WC_Order is payed through the approved webhook .
*/
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset ( $_REQUEST [ 'ppcp-resume-order' ] ) && $wc_order -> has_status ( 'processing' ) ) {
$this -> session_handler -> destroy_session_data ();
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
}
//phpcs:enable WordPress.Security.NonceVerification.Recommended
try {
2021-03-15 17:57:55 +02:00
if ( $this -> order_processor -> process ( $wc_order ) ) {
2021-10-05 15:18:17 +02:00
2021-10-06 15:57:57 +02:00
if ( $this -> subscription_helper -> has_subscription ( $order_id ) ) {
$this -> logger -> info ( " Trying to save payment for subscription parent order # { $order_id } . " );
2021-10-05 17:30:00 +02:00
2021-10-06 15:57:57 +02:00
$tokens = $this -> payment_token_repository -> all_for_user_id ( $wc_order -> get_customer_id () );
if ( $tokens ) {
$this -> logger -> info ( " Payment for subscription parent order # { $order_id } was saved correctly. " );
2021-10-05 17:30:00 +02:00
2021-10-14 15:45:57 +02:00
$this -> authorized_payments_processor -> capture_authorized_payment ( $wc_order );
2021-10-05 15:18:17 +02:00
$this -> session_handler -> destroy_session_data ();
2021-10-05 17:30:00 +02:00
2021-10-05 15:18:17 +02:00
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
}
2021-10-06 15:57:57 +02:00
$this -> logger -> error ( " Payment for subscription parent order # { $order_id } was not saved. " );
$paypal_order_id = $wc_order -> get_meta ( PayPalGateway :: ORDER_ID_META_KEY );
if ( ! $paypal_order_id ) {
throw new RuntimeException ( 'PayPal order ID not found in meta.' );
}
$order = $this -> order_endpoint -> order ( $paypal_order_id );
$purchase_units = $order -> purchase_units ();
if ( ! $purchase_units ) {
throw new RuntimeException ( 'No purchase units.' );
}
$payments = $purchase_units [ 0 ] -> payments ();
if ( ! $payments ) {
throw new RuntimeException ( 'No payments.' );
}
$this -> logger -> debug (
sprintf (
'Trying to void order %1$s, payments: %2$s.' ,
$order -> id (),
wp_json_encode ( $payments -> to_array () )
)
);
$voidable_authorizations = array_filter (
$payments -> authorizations (),
2021-10-15 16:20:00 +03:00
function ( Authorization $authorization ) : bool {
return $authorization -> is_voidable ();
}
2021-10-06 15:57:57 +02:00
);
if ( ! $voidable_authorizations ) {
throw new RuntimeException ( 'No voidable authorizations.' );
}
2021-10-05 17:30:00 +02:00
2021-10-06 15:57:57 +02:00
foreach ( $voidable_authorizations as $authorization ) {
$this -> payments_endpoint -> void ( $authorization );
}
2021-10-05 15:18:17 +02:00
2021-10-14 15:45:57 +02:00
$this -> logger -> debug (
sprintf (
'Order %1$s voided successfully.' ,
$order -> id ()
)
);
2021-10-06 15:57:57 +02:00
$error_message = __ ( 'Could not process order because it was not possible to save the payment.' , 'woocommerce-paypal-payments' );
$wc_order -> update_status ( 'failed' , $error_message );
2021-10-05 17:30:00 +02:00
2021-10-06 15:57:57 +02:00
$subscriptions = wcs_get_subscriptions_for_order ( $order_id );
foreach ( $subscriptions as $key => $subscription ) {
if ( $subscription -> get_parent_id () === $order_id ) {
2021-10-05 17:30:00 +02:00
try {
2021-10-06 15:57:57 +02:00
$subscription -> update_status ( 'cancelled' );
2021-10-05 17:30:00 +02:00
break ;
2021-10-06 15:57:57 +02:00
} catch ( Exception $exception ) {
$this -> logger -> error ( " Could not update cancelled status on subscription # { $subscription -> get_id () } " . $exception -> getMessage () );
2021-10-05 17:30:00 +02:00
}
}
}
2021-10-05 15:18:17 +02:00
$this -> session_handler -> destroy_session_data ();
2021-10-06 15:57:57 +02:00
wc_add_notice ( $error_message , 'error' );
2021-10-05 17:30:00 +02:00
2021-10-05 15:18:17 +02:00
return $failure_data ;
}
2020-09-14 14:17:39 +03:00
$this -> session_handler -> destroy_session_data ();
return array (
'result' => 'success' ,
'redirect' => $this -> get_return_url ( $wc_order ),
);
}
} catch ( PayPalApiException $error ) {
if ( $error -> has_detail ( 'INSTRUMENT_DECLINED' ) ) {
2021-08-11 15:01:23 +02:00
$wc_order -> update_status (
'failed' ,
__ ( 'Instrument declined.' , 'woocommerce-paypal-payments' )
);
2020-09-14 14:17:39 +03:00
$this -> session_handler -> increment_insufficient_funding_tries ();
$host = $this -> config -> has ( 'sandbox_on' ) && $this -> config -> get ( 'sandbox_on' ) ?
'https://www.sandbox.paypal.com/' : 'https://www.paypal.com/' ;
$url = $host . 'checkoutnow?token=' . $this -> session_handler -> order () -> id ();
if ( $this -> session_handler -> insufficient_funding_tries () >= 3 ) {
$this -> session_handler -> destroy_session_data ();
wc_add_notice (
2020-10-08 20:03:07 -03:00
__ ( 'Please use a different payment method.' , 'woocommerce-paypal-payments' ),
2020-09-14 14:17:39 +03:00
'error'
);
2021-03-15 16:43:39 +02:00
return $failure_data ;
2020-09-14 14:17:39 +03:00
}
return array (
'result' => 'success' ,
'redirect' => $url ,
);
}
$this -> session_handler -> destroy_session_data ();
2020-11-20 15:40:01 +01:00
} catch ( RuntimeException $error ) {
2021-10-06 16:03:38 +03:00
$this -> handle_failure ( $wc_order , $error );
2021-03-15 16:43:39 +02:00
return $failure_data ;
2020-09-14 14:17:39 +03:00
}
2020-11-20 15:40:01 +01:00
2020-09-14 14:17:39 +03:00
wc_add_notice (
$this -> order_processor -> last_error (),
'error'
);
2021-08-13 11:00:55 +02:00
$wc_order -> update_status (
'failed' ,
__ ( 'Could not process order.' , 'woocommerce-paypal-payments' )
);
2020-09-14 14:17:39 +03:00
2021-03-15 16:43:39 +02:00
return $failure_data ;
2020-09-14 14:17:39 +03:00
}
2021-03-10 12:10:12 +01:00
2021-04-22 12:43:17 +02:00
/**
* Checks if PayPal or Credit Card gateways are enabled .
*
* @ return bool Whether any of the gateways is enabled .
*/
protected function gateways_enabled () : bool {
if ( $this -> config -> has ( 'enabled' ) && $this -> config -> get ( 'enabled' ) ) {
return true ;
}
if ( $this -> config -> has ( 'dcc_enabled' ) && $this -> config -> get ( 'dcc_enabled' ) ) {
return true ;
}
return false ;
}
2021-03-10 12:10:12 +01:00
/**
2021-04-23 10:33:28 +02:00
* Checks if vault setting is enabled .
2021-03-10 12:55:35 +01:00
*
* @ return bool Whether vault settings are enabled or not .
* @ throws \WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException When a setting hasn ' t been found .
2021-03-10 12:10:12 +01:00
*/
2021-04-23 10:33:28 +02:00
protected function vault_setting_enabled () : bool {
2021-03-12 10:52:07 +01:00
if ( $this -> config -> has ( 'vault_enabled' ) && $this -> config -> get ( 'vault_enabled' ) ) {
return true ;
2021-03-10 12:10:12 +01:00
}
2021-03-12 10:52:07 +01:00
return false ;
2021-03-10 12:10:12 +01:00
}
2021-10-06 15:57:57 +02:00
2021-10-06 16:03:38 +03:00
/**
* Handles the payment failure .
*
* @ param \WC_Order $wc_order The order .
* @ param Exception $error The error causing the failure .
*/
protected function handle_failure ( \WC_Order $wc_order , Exception $error ) : void {
$this -> logger -> error ( 'Payment failed: ' . $error -> getMessage () );
$wc_order -> update_status (
'failed' ,
__ ( 'Could not process order.' , 'woocommerce-paypal-payments' )
);
$this -> session_handler -> destroy_session_data ();
wc_add_notice ( $error -> getMessage (), 'error' );
}
2021-10-06 17:44:41 +03:00
/**
* Returns the environment .
*
* @ return Environment
*/
abstract protected function environment () : Environment ;
2020-09-14 14:17:39 +03:00
}