mirror of
https://gh.wpcy.net/https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2026-04-29 04:59:01 +08:00
487 lines
17 KiB
PHP
487 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* The save payment methods module.
|
|
*
|
|
* @package WooCommerce\PayPalCommerce\Applepay
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
use WC_Order;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\ReferenceTransactionStatus;
|
|
use WooCommerce\PayPalCommerce\Assets\AssetGetter;
|
|
use WooCommerce\PayPalCommerce\Button\Helper\Context;
|
|
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
|
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest;
|
|
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
|
use WooCommerce\PayPalCommerce\WcPaymentTokens\WooCommercePaymentTokens;
|
|
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
|
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\Settings\Data\SettingsModel;
|
|
use WooCommerce\PayPalCommerce\Settings\Data\SettingsProvider;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
|
use WooCommerce\PayPalCommerce\WcSubscriptions\Endpoint\SubscriptionChangePaymentMethod;
|
|
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
|
|
|
/**
|
|
* Class SavePaymentMethodsModule
|
|
*/
|
|
class SavePaymentMethodsModule implements ServiceModule, ExecutableModule {
|
|
use ModuleClassNameIdTrait;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function services(): array {
|
|
return require __DIR__ . '/../services.php';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function run( ContainerInterface $c ): bool {
|
|
if ( ! $c->get( 'save-payment-methods.eligible' ) ) {
|
|
return true;
|
|
}
|
|
|
|
add_action(
|
|
'woocommerce_paypal_payments_gateway_migrate_on_update',
|
|
function () use ( $c ) {
|
|
$settings_model = $c->get( 'settings.data.settings' );
|
|
assert( $settings_model instanceof SettingsModel );
|
|
|
|
$reference_transaction_status = $c->get( 'api.reference-transaction-status' );
|
|
assert( $reference_transaction_status instanceof ReferenceTransactionStatus );
|
|
|
|
if ( ! $reference_transaction_status->reference_transaction_enabled() ) {
|
|
$settings_model->set_save_paypal_and_venmo( false );
|
|
$settings_model->save();
|
|
}
|
|
}
|
|
);
|
|
|
|
add_action(
|
|
'after_setup_theme',
|
|
function () use ( $c ) {
|
|
$settings_provider = $c->get( 'settings.settings-provider' );
|
|
assert( $settings_provider instanceof SettingsProvider );
|
|
if ( ! $settings_provider->save_paypal_and_venmo() && ! $settings_provider->save_card_details() ) {
|
|
return true;
|
|
}
|
|
|
|
add_filter(
|
|
'woocommerce_paypal_payments_localized_script_data',
|
|
function ( array $localized_script_data ) use ( $c ) {
|
|
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
|
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
|
if ( ! is_user_logged_in() && ! $subscriptions_helper->cart_contains_subscription() ) {
|
|
return $localized_script_data;
|
|
}
|
|
|
|
$api = $c->get( 'api.user-id-token' );
|
|
assert( $api instanceof UserIdToken );
|
|
|
|
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
|
assert( $logger instanceof LoggerInterface );
|
|
|
|
return $this->add_id_token_to_script_data( $api, $logger, $localized_script_data );
|
|
}
|
|
);
|
|
|
|
// Adds attributes needed to save payment method.
|
|
add_filter(
|
|
'ppcp_create_order_request_body_data',
|
|
function ( array $data, string $payment_method, array $request_data ) use ( $c ): array {
|
|
$settings_provider = $c->get( 'settings.settings-provider' );
|
|
assert( $settings_provider instanceof SettingsProvider );
|
|
|
|
$new_attributes = array(
|
|
'vault' => array(
|
|
'store_in_vault' => 'ON_SUCCESS',
|
|
),
|
|
);
|
|
|
|
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
|
if ( ! $target_customer_id ) {
|
|
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
|
}
|
|
if ( $target_customer_id ) {
|
|
$new_attributes['customer'] = array(
|
|
'id' => $target_customer_id,
|
|
);
|
|
}
|
|
|
|
$funding_source = (string) ( $request_data['funding_source'] ?? '' );
|
|
|
|
if ( $payment_method === CreditCardGateway::ID ) {
|
|
if ( ! $settings_provider->save_card_details() ) {
|
|
return $data;
|
|
}
|
|
|
|
$save_payment_method = $request_data['save_payment_method'] ?? false;
|
|
if ( ! $save_payment_method ) {
|
|
return $data;
|
|
}
|
|
} elseif ( $payment_method === PayPalGateway::ID ) {
|
|
if ( ! $settings_provider->save_paypal_and_venmo() ) {
|
|
return $data;
|
|
}
|
|
|
|
if ( ! in_array( $funding_source, array( 'paypal', 'venmo' ), true ) ) {
|
|
return $data;
|
|
}
|
|
|
|
$new_attributes['vault']['usage_type'] = 'MERCHANT';
|
|
$new_attributes['vault']['permit_multiple_payment_tokens'] = apply_filters( 'woocommerce_paypal_payments_permit_multiple_payment_tokens', false );
|
|
} else {
|
|
return $data;
|
|
}
|
|
|
|
$payment_source = (array) ( $data['payment_source'] ?? array() );
|
|
$key = array_key_first( $payment_source );
|
|
if ( ! is_string( $key ) || empty( $key ) ) {
|
|
$key = $payment_method;
|
|
if ( $payment_method === PayPalGateway::ID && $funding_source ) {
|
|
$key = $funding_source;
|
|
}
|
|
$payment_source[ $key ] = array();
|
|
}
|
|
$payment_source[ $key ] = (array) $payment_source[ $key ];
|
|
$attributes = (array) ( $payment_source[ $key ]['attributes'] ?? array() );
|
|
$payment_source[ $key ]['attributes'] = array_merge( $attributes, $new_attributes );
|
|
|
|
$data['payment_source'] = $payment_source;
|
|
|
|
return $data;
|
|
},
|
|
20,
|
|
3
|
|
);
|
|
|
|
add_action(
|
|
'woocommerce_paypal_payments_after_order_processor',
|
|
function ( WC_Order $wc_order, Order $order ) use ( $c ) {
|
|
$payment_source = $order->payment_source();
|
|
assert( $payment_source instanceof PaymentSource );
|
|
|
|
$payment_vault_attributes = $payment_source->properties()->attributes->vault ?? null;
|
|
if ( $payment_vault_attributes ) {
|
|
$customer_id = $payment_vault_attributes->customer->id ?? '';
|
|
$token_id = $payment_vault_attributes->id ?? '';
|
|
if ( ! $customer_id || ! $token_id ) {
|
|
return;
|
|
}
|
|
|
|
update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id );
|
|
|
|
$wc_payment_tokens = $c->get( 'wc-payment-tokens.wc-payment-tokens' );
|
|
assert( $wc_payment_tokens instanceof WooCommercePaymentTokens );
|
|
|
|
try {
|
|
if ( $wc_order->get_payment_method() === CreditCardGateway::ID ) {
|
|
$wc_payment_tokens->create_payment_token_card(
|
|
$wc_order->get_customer_id(),
|
|
(object) array(
|
|
'id' => $token_id,
|
|
'payment_source' => (object) array(
|
|
'card' => $payment_source->properties(),
|
|
),
|
|
)
|
|
);
|
|
}
|
|
|
|
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
|
|
switch ( $payment_source->name() ) {
|
|
case 'venmo':
|
|
$wc_payment_tokens->create_payment_token_venmo(
|
|
$wc_order->get_customer_id(),
|
|
$token_id,
|
|
$payment_source->properties()->email_address ?? ''
|
|
);
|
|
break;
|
|
case 'apple_pay':
|
|
$wc_payment_tokens->create_payment_token_applepay(
|
|
$wc_order->get_customer_id(),
|
|
$token_id
|
|
);
|
|
break;
|
|
case 'paypal':
|
|
default:
|
|
$wc_payment_tokens->create_payment_token_paypal(
|
|
$wc_order->get_customer_id(),
|
|
$token_id,
|
|
$payment_source->properties()->email_address ?? ''
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
} catch ( \Exception $exception ) {
|
|
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
|
assert( $logger instanceof LoggerInterface );
|
|
|
|
$logger->warning(
|
|
'Failed to save payment token for order: ' . $exception->getMessage(),
|
|
array(
|
|
'order_id' => $wc_order->get_id(),
|
|
'customer_id' => $customer_id,
|
|
'token_id' => $token_id,
|
|
'exception' => $exception,
|
|
)
|
|
);
|
|
}
|
|
}
|
|
},
|
|
10,
|
|
2
|
|
);
|
|
|
|
add_filter( 'woocommerce_paypal_payments_disable_add_payment_method', '__return_false' );
|
|
add_filter( 'woocommerce_paypal_payments_should_render_card_custom_fields', '__return_false' );
|
|
|
|
add_action(
|
|
'wp_enqueue_scripts',
|
|
function () use ( $c ) {
|
|
$context = $c->get( 'button.helper.context' );
|
|
assert( $context instanceof Context );
|
|
if ( ! is_user_logged_in() || ! ( $context->is_add_payment_method_page() || $context->is_subscription_change_payment_method_page() ) ) {
|
|
return;
|
|
}
|
|
|
|
$asset_getter = $c->get( 'save-payment-methods.asset_getter' );
|
|
assert( $asset_getter instanceof AssetGetter );
|
|
|
|
wp_enqueue_script(
|
|
'ppcp-add-payment-method',
|
|
$asset_getter->get_asset_url( 'add-payment-method.js' ),
|
|
array( 'jquery' ),
|
|
$c->get( 'ppcp.asset-version' ),
|
|
true
|
|
);
|
|
|
|
$api = $c->get( 'api.user-id-token' );
|
|
assert( $api instanceof UserIdToken );
|
|
|
|
try {
|
|
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
|
if ( ! $target_customer_id ) {
|
|
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
|
}
|
|
|
|
$id_token = $api->id_token( $target_customer_id );
|
|
|
|
$settings_provider = $c->get( 'settings.settings-provider' );
|
|
assert( $settings_provider instanceof SettingsProvider );
|
|
|
|
$verification_method = apply_filters(
|
|
'woocommerce_paypal_payments_three_d_secure_contingency',
|
|
$settings_provider->three_d_secure_enum()
|
|
);
|
|
|
|
// phpcs:ignore WordPress.Security.NonceVerification
|
|
$change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? '' ) );
|
|
$is_subscription_change_payment_method_page = $context->is_subscription_change_payment_method_page();
|
|
|
|
wp_localize_script(
|
|
'ppcp-add-payment-method',
|
|
'ppcp_add_payment_method',
|
|
array(
|
|
'client_id' => $c->get( 'button.client_id' ),
|
|
'merchant_id' => $c->get( 'api.merchant_id' ),
|
|
'id_token' => $id_token,
|
|
'payment_methods_page' => wc_get_account_endpoint_url( 'payment-methods' ),
|
|
'view_subscriptions_page' => wc_get_account_endpoint_url( 'view-subscription' ),
|
|
'is_subscription_change_payment_page' => $is_subscription_change_payment_method_page,
|
|
'subscription_id_to_change_payment' => $is_subscription_change_payment_method_page ? (int) $change_payment_method : 0,
|
|
'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ),
|
|
'verification_method' => $verification_method,
|
|
'user' => array(
|
|
'is_logged' => is_user_logged_in(),
|
|
),
|
|
'ajax' => array(
|
|
'create_setup_token' => array(
|
|
'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ),
|
|
'nonce' => wp_create_nonce( CreateSetupToken::nonce() ),
|
|
),
|
|
'create_payment_token' => array(
|
|
'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ),
|
|
'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ),
|
|
),
|
|
'subscription_change_payment_method' => array(
|
|
'endpoint' => \WC_AJAX::get_endpoint( SubscriptionChangePaymentMethod::ENDPOINT ),
|
|
'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ),
|
|
),
|
|
),
|
|
'labels' => array(
|
|
'error' => array(
|
|
'generic' => __(
|
|
'Something went wrong. Please try again or choose another payment source.',
|
|
'woocommerce-paypal-payments'
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
} catch ( RuntimeException $exception ) {
|
|
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
|
assert( $logger instanceof LoggerInterface );
|
|
|
|
$error = $exception->getMessage();
|
|
if ( $exception instanceof PayPalApiException ) {
|
|
$error = $exception->get_details( $error );
|
|
}
|
|
|
|
$logger->error( $error );
|
|
}
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Displays the PayPal button on the Add Payment Method page.
|
|
*/
|
|
add_action(
|
|
'woocommerce_add_payment_method_form_bottom',
|
|
function () {
|
|
if ( ! is_user_logged_in() || ! is_add_payment_method_page() ) {
|
|
return;
|
|
}
|
|
|
|
echo '<div id="ppc-button-' . esc_attr( PayPalGateway::ID ) . '-save-payment-method"></div>';
|
|
}
|
|
);
|
|
|
|
add_action(
|
|
'wc_ajax_' . CreateSetupToken::ENDPOINT,
|
|
static function () use ( $c ) {
|
|
$endpoint = $c->get( 'save-payment-methods.endpoint.create-setup-token' );
|
|
assert( $endpoint instanceof CreateSetupToken );
|
|
|
|
$endpoint->handle_request();
|
|
}
|
|
);
|
|
|
|
add_action(
|
|
'wc_ajax_' . CreatePaymentToken::ENDPOINT,
|
|
static function () use ( $c ) {
|
|
$endpoint = $c->get( 'save-payment-methods.endpoint.create-payment-token' );
|
|
assert( $endpoint instanceof CreatePaymentToken );
|
|
|
|
$endpoint->handle_request();
|
|
}
|
|
);
|
|
|
|
add_action(
|
|
'wc_ajax_' . CreatePaymentTokenForGuest::ENDPOINT,
|
|
static function () use ( $c ) {
|
|
$endpoint = $c->get( 'save-payment-methods.endpoint.create-payment-token-for-guest' );
|
|
assert( $endpoint instanceof CreatePaymentTokenForGuest );
|
|
|
|
$endpoint->handle_request();
|
|
}
|
|
);
|
|
|
|
add_action(
|
|
'woocommerce_paypal_payments_before_delete_payment_token',
|
|
function ( string $token_id ) use ( $c ) {
|
|
try {
|
|
$endpoint = $c->get( 'api.endpoint.payment-tokens' );
|
|
assert( $endpoint instanceof PaymentTokensEndpoint );
|
|
|
|
$endpoint->delete( $token_id );
|
|
} catch ( RuntimeException $exception ) {
|
|
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
|
assert( $logger instanceof LoggerInterface );
|
|
|
|
$error = $exception->getMessage();
|
|
if ( $exception instanceof PayPalApiException ) {
|
|
$error = $exception->get_details( $error );
|
|
}
|
|
|
|
$logger->error( $error );
|
|
}
|
|
}
|
|
);
|
|
|
|
add_filter(
|
|
'woocommerce_paypal_payments_credit_card_gateway_supports',
|
|
function ( array $supports ) use ( $c ): array {
|
|
if ( ! $c->get( 'save-payment-methods.eligible' ) ) {
|
|
return $supports;
|
|
}
|
|
|
|
$settings_provider = $c->get( 'settings.settings-provider' );
|
|
assert( $settings_provider instanceof SettingsProvider );
|
|
|
|
if ( $settings_provider->save_card_details() ) {
|
|
$supports[] = 'tokenization';
|
|
$supports[] = 'add_payment_method';
|
|
}
|
|
|
|
return $supports;
|
|
}
|
|
);
|
|
|
|
add_filter(
|
|
'woocommerce_paypal_payments_save_payment_methods_eligible',
|
|
function () {
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Adds id token to localized script data.
|
|
*
|
|
* @param UserIdToken $api User id token api.
|
|
* @param LoggerInterface $logger The logger.
|
|
* @param array $localized_script_data The localized script data.
|
|
* @return array
|
|
*/
|
|
private function add_id_token_to_script_data(
|
|
UserIdToken $api,
|
|
LoggerInterface $logger,
|
|
array $localized_script_data
|
|
): array {
|
|
try {
|
|
$target_customer_id = '';
|
|
if ( is_user_logged_in() ) {
|
|
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
|
if ( ! $target_customer_id ) {
|
|
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
|
}
|
|
}
|
|
|
|
$id_token = $api->id_token( $target_customer_id );
|
|
$localized_script_data['save_payment_methods'] = array(
|
|
'id_token' => $id_token,
|
|
);
|
|
|
|
$localized_script_data['data_client_id']['set_attribute'] = false;
|
|
|
|
} catch ( RuntimeException $exception ) {
|
|
$error = $exception->getMessage();
|
|
if ( $exception instanceof PayPalApiException ) {
|
|
$error = $exception->get_details( $error );
|
|
}
|
|
|
|
$logger->error( $error );
|
|
}
|
|
|
|
return $localized_script_data;
|
|
}
|
|
}
|