$wc_order = wc_get_order($order_id); if (!is_a($wc_order, WC_Order::class)) { return $this->handle_payment_failure(null, new GatewayGenericException(new Exception('WC order was not found.'))); } // phpcs:ignore WordPress.Security.NonceVerification.Missing $funding_source = wc_clean(wp_unslash($_POST['ppcp-funding-source'] ?? $_POST['funding_source'] ?? '')); if ($funding_source) { $wc_order->set_payment_method_title($this->funding_source_renderer->render_name($funding_source)); $wc_order->save(); } if ('card' !== $funding_source && $this->is_free_trial_order($wc_order) && !$this->subscription_helper->paypal_subscription_id()) { $ppcp_guest_payment_for_free_trial = WC()->session->get('ppcp_guest_payment_for_free_trial') ?? null; if ($this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial) { $customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? ''; if ($customer_id) { update_user_meta($wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id); } if (isset($ppcp_guest_payment_for_free_trial->payment_source->paypal)) { $email = ''; if (isset($ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address)) { $email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address; } $this->wc_payment_tokens->create_payment_token_paypal($wc_order->get_customer_id(), $ppcp_guest_payment_for_free_trial->id, $email); } WC()->session->set('ppcp_guest_payment_for_free_trial', null); $wc_order->payment_complete(); return $this->handle_payment_success($wc_order); } $customer_id = get_user_meta($wc_order->get_customer_id(), '_ppcp_target_customer_id', \true); if ($customer_id) { $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer($customer_id); foreach ($customer_tokens as $token) { $payment_source_name = $token['payment_source']->name() ?? ''; if ($payment_source_name === 'paypal' || $payment_source_name === 'venmo') { $wc_order->payment_complete(); return $this->handle_payment_success($wc_order); } } } $user_id = (int) $wc_order->get_customer_id(); $tokens = $this->payment_token_repository->all_for_user_id($user_id); if (!array_filter($tokens, function (PaymentToken $token): bool { return isset($token->source()->paypal); })) { return $this->handle_payment_failure($wc_order, new Exception('No saved PayPal account.')); } $wc_order->payment_complete(); return $this->handle_payment_success($wc_order); } /** * If customer has chosen change Subscription payment. */ if ($this->subscription_helper->has_subscription($order_id) && $this->subscription_helper->is_subscription_change_payment()) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $saved_paypal_payment = wc_clean(wp_unslash($_POST['saved_paypal_payment'] ?? '')); if ($saved_paypal_payment) { $payment_token = WC_Payment_Tokens::get($saved_paypal_payment); if ($payment_token) { $wc_order->add_payment_token($payment_token); $wc_order->save(); return $this->handle_payment_success($wc_order); } wc_add_notice(__('Could not change payment.', 'woocommerce-paypal-payments'), 'error'); return array('result' => 'failure', 'redirect' => wc_get_checkout_url(), 'errorMessage' => __('Could not change payment.', 'woocommerce-paypal-payments')); } } /** * If the WC_Order is paid through the approved webhook. */ //phpcs:disable WordPress.Security.NonceVerification.Recommended if (isset($_REQUEST['ppcp-resume-order']) && $wc_order->has_status('processing')) { return $this->handle_payment_success($wc_order); } //phpcs:enable WordPress.Security.NonceVerification.Recommended try { try { /** * This filter controls if the method 'process()' from OrderProcessor will be called. * So you can implement your own for example on subscriptions * * - true bool controls execution of 'OrderProcessor::process()' * - $this \WC_Payment_Gateway * - $wc_order \WC_Order */ $process = apply_filters('woocommerce_paypal_payments_before_order_process', \true, $this, $wc_order); if ($process) { $this->order_processor->process($wc_order); } do_action('woocommerce_paypal_payments_before_handle_payment_success', $wc_order); return $this->handle_payment_success($wc_order); } catch (PayPalOrderMissingException $exc) { $order = $this->order_processor->create_order($wc_order); return array('result' => 'success', 'redirect' => ($this->paypal_checkout_url_factory)($order->id())); } } catch (PayPalApiException $error) { $retry_keys_messages = array('INSTRUMENT_DECLINED' => __('Instrument declined.', 'woocommerce-paypal-payments'), 'PAYER_ACTION_REQUIRED' => __('Payer action required, possibly overcharge.', 'woocommerce-paypal-payments')); $retry_errors = array_values(array_filter(array_keys($retry_keys_messages), function (string $key) use ($error): bool { return $error->has_detail($key); })); if ($retry_errors) { $retry_error_key = $retry_errors[0]; $wc_order->update_status('failed', $retry_keys_messages[$retry_error_key] . ' ' . $error->details()[0]->description ?? ''); $this->session_handler->increment_insufficient_funding_tries(); if ($this->session_handler->insufficient_funding_tries() >= 3) { return $this->handle_payment_failure(null, new Exception(__('Please use a different payment method.', 'woocommerce-paypal-payments'), $error->getCode(), $error)); } return array('result' => 'success', 'redirect' => ($this->paypal_checkout_url_factory)($this->session_handler->order()->id())); } return $this->handle_payment_failure($wc_order, new Exception(\WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages::generic_payment_error_message() . ' ' . $error->getMessage(), $error->getCode(), $error)); } catch (Exception $error) { return $this->handle_payment_failure($wc_order, $error); } } /** * Process refund. * * If the gateway declares 'refunds' support, this will allow it to refund. * a passed in amount. * * @param int $order_id Order ID. * @param float $amount Refund amount. * @param string $reason Refund reason. * @return boolean True or false based on success, or a WP_Error object. */ public function process_refund($order_id, $amount = null, $reason = '') { $order = wc_get_order($order_id); if (!is_a($order, \WC_Order::class)) { return \false; } return $this->refund_processor->process($order, (float) $amount, (string) $reason); } /** * Return transaction url for this gateway and given order. * * @param \WC_Order $order WC order to get transaction url by. * * @return string */ public function get_transaction_url($order): string { $this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base($order); return parent::get_transaction_url($order); } /** * Updates WooCommerce gateway option. * * @param string $key The option key. * @param string $value The option value. * @return bool was anything saved? */ public function update_option($key, $value = '') { $ret = parent::update_option($key, $value); if ('enabled' === $key) { $this->config->set('enabled', 'yes' === $value); $this->config->persist(); return \true; } return $ret; } /** * Override the parent admin_options method. */ public function admin_options() { if (!$this->admin_settings_enabled) { parent::admin_options(); } do_action('woocommerce_paypal_payments_gateway_admin_options_wrapper', $this); } /** * Returns the settings renderer. * * @return SettingsRenderer */ protected function settings_renderer(): SettingsRenderer { return $this->settings_renderer; } }