point->create( array( $purchase_unit ), $shipping_preference, $payer, $token ); $this->handle_paypal_order( $wc_order, $order ); $this->logger->info( sprintf( 'Renewal for order %d is completed.', $wc_order->get_id() ) ); } } } /** * Returns a payment token for a customer. * * @param \WC_Customer $customer The customer. * @param \WC_Order $wc_order The current WooCommerce order we want to process. * * @return PaymentToken|null|false */ private function get_token_for_customer( \WC_Customer $customer, \WC_Order $wc_order ) { /** * Returns a payment token for a customer, or null. */ $token = apply_filters( 'woocommerce_paypal_payments_subscriptions_get_token_for_customer', null, $customer, $wc_order ); if ( null !== $token ) { return $token; } $tokens = $this->repository->all_for_user_id( (int) $customer->get_id() ); if ( ! $tokens ) { return false; } $subscription = function_exists( 'wcs_get_subscription' ) ? wcs_get_subscription( $wc_order->get_meta( '_subscription_renewal' ) ) : null; if ( $subscription ) { $token_id = $subscription->get_meta( 'payment_token_id' ); if ( $token_id ) { foreach ( $tokens as $token ) { if ( $token_id === $token->id() ) { return $token; } } } } return current( $tokens ); } /** * Returns if an order should be captured immediately. * * @param Order $order The PayPal order. * * @return bool * @throws NotFoundException When a setting was not found. */ protected 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() ); foreach ( $order->purchase_units() as $unit ) { if ( $unit->contains_physical_goods() ) { return false; } } return true; } /** * Handles PayPal order creation and updates WC order accordingly. * * @param \WC_Order $wc_order WC order. * @param Order $order PayPal order. * @return void * @throws NotFoundException When something goes wrong while handling the order. */ private function handle_paypal_order( \WC_Order $wc_order, Order $order ): void { $this->add_paypal_meta( $wc_order, $order, $this->environment ); 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 ); $payment_source = $order->payment_source(); if ( $payment_source instanceof PaymentSource ) { $this->update_payment_source( $payment_source, $wc_order ); } } $this->handle_new_order_status( $order, $wc_order ); if ( $this->capture_authorized_downloads( $order ) ) { $this->authorized_payments_processor->capture_authorized_payment( $wc_order ); } } /** * Returns a Card payment source. * * @param string $token Vault token id. * @param WC_Order $wc_order WC order. * @return PaymentSource * @throws NotFoundException If setting is not found. */ private function card_payment_source( string $token, WC_Order $wc_order ): PaymentSource { $properties = array( 'vault_id' => $token, ); $subscriptions = wcs_get_subscriptions_for_renewal_order( $wc_order ); $subscription = end( $subscriptions ); if ( $subscription ) { $transaction = $this->subscription_helper->previous_transaction( $subscription ); if ( $transaction ) { $properties['stored_credential'] = array( 'payment_initiator' => 'MERCHANT', 'payment_type' => 'RECURRING', 'usage' => 'SUBSEQUENT', 'previous_transaction_reference' => $transaction, ); } } return new PaymentSource( 'card', (object) $properties ); } /** * Updates the payment source name to the one really used for the payment. * * @param PaymentSource $payment_source The Payment Source. * @param \WC_Order $wc_order WC order. * @return void */ private function update_payment_source( PaymentSource $payment_source, \WC_Order $wc_order ): void { if ( ! $payment_source->name() ) { return; } try { $wc_order->set_payment_method_title( $this->funding_source_renderer->render_name( $payment_source->name() ) ); $wc_order->save(); } catch ( \Exception $e ) { $this->logger->error( sprintf( 'Failed to update payment source to "%1$s" on order %2$d', $payment_source->name(), $wc_order->get_id() ) ); } } }