mirror of
https://gh.wpcy.net/https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2026-04-30 04:42:19 +08:00
773 lines
38 KiB
PHP
773 lines
38 KiB
PHP
<?php
|
|
|
|
/**
|
|
* The Gateway module.
|
|
*
|
|
* @package WooCommerce\PayPalCommerce\WcGateway
|
|
*/
|
|
declare (strict_types=1);
|
|
namespace WooCommerce\PayPalCommerce\WcGateway;
|
|
|
|
use Exception;
|
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
|
|
use Throwable;
|
|
use WC_Order;
|
|
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
|
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\Authorization;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\Capture;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\ReferenceTransactionStatus;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
|
use WooCommerce\PayPalCommerce\LocalAlternativePaymentMethods\LocalApmProductStatus;
|
|
use WooCommerce\PayPalCommerce\Settings\Data\Definition\FeaturesDefinition;
|
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
|
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Admin\OrderTablePaymentStatusColumn;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Admin\PaymentStatusOrderDetail;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Assets\FraudNetAssets;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Assets\SettingsPageAssets;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Assets\VoidButtonAssets;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Checkout\CheckoutPayPalAddressPreset;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Checkout\DisableGateways;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ShippingCallbackEndpoint;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewayRepository;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\OXXO\OXXOGateway;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\CardPaymentsConfiguration;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\InstallmentsProductStatus;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\PWCProductStatus;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Notice\ConnectAdminNotice;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Notice\GatewayWithoutPayPalAdminNotice;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Notice\SendOnlyCountryNotice;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Notice\UnsupportedCurrencyAdminNotice;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Processor\CreditCardOrderInfoHandlingTrait;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|
use WooCommerce\PayPalCommerce\WcGateway\WcInboxNotes\InboxNoteRegistrar;
|
|
use WooCommerce\PayPalCommerce\WcGateway\WcTasks\Registrar\TaskRegistrarInterface;
|
|
use WooCommerce\PayPalCommerce\Settings\Data\SettingsProvider;
|
|
/**
|
|
* Class WcGatewayModule
|
|
*/
|
|
class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModule
|
|
{
|
|
use ModuleClassNameIdTrait;
|
|
use CreditCardOrderInfoHandlingTrait;
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function services(): array
|
|
{
|
|
return require __DIR__ . '/../services.php';
|
|
}
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function extensions(): array
|
|
{
|
|
return require __DIR__ . '/../extensions.php';
|
|
}
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function run(ContainerInterface $c): bool
|
|
{
|
|
$this->register_payment_gateways($c);
|
|
$this->register_order_functionality($c);
|
|
$this->register_contact_handlers();
|
|
$this->register_columns($c);
|
|
$this->register_checkout_paypal_address_preset($c);
|
|
$this->register_wc_tasks($c);
|
|
$this->register_woo_inbox_notes($c);
|
|
$this->register_void_button($c);
|
|
add_action('woocommerce_paypal_payments_order_captured', function (WC_Order $wc_order, Capture $capture) use ($c) {
|
|
$breakdown = $capture->seller_receivable_breakdown();
|
|
if ($breakdown) {
|
|
$wc_order->update_meta_data(PayPalGateway::FEES_META_KEY, $breakdown->to_array());
|
|
$paypal_fee = $breakdown->paypal_fee();
|
|
if ($paypal_fee) {
|
|
$wc_order->update_meta_data('PayPal Transaction Fee', (string) $paypal_fee->value());
|
|
}
|
|
$wc_order->save_meta_data();
|
|
}
|
|
$order = $c->get('session.handler')->order();
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
$fraud = $capture->fraud_processor_response();
|
|
if ($fraud) {
|
|
$this->handle_fraud($fraud, $order, $wc_order);
|
|
}
|
|
$this->handle_three_d_secure($order, $wc_order);
|
|
}, 10, 2);
|
|
add_action('woocommerce_paypal_payments_order_authorized', function (WC_Order $wc_order, Authorization $authorization) use ($c) {
|
|
$order = $c->get('session.handler')->order();
|
|
if (!$order) {
|
|
return;
|
|
}
|
|
$fraud = $authorization->fraud_processor_response();
|
|
if ($fraud) {
|
|
$this->handle_fraud($fraud, $order, $wc_order);
|
|
}
|
|
$this->handle_three_d_secure($order, $wc_order);
|
|
}, 10, 2);
|
|
$fees_renderer = $c->get('wcgateway.admin.fees-renderer');
|
|
assert($fees_renderer instanceof FeesRenderer);
|
|
add_action('woocommerce_admin_order_totals_after_total', function (int $order_id) use ($fees_renderer) {
|
|
$wc_order = wc_get_order($order_id);
|
|
if (!$wc_order instanceof WC_Order) {
|
|
return;
|
|
}
|
|
/**
|
|
* The filter can be used to remove the rows with PayPal fees in WC orders.
|
|
*/
|
|
if (!apply_filters('woocommerce_paypal_payments_show_fees_on_order_admin_page', \true, $wc_order)) {
|
|
return;
|
|
}
|
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
echo $fees_renderer->render($wc_order);
|
|
});
|
|
add_action('admin_enqueue_scripts', function () use ($c) {
|
|
if (!is_admin() || wp_doing_ajax()) {
|
|
return;
|
|
}
|
|
if (!$c->has('wcgateway.asset_getter')) {
|
|
return;
|
|
}
|
|
$settings_status = $c->get('wcgateway.settings.status');
|
|
assert($settings_status instanceof SettingsStatus);
|
|
$settings = $c->get('wcgateway.settings');
|
|
assert($settings instanceof Settings);
|
|
$dcc_configuration = $c->get('wcgateway.configuration.card-configuration');
|
|
assert($dcc_configuration instanceof CardPaymentsConfiguration);
|
|
// todo: #legacy-ui assets that can be removed.
|
|
$assets = new SettingsPageAssets($c->get('wcgateway.asset_getter'), $c->get('ppcp.asset-version'), $c->get('wc-subscriptions.helper'), $c->get('button.client_id_for_admin'), $c->get('api.shop.currency.getter'), $c->get('api.shop.country'), $c->get('settings.environment'), $settings_status->is_pay_later_button_enabled(), $settings->has('disable_funding') ? $settings->get('disable_funding') : array(), array(), $c->get('wcgateway.is-plugin-settings-page'), $dcc_configuration->is_enabled(), $c->get('api.reference-transaction-status'), $c->get('wcgateway.is-plugin-settings-page'));
|
|
$assets->register_assets();
|
|
});
|
|
// todo: remove this with #legacy-ui code?
|
|
add_filter(Repository::NOTICES_FILTER, static function ($notices) use ($c): array {
|
|
$notice = $c->get('wcgateway.notice.connect');
|
|
assert($notice instanceof ConnectAdminNotice);
|
|
$connect_message = $notice->connect_message();
|
|
if ($connect_message) {
|
|
$notices[] = $connect_message;
|
|
}
|
|
$notice = $c->get('wcgateway.notice.currency-unsupported');
|
|
assert($notice instanceof UnsupportedCurrencyAdminNotice);
|
|
$unsupported_currency_message = $notice->unsupported_currency_message();
|
|
if ($unsupported_currency_message) {
|
|
$notices[] = $unsupported_currency_message;
|
|
}
|
|
foreach (array($c->get('wcgateway.notice.dcc-without-paypal'), $c->get('wcgateway.notice.card-button-without-paypal')) as $gateway_without_paypal_notice) {
|
|
assert($gateway_without_paypal_notice instanceof GatewayWithoutPayPalAdminNotice);
|
|
$message = $gateway_without_paypal_notice->message();
|
|
if ($message) {
|
|
$notices[] = $message;
|
|
}
|
|
}
|
|
$send_only_country_notice = $c->get('wcgateway.notice.send-only-country');
|
|
assert($send_only_country_notice instanceof SendOnlyCountryNotice);
|
|
$message = $send_only_country_notice->message();
|
|
if ($message) {
|
|
$notices[] = $message;
|
|
}
|
|
$authorize_order_action = $c->get('wcgateway.notice.authorize-order-action');
|
|
$authorized_message = $authorize_order_action->message();
|
|
if ($authorized_message) {
|
|
$notices[] = $authorized_message;
|
|
}
|
|
return $notices;
|
|
});
|
|
add_action('woocommerce_paypal_commerce_gateway_deactivate', static function () {
|
|
delete_option(Settings::KEY);
|
|
delete_option('woocommerce_' . PayPalGateway::ID . '_settings');
|
|
delete_option('woocommerce_' . CreditCardGateway::ID . '_settings');
|
|
});
|
|
add_action('wc_ajax_' . ReturnUrlEndpoint::ENDPOINT, static function () use ($c) {
|
|
$endpoint = $c->get('wcgateway.endpoint.return-url');
|
|
/**
|
|
* The Endpoint.
|
|
*
|
|
* @var ReturnUrlEndpoint $endpoint
|
|
*/
|
|
$endpoint->handle_request();
|
|
});
|
|
add_action('wc_ajax_' . RefreshFeatureStatusEndpoint::ENDPOINT, static function () use ($c) {
|
|
$endpoint = $c->get('wcgateway.endpoint.refresh-feature-status');
|
|
assert($endpoint instanceof RefreshFeatureStatusEndpoint);
|
|
$endpoint->handle_request();
|
|
});
|
|
add_action('woocommerce_paypal_payments_gateway_migrate', static function () use ($c) {
|
|
delete_option('ppcp-request-ids');
|
|
$settings = $c->get('wcgateway.settings');
|
|
assert($settings instanceof Settings);
|
|
try {
|
|
if ($settings->has('3d_secure_contingency') && $settings->get('3d_secure_contingency') === '3D_SECURE') {
|
|
$settings->set('3d_secure_contingency', 'SCA_ALWAYS');
|
|
$settings->persist();
|
|
}
|
|
} catch (NotFoundException $exception) {
|
|
return;
|
|
}
|
|
});
|
|
// Clear the cached ProductStatus information on plugin update.
|
|
add_action('woocommerce_paypal_payments_gateway_migrate_on_update', static function () {
|
|
do_action('woocommerce_paypal_payments_clear_apm_product_status');
|
|
});
|
|
add_action('woocommerce_paypal_payments_clear_apm_product_status', static function () use ($c) {
|
|
$dcc_status = $c->get('wcgateway.helper.dcc-product-status');
|
|
assert($dcc_status instanceof DCCProductStatus);
|
|
$dcc_status->clear();
|
|
$dcc_status->is_active();
|
|
$pui_status = $c->get('wcgateway.pay-upon-invoice-product-status');
|
|
assert($pui_status instanceof PayUponInvoiceProductStatus);
|
|
$pui_status->clear();
|
|
$pui_status->is_active();
|
|
}, 20);
|
|
add_action('wp_loaded', function () use ($c) {
|
|
if ('DE' === $c->get('api.shop.country')) {
|
|
$c->get('wcgateway.pay-upon-invoice')->init();
|
|
}
|
|
$c->get('wcgateway.oxxo')->init();
|
|
$fraudnet_assets = $c->get('wcgateway.fraudnet-assets');
|
|
assert($fraudnet_assets instanceof FraudNetAssets);
|
|
$fraudnet_assets->register_assets();
|
|
});
|
|
add_action('woocommerce_paypal_payments_check_pui_payment_captured', function (int $wc_order_id, string $order_id) use ($c) {
|
|
$order_endpoint = $c->get('api.endpoint.order');
|
|
$logger = $c->get('woocommerce.logger.woocommerce');
|
|
$order = $order_endpoint->order($order_id);
|
|
$order_status = $order->status();
|
|
$logger->info("Checking payment captured webhook for WC order #{$wc_order_id}, PayPal order status: " . $order_status->name());
|
|
$wc_order = wc_get_order($wc_order_id);
|
|
if (!$wc_order instanceof WC_Order || $wc_order->get_status() !== 'on-hold') {
|
|
return;
|
|
}
|
|
if ($order_status->name() !== OrderStatus::COMPLETED) {
|
|
$message = __('Could not process WC order because PAYMENT.CAPTURE.COMPLETED webhook not received.', 'woocommerce-paypal-payments');
|
|
$logger->error($message);
|
|
$wc_order->update_status('failed', $message);
|
|
}
|
|
}, 10, 2);
|
|
add_action('woocommerce_order_status_changed', static function (int $order_id, string $from, string $to) use ($c) {
|
|
$wc_order = wc_get_order($order_id);
|
|
if (!$wc_order instanceof WC_Order) {
|
|
return;
|
|
}
|
|
$settings = $c->get('settings.settings-provider');
|
|
assert($settings instanceof SettingsProvider);
|
|
if (!$settings->capture_on_status_change()) {
|
|
return;
|
|
}
|
|
$gateway_repository = $c->get('wcgateway.gateway-repository');
|
|
assert($gateway_repository instanceof GatewayRepository);
|
|
// Only allow proceeding if the payment method is one of our Gateways.
|
|
if (!$gateway_repository->exists($wc_order->get_payment_method())) {
|
|
return;
|
|
}
|
|
$intent = strtoupper((string) $wc_order->get_meta(PayPalGateway::INTENT_META_KEY));
|
|
$captured = wc_string_to_bool($wc_order->get_meta(AuthorizedPaymentsProcessor::CAPTURED_META_KEY));
|
|
if ($intent !== 'AUTHORIZE' || $captured) {
|
|
return;
|
|
}
|
|
/**
|
|
* The filter returning the WC order statuses which trigger capturing of payment authorization.
|
|
*/
|
|
$capture_statuses = apply_filters('woocommerce_paypal_payments_auto_capture_statuses', array('processing', 'completed'), $wc_order);
|
|
if (!in_array($to, $capture_statuses, \true)) {
|
|
return;
|
|
}
|
|
$authorized_payment_processor = $c->get('wcgateway.processor.authorized-payments');
|
|
assert($authorized_payment_processor instanceof AuthorizedPaymentsProcessor);
|
|
try {
|
|
if ($authorized_payment_processor->capture_authorized_payment($wc_order)) {
|
|
return;
|
|
}
|
|
} catch (Throwable $error) {
|
|
$logger = $c->get('woocommerce.logger.woocommerce');
|
|
assert($logger instanceof LoggerInterface);
|
|
$logger->error("Capture failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}");
|
|
}
|
|
$wc_order->update_status('failed', __('Could not capture the payment.', 'woocommerce-paypal-payments'));
|
|
}, 10, 3);
|
|
// Clears product status when appropriate.
|
|
add_action('woocommerce_paypal_payments_clear_apm_product_status', static function () use ($c): void {
|
|
// Clear DCC Product status.
|
|
$dcc_product_status = $c->get('wcgateway.helper.dcc-product-status');
|
|
if ($dcc_product_status instanceof DCCProductStatus) {
|
|
$dcc_product_status->clear();
|
|
}
|
|
// Clear Pay Upon Invoice status.
|
|
$pui_product_status = $c->get('wcgateway.pay-upon-invoice-product-status');
|
|
if ($pui_product_status instanceof PayUponInvoiceProductStatus) {
|
|
$pui_product_status->clear();
|
|
}
|
|
// Clear PWC status.
|
|
$pwc_product_status = $c->get('wcgateway.pwc-product-status');
|
|
if ($pwc_product_status instanceof PWCProductStatus) {
|
|
$pwc_product_status->clear();
|
|
}
|
|
$reference_transaction_status_cache = $c->get('api.reference-transaction-status-cache');
|
|
assert($reference_transaction_status_cache instanceof Cache);
|
|
// Clear Reference Transaction status.
|
|
if ($reference_transaction_status_cache->has(ReferenceTransactionStatus::CACHE_KEY)) {
|
|
$reference_transaction_status_cache->delete(ReferenceTransactionStatus::CACHE_KEY);
|
|
}
|
|
});
|
|
/**
|
|
* Param types removed to avoid third-party issues.
|
|
*
|
|
* @psalm-suppress MissingClosureParamType
|
|
*/
|
|
add_filter('woocommerce_admin_billing_fields', fn($fields) => $this->insert_custom_fields_into_order_details($fields));
|
|
/**
|
|
* Param types removed to avoid third-party issues.
|
|
*
|
|
* @psalm-suppress MissingClosureParamType
|
|
*/
|
|
add_action('woocommerce_admin_order_data_after_shipping_address', fn($order) => $this->display_original_contact_in_order_details($order));
|
|
add_action('woocommerce_paypal_payments_gateway_migrate', function (string $installed_plugin_version) use ($c) {
|
|
$settings = $c->get('wcgateway.settings');
|
|
assert($settings instanceof Settings);
|
|
if (!$installed_plugin_version) {
|
|
$settings->set('allow_local_apm_gateways', \true);
|
|
$settings->persist();
|
|
}
|
|
});
|
|
add_filter('woocommerce_paypal_payments_rest_common_merchant_features', static function (array $features) use ($c): array {
|
|
$is_connected = $c->get('settings.flag.is-connected');
|
|
if (!$is_connected) {
|
|
return $features;
|
|
}
|
|
$reference_transaction_status = $c->get('api.reference-transaction-status');
|
|
assert($reference_transaction_status instanceof ReferenceTransactionStatus);
|
|
$dcc_product_status = $c->get('wcgateway.helper.dcc-product-status');
|
|
assert($dcc_product_status instanceof DCCProductStatus);
|
|
$dcc_applies = $c->get('api.helpers.dccapplies');
|
|
$apms_product_status = $c->get('ppcp-local-apms.product-status');
|
|
assert($apms_product_status instanceof LocalApmProductStatus);
|
|
$installments_product_status = $c->get('wcgateway.installments-product-status');
|
|
assert($installments_product_status instanceof InstallmentsProductStatus);
|
|
$pwc_product_status = $c->get('wcgateway.pwc-product-status');
|
|
assert($pwc_product_status instanceof PWCProductStatus);
|
|
$contact_module_check = $c->get('wcgateway.contact-module.eligibility.check');
|
|
assert(is_callable($contact_module_check));
|
|
$pui_product_status = $c->get('wcgateway.pay-upon-invoice-product-status');
|
|
assert($pui_product_status instanceof PayUponInvoiceProductStatus);
|
|
$save_payment_methods_check = $c->get('save-payment-methods.eligibility.check');
|
|
assert(is_callable($save_payment_methods_check));
|
|
$features[FeaturesDefinition::FEATURE_SAVE_PAYPAL_AND_VENMO] = array('enabled' => $reference_transaction_status->reference_transaction_enabled() && $save_payment_methods_check());
|
|
$features[FeaturesDefinition::FEATURE_ADVANCED_CREDIT_AND_DEBIT_CARDS] = array('enabled' => $dcc_product_status->is_active() && $dcc_applies->for_country_currency());
|
|
$features[FeaturesDefinition::FEATURE_ALTERNATIVE_PAYMENT_METHODS] = array('enabled' => $apms_product_status->is_active());
|
|
// When local APMs are available, then PayLater messaging is also available.
|
|
$features[FeaturesDefinition::FEATURE_PAY_LATER_MESSAGING] = array('enabled' => $features[FeaturesDefinition::FEATURE_ALTERNATIVE_PAYMENT_METHODS]['enabled']);
|
|
$features[FeaturesDefinition::FEATURE_INSTALLMENTS] = array('enabled' => $installments_product_status->is_active());
|
|
$features[FeaturesDefinition::FEATURE_PAY_WITH_CRYPTO] = array('enabled' => $pwc_product_status->is_active());
|
|
$features[FeaturesDefinition::FEATURE_CONTACT_MODULE] = array('enabled' => $contact_module_check());
|
|
$features[FeaturesDefinition::FEATURE_PAY_UPON_INVOICE] = array('enabled' => $pui_product_status->is_active());
|
|
return $features;
|
|
});
|
|
add_action('rest_api_init', static function () use ($c) {
|
|
$endpoint = $c->get('wcgateway.shipping.callback.endpoint');
|
|
assert($endpoint instanceof ShippingCallbackEndpoint);
|
|
$endpoint->register();
|
|
});
|
|
// Add processing instruction request data for OXXO payment.
|
|
add_filter('ppcp_create_order_request_body_data', static function (array $data, string $payment_method, array $request): array {
|
|
if ($payment_method !== OXXOGateway::ID) {
|
|
return $data;
|
|
}
|
|
$processing_instruction = $request['processing_instruction'] ?? '';
|
|
if ($processing_instruction) {
|
|
$data['processing_instruction'] = $processing_instruction;
|
|
}
|
|
return $data;
|
|
}, 10, 3);
|
|
return \true;
|
|
}
|
|
/**
|
|
* Registers the payment gateways.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
*/
|
|
private function register_payment_gateways(ContainerInterface $container)
|
|
{
|
|
add_filter('woocommerce_payment_gateways', static function ($methods) use ($container): array {
|
|
$paypal_gateway = $container->get('wcgateway.paypal-gateway');
|
|
assert($paypal_gateway instanceof \WC_Payment_Gateway);
|
|
$paypal_gateway_enabled = wc_string_to_bool($paypal_gateway->get_option('enabled'));
|
|
$methods[] = $paypal_gateway;
|
|
$settings = $container->get('wcgateway.settings');
|
|
assert($settings instanceof ContainerInterface);
|
|
$is_our_page = $container->get('wcgateway.is-plugin-settings-page');
|
|
$is_gateways_list_page = $container->get('wcgateway.is-wc-gateways-list-page');
|
|
$is_connected = $container->get('settings.flag.is-connected');
|
|
if (!$is_connected) {
|
|
return $methods;
|
|
}
|
|
$dcc_configuration = $container->get('wcgateway.configuration.card-configuration');
|
|
assert($dcc_configuration instanceof CardPaymentsConfiguration);
|
|
$standard_card_button = get_option('woocommerce_ppcp-card-button-gateway_settings');
|
|
if ($dcc_configuration->is_acdc_enabled() && isset($standard_card_button['enabled'])) {
|
|
$standard_card_button['enabled'] = 'no';
|
|
update_option('woocommerce_ppcp-card-button-gateway_settings', $standard_card_button);
|
|
}
|
|
$dcc_applies = $container->get('api.helpers.dccapplies');
|
|
assert($dcc_applies instanceof DccApplies);
|
|
$dcc_product_status = $container->get('wcgateway.helper.dcc-product-status');
|
|
assert($dcc_product_status instanceof DCCProductStatus);
|
|
if ($dcc_applies->for_country_currency() && ($is_our_page || $dcc_product_status->is_active())) {
|
|
$methods[] = $container->get('wcgateway.credit-card-gateway');
|
|
}
|
|
if ($paypal_gateway_enabled && apply_filters('woocommerce_paypal_payments_card_button_gateway_should_register_gateway', $container->get('wcgateway.settings.allow_card_button_gateway'))) {
|
|
$methods[] = $container->get('wcgateway.card-button-gateway');
|
|
}
|
|
$pui_product_status = $container->get('wcgateway.pay-upon-invoice-product-status');
|
|
assert($pui_product_status instanceof PayUponInvoiceProductStatus);
|
|
$shop_country = $container->get('api.shop.country');
|
|
if ('DE' === $shop_country && ($is_our_page || $is_gateways_list_page || $pui_product_status->is_active())) {
|
|
$methods[] = $container->get('wcgateway.pay-upon-invoice-gateway');
|
|
}
|
|
if ('MX' === $shop_country) {
|
|
$methods[] = $container->get('wcgateway.oxxo-gateway');
|
|
}
|
|
return (array) $methods;
|
|
});
|
|
add_filter('woocommerce_available_payment_gateways', static function ($methods) use ($container): array {
|
|
$disabler = $container->get('wcgateway.disabler');
|
|
/**
|
|
* The Gateway disabler.
|
|
*
|
|
* @var DisableGateways $disabler
|
|
*/
|
|
return $disabler->handler((array) $methods);
|
|
});
|
|
}
|
|
/**
|
|
* Registers the authorize order functionality.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
*/
|
|
private function register_order_functionality(ContainerInterface $container)
|
|
{
|
|
add_filter('woocommerce_order_actions', static function ($order_actions) use ($container): array {
|
|
global $theorder;
|
|
if (!$theorder instanceof WC_Order) {
|
|
return $order_actions;
|
|
}
|
|
$render_reauthorize = $container->get('wcgateway.admin.render-reauthorize-action');
|
|
$render_authorize = $container->get('wcgateway.admin.render-authorize-action');
|
|
// Renders the authorize action in the select field.
|
|
return $render_reauthorize->render($render_authorize->render($order_actions, $theorder), $theorder);
|
|
});
|
|
add_action('woocommerce_order_action_ppcp_authorize_order', static function (WC_Order $wc_order) use ($container) {
|
|
/**
|
|
* The authorized payments processor.
|
|
*
|
|
* @var AuthorizedPaymentsProcessor $authorized_payments_processor
|
|
*/
|
|
$authorized_payments_processor = $container->get('wcgateway.processor.authorized-payments');
|
|
$authorized_payments_processor->capture_authorized_payment($wc_order);
|
|
});
|
|
add_action('woocommerce_order_action_ppcp_reauthorize_order', static function (WC_Order $wc_order) use ($container) {
|
|
$admin_notices = $container->get('admin-notices.repository');
|
|
assert($admin_notices instanceof Repository);
|
|
/**
|
|
* The authorized payments processor.
|
|
*
|
|
* @var AuthorizedPaymentsProcessor $authorized_payments_processor
|
|
*/
|
|
$authorized_payments_processor = $container->get('wcgateway.processor.authorized-payments');
|
|
if ($authorized_payments_processor->reauthorize_payment($wc_order) !== AuthorizedPaymentsProcessor::SUCCESSFUL) {
|
|
$message = sprintf('%1$s %2$s', esc_html__('Reauthorization with PayPal failed: ', 'woocommerce-paypal-payments'), $authorized_payments_processor->reauthorization_failure_reason() ?: '');
|
|
$admin_notices->persist(new Message($message, 'error'));
|
|
} else {
|
|
$admin_notices->persist(new Message('Payment reauthorized.', 'info'));
|
|
$wc_order->add_order_note(__('Payment reauthorized.', 'woocommerce-paypal-payments'));
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Registers the additional columns on the order list page.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
*/
|
|
private function register_columns(ContainerInterface $container)
|
|
{
|
|
add_action('woocommerce_order_actions_start', static function ($wc_order_id) use ($container) {
|
|
/**
|
|
* The Payment Status Order Detail.
|
|
*
|
|
* @var PaymentStatusOrderDetail $class
|
|
*/
|
|
$class = $container->get('wcgateway.admin.order-payment-status');
|
|
$class->render(intval($wc_order_id));
|
|
});
|
|
add_filter('manage_edit-shop_order_columns', static function ($columns) use ($container) {
|
|
/**
|
|
* The Order Table Payment Status object.
|
|
*
|
|
* @var OrderTablePaymentStatusColumn $payment_status_column
|
|
*/
|
|
$payment_status_column = $container->get('wcgateway.admin.orders-payment-status-column');
|
|
return $payment_status_column->register($columns);
|
|
});
|
|
add_action('manage_shop_order_posts_custom_column', static function ($column, $wc_order_id) use ($container) {
|
|
/**
|
|
* The column object.
|
|
*
|
|
* @var OrderTablePaymentStatusColumn $payment_status_column
|
|
*/
|
|
$payment_status_column = $container->get('wcgateway.admin.orders-payment-status-column');
|
|
$payment_status_column->render((string) $column, intval($wc_order_id));
|
|
}, 10, 2);
|
|
}
|
|
/**
|
|
* Registers the PayPal Address preset to overwrite Shipping in checkout.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
*/
|
|
private function register_checkout_paypal_address_preset(ContainerInterface $container)
|
|
{
|
|
add_filter('woocommerce_checkout_get_value', static function (...$args) use ($container) {
|
|
/**
|
|
* Its important to not instantiate the service too early as it
|
|
* depends on SessionHandler and WooCommerce Session.
|
|
*/
|
|
/**
|
|
* The CheckoutPayPalAddressPreset object.
|
|
*
|
|
* @var CheckoutPayPalAddressPreset $service
|
|
*/
|
|
$service = $container->get('wcgateway.checkout.address-preset');
|
|
return $service->filter_checkout_field(...$args);
|
|
}, 10, 2);
|
|
}
|
|
/**
|
|
* Registers the tasks inside "Things to do next" WC section.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
* @return void
|
|
*/
|
|
protected function register_wc_tasks(ContainerInterface $container): void
|
|
{
|
|
add_action('init', static function () use ($container): void {
|
|
$logger = $container->get('woocommerce.logger.woocommerce');
|
|
assert($logger instanceof LoggerInterface);
|
|
try {
|
|
$simple_redirect_tasks = $container->get('wcgateway.settings.wc-tasks.simple-redirect-tasks');
|
|
if (empty($simple_redirect_tasks)) {
|
|
return;
|
|
}
|
|
$task_registrar = $container->get('wcgateway.settings.wc-tasks.task-registrar');
|
|
assert($task_registrar instanceof TaskRegistrarInterface);
|
|
$task_registrar->register('extended', $simple_redirect_tasks);
|
|
} catch (Exception $exception) {
|
|
$logger->error("Failed to create a task in the 'Things to do next' section of WC. " . $exception->getMessage());
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Registers inbox notes in the WooCommerce Admin inbox section.
|
|
*/
|
|
protected function register_woo_inbox_notes(ContainerInterface $container): void
|
|
{
|
|
add_action('admin_init', static function () use ($container): void {
|
|
$logger = $container->get('woocommerce.logger.woocommerce');
|
|
assert($logger instanceof LoggerInterface);
|
|
$inbox_note_registrar = $container->get('wcgateway.settings.inbox-note-registrar');
|
|
assert($inbox_note_registrar instanceof InboxNoteRegistrar);
|
|
try {
|
|
$inbox_note_registrar->register();
|
|
} catch (Exception $exception) {
|
|
$logger->error('Failed to add note to the WooCommerce inbox section. ' . $exception->getMessage());
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Registers the assets and ajax endpoint for the void button.
|
|
*
|
|
* @param ContainerInterface $container The container.
|
|
*/
|
|
protected function register_void_button(ContainerInterface $container): void
|
|
{
|
|
add_action('admin_enqueue_scripts', static function () use ($container) {
|
|
$assets = $container->get('wcgateway.void-button.assets');
|
|
assert($assets instanceof VoidButtonAssets);
|
|
if ($assets->should_register()) {
|
|
$assets->register();
|
|
}
|
|
});
|
|
add_action('wc_ajax_' . VoidOrderEndpoint::ENDPOINT, static function () use ($container) {
|
|
$endpoint = $container->get('wcgateway.void-button.endpoint');
|
|
assert($endpoint instanceof VoidOrderEndpoint);
|
|
$endpoint->handle_request();
|
|
});
|
|
}
|
|
/**
|
|
* Checks, if the provided argument is a WC_Order which was paid directly by PayPal.
|
|
*
|
|
* Only considers direct PayPal payments, and returns false for orders that were paid "via"
|
|
* PayPal, like wallets (Google Pay, ...) or local APMs.
|
|
*
|
|
* @param WC_Order|mixed $order The order to verify.
|
|
* @return bool True, if it's a valid order that was paid via PayPal.
|
|
*/
|
|
private function is_order_paid_by_paypal($order): bool
|
|
{
|
|
if (!$order instanceof WC_Order) {
|
|
return \false;
|
|
}
|
|
if (!$order->get_meta(PayPalGateway::ORDER_PAYER_EMAIL_META_KEY)) {
|
|
return \false;
|
|
}
|
|
if ('paypal' !== $order->get_meta(PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY)) {
|
|
return \false;
|
|
}
|
|
return \false === strpos($order->get_payment_method_title(), '(via PayPal)');
|
|
}
|
|
/**
|
|
* Overwrite WC order email/phone.
|
|
*
|
|
* The contact module can provide a custom email and phone number.
|
|
* These two properties are intended to be treated as primary contact details.
|
|
*/
|
|
private function register_contact_handlers(): void
|
|
{
|
|
$set_order_contacts = function (WC_Order $wc_order): void {
|
|
$email = $wc_order->get_meta(PayPalGateway::CONTACT_EMAIL_META_KEY);
|
|
$phone = $wc_order->get_meta(PayPalGateway::CONTACT_PHONE_META_KEY);
|
|
if ($email && is_email($email)) {
|
|
$wc_order->set_billing_email($email);
|
|
}
|
|
if ($phone && is_string($phone)) {
|
|
$wc_order->set_billing_phone($phone);
|
|
}
|
|
};
|
|
add_action('woocommerce_paypal_payments_contacts_added', function (WC_Order $wc_order) use ($set_order_contacts): void {
|
|
$set_order_contacts($wc_order);
|
|
});
|
|
// There is a race condition in express block checkout, the contacts set in woocommerce_paypal_payments_contacts_added
|
|
// may get reverted by another ajax WC request. So we set them again after that.
|
|
add_action('woocommerce_store_api_cart_update_order_from_request', function (WC_Order $wc_order) use ($set_order_contacts): void {
|
|
// This hook fires for all methods, but we do not do anything if the meta is not set
|
|
// so probably no need to add additional checks.
|
|
$set_order_contacts($wc_order);
|
|
});
|
|
}
|
|
/**
|
|
* Inserts custom fields into the order-detail view.
|
|
*
|
|
* @param mixed $fields The field-list provided by WooCommerce, should be an array.
|
|
* @return array|mixed The filtered field list.
|
|
*
|
|
* @psalm-suppress MissingClosureParamType
|
|
*/
|
|
private function insert_custom_fields_into_order_details($fields)
|
|
{
|
|
global $theorder;
|
|
if (!is_array($fields)) {
|
|
return $fields;
|
|
}
|
|
if (!$this->is_order_paid_by_paypal($theorder)) {
|
|
return $fields;
|
|
}
|
|
/**
|
|
* We use this filter to de-customize the order details - 'billing' and 'shipping' section.
|
|
*/
|
|
if (!apply_filters('woocommerce_paypal_payments_order_details_show_paypal_email', \true)) {
|
|
return $fields;
|
|
}
|
|
$email = $theorder->get_meta(PayPalGateway::ORDER_PAYER_EMAIL_META_KEY) ?: '';
|
|
$fields['paypal_email'] = array('label' => __('PayPal email address', 'woocommerce-paypal-payments'), 'value' => $email, 'wrapper_class' => 'form-field-wide', 'custom_attributes' => array('disabled' => 'disabled'));
|
|
return $fields;
|
|
}
|
|
/**
|
|
* Displays a custom section in the order details page with the original contact details entered
|
|
* during checkout.
|
|
*
|
|
* When the Contact module is active, those contact details are replaced with details provided
|
|
* by PayPal; this section shows the (unused) details which the user originally entered.
|
|
*
|
|
* @param WC_Order|mixed $order The order which is rendered.
|
|
* @return void
|
|
*/
|
|
private function display_original_contact_in_order_details($order): void
|
|
{
|
|
if (!$this->is_order_paid_by_paypal($order)) {
|
|
return;
|
|
}
|
|
if (!apply_filters('woocommerce_paypal_payments_order_details_show_original_contact', \true)) {
|
|
return;
|
|
}
|
|
assert($order instanceof WC_Order);
|
|
$contact_email = $order->get_meta(PayPalGateway::ORIGINAL_EMAIL_META_KEY);
|
|
$contact_phone = $order->get_meta(PayPalGateway::ORIGINAL_PHONE_META_KEY);
|
|
if (!$contact_email && !$contact_phone) {
|
|
return;
|
|
}
|
|
?>
|
|
<div class="ppcp-original-contact-data address" style="clear:both">
|
|
<h3>
|
|
<?php
|
|
esc_html_e('Other', 'woocommerce-paypal-payments');
|
|
?>
|
|
<span
|
|
class="woocommerce-help-tip alignright" tabindex="0"
|
|
data-tip="<?php
|
|
esc_attr_e('The customer entered these contact details during checkout, but provided different details in the PayPal popup. These details are kept for reference only.', 'woocommerce-paypal-payments');
|
|
?>"
|
|
></span>
|
|
</h3>
|
|
<?php
|
|
if (!empty($contact_email)) {
|
|
?>
|
|
<p>
|
|
<strong>
|
|
<?php
|
|
esc_html_e('Email address', 'woocommerce-paypal-payments');
|
|
?>:
|
|
</strong>
|
|
<a href="<?php
|
|
echo esc_url('mailto:' . $contact_email);
|
|
?>"><?php
|
|
echo esc_html($contact_email);
|
|
?></a>
|
|
</p>
|
|
<?php
|
|
}
|
|
?>
|
|
<?php
|
|
if (!empty($contact_phone)) {
|
|
?>
|
|
<p>
|
|
<strong>
|
|
<?php
|
|
esc_html_e('Phone', 'woocommerce-paypal-payments');
|
|
?>:
|
|
</strong>
|
|
<?php
|
|
echo wc_make_phone_clickable($contact_phone);
|
|
?>
|
|
</p>
|
|
<?php
|
|
}
|
|
?>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|