mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 18:16:38 +08:00
Merge branch 'trunk' into PCP-1480-order-left-on-hold-after-payment-trough-pui-if-merchant-connected-account-trough-manual-credential-input
This commit is contained in:
commit
8dc0079a41
121 changed files with 2730 additions and 1449 deletions
|
@ -62,8 +62,6 @@ class ResubscribeEndpoint {
|
|||
// Validate nonce.
|
||||
$this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$this->registrar->unregister();
|
||||
|
||||
if ( ! $this->registrar->register() ) {
|
||||
wp_send_json_error( 'Webhook subscription failed.', 500 );
|
||||
return false;
|
||||
|
|
|
@ -10,6 +10,12 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Payment_Token_CC;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
@ -40,17 +46,31 @@ class VaultPaymentTokenCreated implements RequestHandler {
|
|||
*/
|
||||
protected $authorized_payments_processor;
|
||||
|
||||
/**
|
||||
* The payment token factory.
|
||||
*
|
||||
* @var PaymentTokenFactory
|
||||
*/
|
||||
protected $payment_token_factory;
|
||||
|
||||
/**
|
||||
* VaultPaymentTokenCreated constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $prefix The prefix.
|
||||
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The authorized payment processor.
|
||||
* @param PaymentTokenFactory $payment_token_factory The payment token factory.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger, string $prefix, AuthorizedPaymentsProcessor $authorized_payments_processor ) {
|
||||
public function __construct(
|
||||
LoggerInterface $logger,
|
||||
string $prefix,
|
||||
AuthorizedPaymentsProcessor $authorized_payments_processor,
|
||||
PaymentTokenFactory $payment_token_factory
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->prefix = $prefix;
|
||||
$this->authorized_payments_processor = $authorized_payments_processor;
|
||||
$this->payment_token_factory = $payment_token_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,6 +118,38 @@ class VaultPaymentTokenCreated implements RequestHandler {
|
|||
$wc_customer_id = (int) str_replace( $this->prefix, '', $customer_id );
|
||||
$this->authorized_payments_processor->capture_authorized_payments_for_customer( $wc_customer_id );
|
||||
|
||||
if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
|
||||
if ( ! is_null( $request['resource']['source'] ) && isset( $request['resource']['source']['card'] ) ) {
|
||||
$token = new WC_Payment_Token_CC();
|
||||
$token->set_token( $request['resource']['id'] );
|
||||
$token->set_user_id( $wc_customer_id );
|
||||
$token->set_gateway_id( CreditCardGateway::ID );
|
||||
|
||||
$token->set_last4( $request['resource']['source']['card']['last_digits'] ?? '' );
|
||||
$expiry = explode( '-', $request['resource']['source']['card']['expiry'] ?? '' );
|
||||
$token->set_expiry_year( $expiry[0] ?? '' );
|
||||
$token->set_expiry_month( $expiry[1] ?? '' );
|
||||
$token->set_card_type( $request['resource']['source']['card']['brand'] ?? '' );
|
||||
$token->save();
|
||||
WC_Payment_Tokens::set_users_default( $wc_customer_id, $token->get_id() );
|
||||
} elseif ( isset( $request['resource']['source']['paypal'] ) ) {
|
||||
$payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
|
||||
assert( $payment_token_paypal instanceof PaymentTokenPayPal );
|
||||
|
||||
$payment_token_paypal->set_token( $request['resource']['id'] );
|
||||
$payment_token_paypal->set_user_id( $wc_customer_id );
|
||||
$payment_token_paypal->set_gateway_id( PayPalGateway::ID );
|
||||
|
||||
$email = $request['resource']['source']['paypal']['payer']['email_address'] ?? '';
|
||||
if ( $email && is_email( $email ) ) {
|
||||
$payment_token_paypal->set_email( $email );
|
||||
}
|
||||
|
||||
$payment_token_paypal->save();
|
||||
WC_Payment_Tokens::set_users_default( $wc_customer_id, $payment_token_paypal->get_id() );
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Webhook VAULT.CREDIT-CARD.CREATED
|
||||
* Handles the Webhook VAULT.PAYMENT-TOKEN.DELETED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
@ -10,37 +10,29 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Webhooks\Handler;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Payment_Tokens;
|
||||
use WP_REST_Request;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Class VaultCreditCardCreated
|
||||
* Class VaultPaymentTokenDeleted
|
||||
*/
|
||||
class VaultCreditCardCreated implements RequestHandler {
|
||||
class VaultPaymentTokenDeleted implements RequestHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* The prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* VaultCreditCardCreated constructor.
|
||||
* VaultPaymentTokenDeleted constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $prefix The prefix.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger, string $prefix ) {
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +42,7 @@ class VaultCreditCardCreated implements RequestHandler {
|
|||
*/
|
||||
public function event_types(): array {
|
||||
return array(
|
||||
'VAULT.CREDIT-CARD.CREATED',
|
||||
'VAULT.PAYMENT-TOKEN.DELETED',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,15 +65,31 @@ class VaultCreditCardCreated implements RequestHandler {
|
|||
* @return WP_REST_Response
|
||||
*/
|
||||
public function handle_request( WP_REST_Request $request ): WP_REST_Response {
|
||||
// TODO currently this webhook is not triggered from PayPal, implement it once is available.
|
||||
$response = array( 'success' => false );
|
||||
|
||||
$message = 'VAULT.CREDIT-CARD.CREATED received.';
|
||||
$this->logger->log( 'info', $message );
|
||||
$response = array(
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
);
|
||||
if ( ! is_null( $request['resource'] ) && isset( $request['resource']['id'] ) ) {
|
||||
$token_id = wc_clean( wp_unslash( $request['resource']['id'] ?? '' ) );
|
||||
|
||||
/**
|
||||
* Needed for database query.
|
||||
*
|
||||
* @psalm-suppress InvalidGlobal
|
||||
*/
|
||||
global $wpdb;
|
||||
|
||||
$token = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}woocommerce_payment_tokens WHERE token=%s",
|
||||
$token_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( isset( $token->token_id ) ) {
|
||||
WC_Payment_Tokens::delete( $token->token_id );
|
||||
}
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
return new WP_REST_Response( $response );
|
||||
}
|
||||
}
|
|
@ -77,11 +77,11 @@ class IncomingWebhookEndpoint {
|
|||
private $simulation;
|
||||
|
||||
/**
|
||||
* The last webhook info storage.
|
||||
* The last webhook event storage.
|
||||
*
|
||||
* @var WebhookInfoStorage
|
||||
* @var WebhookEventStorage
|
||||
*/
|
||||
private $last_webhook_storage;
|
||||
private $last_webhook_event_storage;
|
||||
|
||||
/**
|
||||
* IncomingWebhookEndpoint constructor.
|
||||
|
@ -92,7 +92,7 @@ class IncomingWebhookEndpoint {
|
|||
* @param bool $verify_request Whether requests need to be verified or not.
|
||||
* @param WebhookEventFactory $webhook_event_factory The webhook event factory.
|
||||
* @param WebhookSimulation $simulation The simulation handler.
|
||||
* @param WebhookInfoStorage $last_webhook_storage The last webhook info storage.
|
||||
* @param WebhookEventStorage $last_webhook_event_storage The last webhook event storage.
|
||||
* @param RequestHandler ...$handlers The handlers, which process a request in the end.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -102,18 +102,18 @@ class IncomingWebhookEndpoint {
|
|||
bool $verify_request,
|
||||
WebhookEventFactory $webhook_event_factory,
|
||||
WebhookSimulation $simulation,
|
||||
WebhookInfoStorage $last_webhook_storage,
|
||||
WebhookEventStorage $last_webhook_event_storage,
|
||||
RequestHandler ...$handlers
|
||||
) {
|
||||
|
||||
$this->webhook_endpoint = $webhook_endpoint;
|
||||
$this->webhook = $webhook;
|
||||
$this->handlers = $handlers;
|
||||
$this->logger = $logger;
|
||||
$this->verify_request = $verify_request;
|
||||
$this->webhook_event_factory = $webhook_event_factory;
|
||||
$this->last_webhook_storage = $last_webhook_storage;
|
||||
$this->simulation = $simulation;
|
||||
$this->webhook_endpoint = $webhook_endpoint;
|
||||
$this->webhook = $webhook;
|
||||
$this->handlers = $handlers;
|
||||
$this->logger = $logger;
|
||||
$this->verify_request = $verify_request;
|
||||
$this->webhook_event_factory = $webhook_event_factory;
|
||||
$this->last_webhook_event_storage = $last_webhook_event_storage;
|
||||
$this->simulation = $simulation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,7 +186,7 @@ class IncomingWebhookEndpoint {
|
|||
public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
|
||||
$event = $this->event_from_request( $request );
|
||||
|
||||
$this->last_webhook_storage->save( $event );
|
||||
$this->last_webhook_event_storage->save( $event );
|
||||
|
||||
if ( $this->simulation->is_simulation_event( $event ) ) {
|
||||
$this->logger->info( 'Received simulated webhook.' );
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Status\Assets;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulateEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\SimulationStateEndpoint;
|
||||
|
@ -33,18 +34,28 @@ class WebhooksStatusPageAssets {
|
|||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The environment object.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
private $environment;
|
||||
|
||||
/**
|
||||
* WebhooksStatusPageAssets constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
* @param Environment $environment The environment object.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version
|
||||
string $version,
|
||||
Environment $environment
|
||||
) {
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
$this->version = $version;
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,6 +114,7 @@ class WebhooksStatusPageAssets {
|
|||
'tooLongDelayMessage' => __( 'Looks like the webhook cannot be received. Check that your website is accessible from the internet.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
),
|
||||
'environment' => $this->environment->current_environment(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ namespace WooCommerce\PayPalCommerce\Webhooks;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent;
|
||||
|
||||
/**
|
||||
* Class WebhookInfoStorage
|
||||
* Class WebhookEventStorage
|
||||
*/
|
||||
class WebhookInfoStorage {
|
||||
class WebhookEventStorage {
|
||||
|
||||
/**
|
||||
* The WP option key.
|
|
@ -153,7 +153,6 @@ class WebhookModule implements ModuleInterface {
|
|||
add_action(
|
||||
'init',
|
||||
function () use ( $registrar ) {
|
||||
$registrar->unregister();
|
||||
$registrar->register();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -45,11 +45,11 @@ class WebhookRegistrar {
|
|||
private $rest_endpoint;
|
||||
|
||||
/**
|
||||
* The last webhook info storage.
|
||||
* The last webhook event storage.
|
||||
*
|
||||
* @var WebhookInfoStorage
|
||||
* @var WebhookEventStorage
|
||||
*/
|
||||
private $last_webhook_storage;
|
||||
private $last_webhook_event_storage;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
|
@ -64,22 +64,22 @@ class WebhookRegistrar {
|
|||
* @param WebhookFactory $webhook_factory The Webhook factory.
|
||||
* @param WebhookEndpoint $endpoint The Webhook endpoint.
|
||||
* @param IncomingWebhookEndpoint $rest_endpoint The WordPress Rest API endpoint.
|
||||
* @param WebhookInfoStorage $last_webhook_storage The last webhook info storage.
|
||||
* @param WebhookEventStorage $last_webhook_event_storage The last webhook event storage.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
WebhookFactory $webhook_factory,
|
||||
WebhookEndpoint $endpoint,
|
||||
IncomingWebhookEndpoint $rest_endpoint,
|
||||
WebhookInfoStorage $last_webhook_storage,
|
||||
WebhookEventStorage $last_webhook_event_storage,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->webhook_factory = $webhook_factory;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->rest_endpoint = $rest_endpoint;
|
||||
$this->last_webhook_storage = $last_webhook_storage;
|
||||
$this->logger = $logger;
|
||||
$this->webhook_factory = $webhook_factory;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->rest_endpoint = $rest_endpoint;
|
||||
$this->last_webhook_event_storage = $last_webhook_event_storage;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,6 +88,8 @@ class WebhookRegistrar {
|
|||
* @return bool
|
||||
*/
|
||||
public function register(): bool {
|
||||
$this->unregister();
|
||||
|
||||
$webhook = $this->webhook_factory->for_url_and_events(
|
||||
$this->rest_endpoint->url(),
|
||||
$this->rest_endpoint->handled_event_types()
|
||||
|
@ -102,7 +104,7 @@ class WebhookRegistrar {
|
|||
self::KEY,
|
||||
$created->to_array()
|
||||
);
|
||||
$this->last_webhook_storage->clear();
|
||||
$this->last_webhook_event_storage->clear();
|
||||
$this->logger->info( 'Webhooks subscribed.' );
|
||||
return true;
|
||||
} catch ( RuntimeException $error ) {
|
||||
|
@ -113,27 +115,23 @@ class WebhookRegistrar {
|
|||
|
||||
/**
|
||||
* Unregister webhooks with PayPal.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unregister(): bool {
|
||||
$data = (array) get_option( self::KEY, array() );
|
||||
if ( ! $data ) {
|
||||
return false;
|
||||
}
|
||||
public function unregister(): void {
|
||||
try {
|
||||
$webhook = $this->webhook_factory->from_array( $data );
|
||||
$success = $this->endpoint->delete( $webhook );
|
||||
$webhooks = $this->endpoint->list();
|
||||
foreach ( $webhooks as $webhook ) {
|
||||
try {
|
||||
$this->endpoint->delete( $webhook );
|
||||
} catch ( RuntimeException $deletion_error ) {
|
||||
$this->logger->error( "Failed to delete webhook {$webhook->id()}: {$deletion_error->getMessage()}" );
|
||||
}
|
||||
}
|
||||
} catch ( RuntimeException $error ) {
|
||||
$this->logger->error( 'Failed to delete webhooks: ' . $error->getMessage() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $success ) {
|
||||
delete_option( self::KEY );
|
||||
$this->last_webhook_storage->clear();
|
||||
$this->logger->info( 'Webhooks deleted.' );
|
||||
}
|
||||
return $success;
|
||||
delete_option( self::KEY );
|
||||
$this->last_webhook_event_storage->clear();
|
||||
$this->logger->info( 'Webhooks deleted.' );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue