<?php /** * Handles payment through saved credit card. * * @package WooCommerce\PayPalCommerce\Vaulting */ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Vaulting; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WC_Customer; use WC_Order; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; /** * Class VaultedCreditCardHandler */ class VaultedCreditCardHandler { use OrderMetaTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait, FreeTrialHandlerTrait; /** * The subscription helper. * * @var SubscriptionHelper */ protected $subscription_helper; /** * The payment token repository. * * @var PaymentTokenRepository */ private $payment_token_repository; /** * The purchase unit factory. * * @var PurchaseUnitFactory */ private $purchase_unit_factory; /** * The payer factory. * * @var PayerFactory */ private $payer_factory; /** * The shipping_preference factory. * * @var ShippingPreferenceFactory */ private $shipping_preference_factory; /** * The order endpoint. * * @var OrderEndpoint */ private $order_endpoint; /** * The environment. * * @var Environment */ protected $environment; /** * The processor for authorized payments. * * @var AuthorizedPaymentsProcessor */ protected $authorized_payments_processor; /** * The settings. * * @var ContainerInterface */ protected $config; /** * VaultedCreditCardHandler constructor * * @param SubscriptionHelper $subscription_helper The subscription helper. * @param PaymentTokenRepository $payment_token_repository The payment token repository. * @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory. * @param PayerFactory $payer_factory The payer factory. * @param ShippingPreferenceFactory $shipping_preference_factory The shipping_preference factory. * @param OrderEndpoint $order_endpoint The order endpoint. * @param Environment $environment The environment. * @param AuthorizedPaymentsProcessor $authorized_payments_processor The processor for authorized payments. * @param ContainerInterface $config The settings. */ public function __construct( SubscriptionHelper $subscription_helper, PaymentTokenRepository $payment_token_repository, PurchaseUnitFactory $purchase_unit_factory, PayerFactory $payer_factory, ShippingPreferenceFactory $shipping_preference_factory, OrderEndpoint $order_endpoint, Environment $environment, AuthorizedPaymentsProcessor $authorized_payments_processor, ContainerInterface $config ) { $this->subscription_helper = $subscription_helper; $this->payment_token_repository = $payment_token_repository; $this->purchase_unit_factory = $purchase_unit_factory; $this->payer_factory = $payer_factory; $this->shipping_preference_factory = $shipping_preference_factory; $this->order_endpoint = $order_endpoint; $this->environment = $environment; $this->authorized_payments_processor = $authorized_payments_processor; $this->config = $config; } /** * Handles the saved credit card payment. * * @param string $saved_credit_card The saved credit card. * @param WC_Order $wc_order The WC order. * @return WC_Order * @throws RuntimeException When something went wrong with the payment process. */ public function handle_payment( string $saved_credit_card, WC_Order $wc_order ): WC_Order { $tokens = $this->payment_token_repository->all_for_user_id( $wc_order->get_customer_id() ); $selected_token = null; foreach ( $tokens as $token ) { if ( $token->id() === $saved_credit_card ) { $selected_token = $token; break; } } if ( ! $selected_token ) { throw new RuntimeException( 'Saved card token not found.' ); } $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); $payer = $this->payer_factory->from_wc_order( $wc_order ); $shipping_preference = $this->shipping_preference_factory->from_state( $purchase_unit, '' ); try { $order = $this->order_endpoint->create( array( $purchase_unit ), $shipping_preference, $payer, $selected_token ); $this->add_paypal_meta( $wc_order, $order, $this->environment ); if ( ! $order->status()->is( OrderStatus::COMPLETED ) ) { throw new RuntimeException( "Unexpected status for order {$order->id()} using a saved card: {$order->status()->name()}." ); } if ( ! in_array( $order->intent(), array( 'CAPTURE', 'AUTHORIZE' ), true ) ) { throw new RuntimeException( "Could neither capture nor authorize order {$order->id()} using a saved card. Status: {$order->status()->name()}. Intent: {$order->intent()}." ); } if ( $order->intent() === 'AUTHORIZE' ) { $order = $this->order_endpoint->authorize( $order ); $wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' ); } $transaction_id = $this->get_paypal_order_transaction_id( $order ); if ( $transaction_id ) { $this->update_transaction_id( $transaction_id, $wc_order ); } $this->handle_new_order_status( $order, $wc_order ); if ( $this->is_free_trial_order( $wc_order ) ) { $this->authorized_payments_processor->void_authorizations( $order ); $wc_order->payment_complete(); } elseif ( $this->config->has( 'intent' ) && strtoupper( (string) $this->config->get( 'intent' ) ) === 'CAPTURE' ) { $this->authorized_payments_processor->capture_authorized_payment( $wc_order ); } return $wc_order; } catch ( RuntimeException $error ) { throw new RuntimeException( $error->getMessage() ); } } }