mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 10:55:00 +08:00
Merge branch 'trunk' into modularity-full-migration
# Conflicts: # modules/ppcp-blocks/src/BlocksModule.php
This commit is contained in:
commit
5837d65b18
150 changed files with 8479 additions and 3005 deletions
|
@ -2,6 +2,12 @@
|
|||
if (!defined('PAYPAL_INTEGRATION_DATE')) {
|
||||
define('PAYPAL_INTEGRATION_DATE', '2023-06-02');
|
||||
}
|
||||
if (!defined('PAYPAL_URL')) {
|
||||
define( 'PAYPAL_URL', 'https://www.paypal.com' );
|
||||
}
|
||||
if (!defined('PAYPAL_SANDBOX_URL')) {
|
||||
define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' );
|
||||
}
|
||||
if (!defined('EP_PAGES')) {
|
||||
define('EP_PAGES', 4096);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\PPCP;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\RefundFeesUpdater;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
/**
|
||||
|
@ -47,6 +48,19 @@ function ppcp_get_paypal_order( $paypal_id_or_wc_order ): Order {
|
|||
return $order_endpoint->order( $paypal_id_or_wc_order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PayPal order for the given WC order.
|
||||
*
|
||||
* @param WC_Order $wc_order The WC order.
|
||||
* @throws Exception When the operation fails.
|
||||
*/
|
||||
function ppcp_create_paypal_order_for_wc_order( WC_Order $wc_order ): Order {
|
||||
$order_processor = PPCP::container()->get( 'wcgateway.order-processor' );
|
||||
assert( $order_processor instanceof OrderProcessor );
|
||||
|
||||
return $order_processor->create_order( $wc_order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the PayPal order.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,37 @@
|
|||
*** Changelog ***
|
||||
|
||||
= 2.4.3 - xxxx-xx-xx =
|
||||
* Fix - PayPal Subscription initiated without a WooCommerce order #1907
|
||||
* Fix - Block Checkout reloads when submitting order with empty fields #1904
|
||||
* Fix - "Send checkout billing and shipping data to Apple Pay" displayed when Apple Pay is disabled #1883
|
||||
* Fix - "Order does not contain intent" error for ACDC renewals when triggering 3D Secure #1888
|
||||
* Enhancement - Add button to reload feature eligibility status from Connection tab #1902
|
||||
* Enhancement - Apple Pay validation message improvements #1901
|
||||
* Enhancement - Improve support for Classic Cart & Classic Checkout blocks #1894
|
||||
* Enhancement - Ensure uniform button appearance for PayPal, Google Pay, and Apple Pay buttons #1900
|
||||
* Enhancement - remove string translations for package tracking carriers from repository #1885
|
||||
* Enhancement - Incorrect margins when PayPal buttons are rendered as separate gateways. #1908
|
||||
* Feature preview - Save payment methods (Vault v3) integration #1779
|
||||
|
||||
= 2.4.2 - 2023-12-04 =
|
||||
* Fix - Action callback arguments count in ShipStation tracking integration #1841
|
||||
* Fix - Google Pay scripts loading on unrelated admin pages #1834
|
||||
* Fix - Do not ignore disabled APMs list in blocks #1865
|
||||
* Fix - Display Package Tracking metabox below Order actions when HPOS is active #1850
|
||||
* Fix - ApplePay use checkout form data to update shipping and billing #1832
|
||||
* Fix - Fix Apple Pay CSS #1872
|
||||
* Enhancement - Allow redirect to PayPal with "Place order" button if smart buttons failed to load #1840 #1870
|
||||
* Enhancement - Extend list of supported countries for Package Tracking v2 integration #1848
|
||||
* Enhancement - Improve Block Theme support for Pay Later messaging #1855
|
||||
* Enhancement - Render block buttons separately and add block style settings #1858
|
||||
* Enhancement - Enable Block Cart and Block Express Checkout button locations by default #1852
|
||||
* Enhancement - Improve single product page button placement with Block themes #1847
|
||||
* Enhancement - Remove the Home location from default enabled Pay Later messaging locations #1856
|
||||
* Enhancement - Chrome browser detected as eligible for Apple Pay on settings page #1828
|
||||
* Enhancement - Hide Apple Pay & Google Pay for subscription type products #1835
|
||||
* Enhancement - Add Standard Card Button gateway styling settings & preview #1827
|
||||
* Feature preview - Upgrade to new Hosted Card Fields for Advanced Card Processing #1843
|
||||
|
||||
= 2.4.1 - 2023-11-14 =
|
||||
* Fix - Error "PayPal order ID not found in meta" prevents automations from triggering when buying subscription via third-party payment gateway #1822
|
||||
* Fix - Card button subscription support declaration #1796
|
||||
|
|
11
modules.php
11
modules.php
|
@ -20,13 +20,14 @@ return function ( string $root_dir ): iterable {
|
|||
( require "$modules_dir/ppcp-onboarding/module.php" )(),
|
||||
( require "$modules_dir/ppcp-session/module.php" )(),
|
||||
( require "$modules_dir/ppcp-status-report/module.php" )(),
|
||||
( require "$modules_dir/ppcp-subscription/module.php" )(),
|
||||
( require "$modules_dir/ppcp-wc-subscriptions/module.php" )(),
|
||||
( require "$modules_dir/ppcp-wc-gateway/module.php" )(),
|
||||
( require "$modules_dir/ppcp-webhooks/module.php" )(),
|
||||
( require "$modules_dir/ppcp-vaulting/module.php" )(),
|
||||
( require "$modules_dir/ppcp-order-tracking/module.php" )(),
|
||||
( require "$modules_dir/ppcp-uninstall/module.php" )(),
|
||||
( require "$modules_dir/ppcp-blocks/module.php" )(),
|
||||
( require "$modules_dir/ppcp-paypal-subscriptions/module.php" )(),
|
||||
);
|
||||
if ( apply_filters(
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
|
@ -60,5 +61,13 @@ return function ( string $root_dir ): iterable {
|
|||
$modules[] = ( require "$modules_dir/ppcp-card-fields/module.php" )();
|
||||
}
|
||||
|
||||
if ( apply_filters(
|
||||
//phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
'woocommerce.feature-flags.woocommerce_paypal_payments.save_payment_methods_enabled',
|
||||
getenv( 'PCP_SAVE_PAYMENT_METHODS' ) === '1'
|
||||
) ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-save-payment-methods/module.php" )();
|
||||
}
|
||||
|
||||
return $modules;
|
||||
};
|
||||
|
|
|
@ -9,12 +9,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CardAuthenticationResultFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerPayableBreakdown;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\RefundFactory;
|
||||
|
@ -50,7 +54,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayeeFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentsFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentSourceFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenActionLinksFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlatformFeeFactory;
|
||||
|
@ -80,6 +83,15 @@ return array(
|
|||
'api.paypal-host' => function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_API_URL;
|
||||
},
|
||||
// It seems this 'api.paypal-website-url' key is always overridden in ppcp-onboarding/services.php.
|
||||
'api.paypal-website-url' => function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_URL;
|
||||
},
|
||||
'api.factory.paypal-checkout-url' => function( ContainerInterface $container ) : callable {
|
||||
return function ( string $id ) use ( $container ): string {
|
||||
return $container->get( 'api.paypal-website-url' ) . '/checkoutnow?token=' . $id;
|
||||
};
|
||||
},
|
||||
'api.partner_merchant_id' => static function () : string {
|
||||
return '';
|
||||
},
|
||||
|
@ -138,6 +150,13 @@ return array(
|
|||
$container->get( 'api.repository.customer' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.payment-tokens' => static function( ContainerInterface $container ) : PaymentTokensEndpoint {
|
||||
return new PaymentTokensEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.webhook' => static function ( ContainerInterface $container ) : WebhookEndpoint {
|
||||
|
||||
return new WebhookEndpoint(
|
||||
|
@ -204,7 +223,7 @@ return array(
|
|||
|
||||
$intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE';
|
||||
$application_context_repository = $container->get( 'api.repository.application-context' );
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||
return new OrderEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
|
@ -250,6 +269,13 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.payment-method-tokens' => static function( ContainerInterface $container ): PaymentMethodTokensEndpoint {
|
||||
return new PaymentMethodTokensEndpoint(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
@ -372,21 +398,16 @@ return array(
|
|||
'api.factory.address' => static function ( ContainerInterface $container ): AddressFactory {
|
||||
return new AddressFactory();
|
||||
},
|
||||
'api.factory.payment-source' => static function ( ContainerInterface $container ): PaymentSourceFactory {
|
||||
return new PaymentSourceFactory();
|
||||
},
|
||||
'api.factory.order' => static function ( ContainerInterface $container ): OrderFactory {
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
$application_context_repository = $container->get( 'api.repository.application-context' );
|
||||
$application_context_factory = $container->get( 'api.factory.application-context' );
|
||||
$payment_source_factory = $container->get( 'api.factory.payment-source' );
|
||||
return new OrderFactory(
|
||||
$purchase_unit_factory,
|
||||
$payer_factory,
|
||||
$application_context_repository,
|
||||
$application_context_factory,
|
||||
$payment_source_factory
|
||||
$application_context_factory
|
||||
);
|
||||
},
|
||||
'api.factory.payments' => static function ( ContainerInterface $container ): PaymentsFactory {
|
||||
|
@ -438,6 +459,9 @@ return array(
|
|||
$container->get( 'api.factory.payment-preferences' )
|
||||
);
|
||||
},
|
||||
'api.factory.card-authentication-result-factory' => static function( ContainerInterface $container ): CardAuthenticationResultFactory {
|
||||
return new CardAuthenticationResultFactory();
|
||||
},
|
||||
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
|
||||
return new DccApplies(
|
||||
$container->get( 'api.dcc-supported-country-currency-matrix' ),
|
||||
|
@ -1116,4 +1140,11 @@ return array(
|
|||
return new PurchaseUnitSanitizer( $behavior, $line_name );
|
||||
}
|
||||
),
|
||||
'api.user-id-token' => static function( ContainerInterface $container ): UserIdToken {
|
||||
return new UserIdToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
105
modules/ppcp-api-client/src/Authentication/UserIdToken.php
Normal file
105
modules/ppcp-api-client/src/Authentication/UserIdToken.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* Generates user ID token for payer.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Authentication
|
||||
*/
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Authentication;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class UserIdToken
|
||||
*/
|
||||
class UserIdToken {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* UserIdToken constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `id_token` which uniquely identifies the payer.
|
||||
*
|
||||
* @param string $target_customer_id Vaulted customer id.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws PayPalApiException If the request fails.
|
||||
* @throws RuntimeException If something unexpected happens.
|
||||
*/
|
||||
public function id_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token';
|
||||
if ( $target_customer_id ) {
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'target_customer_id' => $target_customer_id,
|
||||
),
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
return $json->id_token;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
|
@ -27,7 +28,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Helper\ErrorResponse;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
|
||||
use WP_Error;
|
||||
|
||||
|
@ -178,8 +179,11 @@ class OrderEndpoint {
|
|||
* @param string $shipping_preference One of ApplicationContext::SHIPPING_PREFERENCE_ values.
|
||||
* @param Payer|null $payer The payer off the order.
|
||||
* @param PaymentToken|null $payment_token The payment token.
|
||||
* @param string $paypal_request_id The paypal request id.
|
||||
* @param string $paypal_request_id The PayPal request id.
|
||||
* @param string $user_action The user action.
|
||||
* @param string $payment_method WC payment method.
|
||||
* @param array $request_data Request data.
|
||||
* @param PaymentSource|null $payment_source The payment source.
|
||||
*
|
||||
* @return Order
|
||||
* @throws RuntimeException If the request fails.
|
||||
|
@ -190,7 +194,10 @@ class OrderEndpoint {
|
|||
Payer $payer = null,
|
||||
PaymentToken $payment_token = null,
|
||||
string $paypal_request_id = '',
|
||||
string $user_action = ApplicationContext::USER_ACTION_CONTINUE
|
||||
string $user_action = ApplicationContext::USER_ACTION_CONTINUE,
|
||||
string $payment_method = '',
|
||||
array $request_data = array(),
|
||||
PaymentSource $payment_source = null
|
||||
): Order {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$data = array(
|
||||
|
@ -217,11 +224,16 @@ class OrderEndpoint {
|
|||
if ( $payment_token ) {
|
||||
$data['payment_source']['token'] = $payment_token->to_array();
|
||||
}
|
||||
if ( $payment_source ) {
|
||||
$data['payment_source'] = array(
|
||||
$payment_source->name() => $payment_source->properties(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter can be used to modify the order creation request body data.
|
||||
*/
|
||||
$data = apply_filters( 'ppcp_create_order_request_body_data', $data );
|
||||
$data = apply_filters( 'ppcp_create_order_request_body_data', $data, $payment_method, $request_data );
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
|
@ -260,26 +272,25 @@ class OrderEndpoint {
|
|||
);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code ) {
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
$error = new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
$this->logger->log(
|
||||
'warning',
|
||||
|
||||
$this->logger->warning(
|
||||
sprintf(
|
||||
'Failed to create order. PayPal API response: %1$s',
|
||||
$error->getMessage()
|
||||
),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$order = $this->order_factory->from_paypal_response( $json );
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_paypal_order_created', $order );
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/**
|
||||
* The Payment Method Tokens endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PaymentMethodTokensEndpoint
|
||||
*/
|
||||
class PaymentMethodTokensEndpoint {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* PaymentMethodTokensEndpoint constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( string $host, Bearer $bearer, LoggerInterface $logger ) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a setup token.
|
||||
*
|
||||
* @param PaymentSource $payment_source The payment source.
|
||||
*
|
||||
* @return stdClass
|
||||
*
|
||||
* @throws RuntimeException When something when wrong with the request.
|
||||
* @throws PayPalApiException When something when wrong setting up the token.
|
||||
*/
|
||||
public function setup_tokens( PaymentSource $payment_source ): stdClass {
|
||||
$data = array(
|
||||
'payment_source' => array(
|
||||
$payment_source->name() => $payment_source->properties(),
|
||||
),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v3/vault/setup-tokens';
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create setup token.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a payment token for the given payment source.
|
||||
*
|
||||
* @param PaymentSource $payment_source The payment source.
|
||||
*
|
||||
* @return stdClass
|
||||
*
|
||||
* @throws RuntimeException When something when wrong with the request.
|
||||
* @throws PayPalApiException When something when wrong setting up the token.
|
||||
*/
|
||||
public function payment_tokens( PaymentSource $payment_source ): stdClass {
|
||||
$data = array(
|
||||
'payment_source' => array(
|
||||
$payment_source->name() => $payment_source->properties(),
|
||||
),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v3/vault/payment-tokens';
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create setup token.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* Payment tokens version 3 endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class PaymentTokensEndpoint
|
||||
*/
|
||||
class PaymentTokensEndpoint {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* PaymentTokensEndpoint constructor.
|
||||
*
|
||||
* @param string $host The bearer.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a payment token with the given id.
|
||||
*
|
||||
* @param string $id Payment token id.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException When something went wrong with the request.
|
||||
* @throws PayPalApiException When something went wrong deleting the payment token.
|
||||
*/
|
||||
public function delete( string $id ): void {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v3/vault/payment-tokens/' . $id;
|
||||
$args = array(
|
||||
'method' => 'DELETE',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
|||
*/
|
||||
class CardAuthenticationResult {
|
||||
|
||||
|
||||
const LIABILITY_SHIFT_POSSIBLE = 'POSSIBLE';
|
||||
const LIABILITY_SHIFT_NO = 'NO';
|
||||
const LIABILITY_SHIFT_UNKNOWN = 'UNKNOWN';
|
||||
|
|
|
@ -107,14 +107,6 @@ class Order {
|
|||
|
||||
$this->id = $id;
|
||||
$this->application_context = $application_context;
|
||||
$this->purchase_units = array_values(
|
||||
array_filter(
|
||||
$purchase_units,
|
||||
static function ( $unit ): bool {
|
||||
return is_a( $unit, PurchaseUnit::class );
|
||||
}
|
||||
)
|
||||
);
|
||||
$this->payer = $payer;
|
||||
$this->order_status = $order_status;
|
||||
$this->intent = ( 'CAPTURE' === $intent ) ? 'CAPTURE' : 'AUTHORIZE';
|
||||
|
@ -236,9 +228,6 @@ class Order {
|
|||
if ( $this->application_context() ) {
|
||||
$order['application_context'] = $this->application_context()->to_array();
|
||||
}
|
||||
if ( $this->payment_source() ) {
|
||||
$order['payment_source'] = $this->payment_source()->to_array();
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class OrderStatus {
|
|||
const VOIDED = 'VOIDED';
|
||||
const COMPLETED = 'COMPLETED';
|
||||
const PENDING_APPROVAL = 'PENDING_APPROVAL';
|
||||
const PAYER_ACTION_REQUIRED = 'PAYER_ACTION_REQUIRED';
|
||||
const VALID_STATUS = array(
|
||||
self::INTERNAL,
|
||||
self::CREATED,
|
||||
|
@ -30,6 +31,7 @@ class OrderStatus {
|
|||
self::VOIDED,
|
||||
self::COMPLETED,
|
||||
self::PENDING_APPROVAL,
|
||||
self::PAYER_ACTION_REQUIRED,
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,74 +9,53 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class PaymentSource
|
||||
*/
|
||||
class PaymentSource {
|
||||
|
||||
/**
|
||||
* The card.
|
||||
* Payment source name.
|
||||
*
|
||||
* @var PaymentSourceCard|null
|
||||
* @var string
|
||||
*/
|
||||
private $card;
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* The wallet.
|
||||
* Payment source properties.
|
||||
*
|
||||
* @var PaymentSourceWallet|null
|
||||
* @var object
|
||||
*/
|
||||
private $wallet;
|
||||
private $properties;
|
||||
|
||||
/**
|
||||
* PaymentSource constructor.
|
||||
*
|
||||
* @param PaymentSourceCard|null $card The card.
|
||||
* @param PaymentSourceWallet|null $wallet The wallet.
|
||||
* @param string $name Payment source name.
|
||||
* @param object $properties Payment source properties.
|
||||
*/
|
||||
public function __construct(
|
||||
PaymentSourceCard $card = null,
|
||||
PaymentSourceWallet $wallet = null
|
||||
) {
|
||||
|
||||
$this->card = $card;
|
||||
$this->wallet = $wallet;
|
||||
public function __construct( string $name, object $properties ) {
|
||||
$this->name = $name;
|
||||
$this->properties = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the card.
|
||||
* Payment source name.
|
||||
*
|
||||
* @return PaymentSourceCard|null
|
||||
* @return string
|
||||
*/
|
||||
public function card() {
|
||||
|
||||
return $this->card;
|
||||
public function name(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wallet.
|
||||
* Payment source properties.
|
||||
*
|
||||
* @return PaymentSourceWallet|null
|
||||
* @return object
|
||||
*/
|
||||
public function wallet() {
|
||||
|
||||
return $this->wallet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of the object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
|
||||
$data = array();
|
||||
if ( $this->card() ) {
|
||||
$data['card'] = $this->card()->to_array();
|
||||
}
|
||||
if ( $this->wallet() ) {
|
||||
$data['wallet'] = $this->wallet()->to_array();
|
||||
}
|
||||
return $data;
|
||||
public function properties(): object {
|
||||
return $this->properties;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The PaymentSourceCard object.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class PaymentSourceCard
|
||||
*/
|
||||
class PaymentSourceCard {
|
||||
|
||||
/**
|
||||
* The last digits of the card.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $last_digits;
|
||||
|
||||
/**
|
||||
* The brand.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $brand;
|
||||
|
||||
/**
|
||||
* The type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* The authentication result.
|
||||
*
|
||||
* @var CardAuthenticationResult|null
|
||||
*/
|
||||
private $authentication_result;
|
||||
|
||||
/**
|
||||
* PaymentSourceCard constructor.
|
||||
*
|
||||
* @param string $last_digits The last digits of the card.
|
||||
* @param string $brand The brand of the card.
|
||||
* @param string $type The type of the card.
|
||||
* @param CardAuthenticationResult|null $authentication_result The authentication result.
|
||||
*/
|
||||
public function __construct(
|
||||
string $last_digits,
|
||||
string $brand,
|
||||
string $type,
|
||||
CardAuthenticationResult $authentication_result = null
|
||||
) {
|
||||
|
||||
$this->last_digits = $last_digits;
|
||||
$this->brand = $brand;
|
||||
$this->type = $type;
|
||||
$this->authentication_result = $authentication_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last digits.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function last_digits(): string {
|
||||
|
||||
return $this->last_digits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the brand.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function brand(): string {
|
||||
|
||||
return $this->brand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string {
|
||||
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication result.
|
||||
*
|
||||
* @return CardAuthenticationResult|null
|
||||
*/
|
||||
public function authentication_result() {
|
||||
|
||||
return $this->authentication_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
|
||||
$data = array(
|
||||
'last_digits' => $this->last_digits(),
|
||||
'brand' => $this->brand(),
|
||||
'type' => $this->type(),
|
||||
);
|
||||
if ( $this->authentication_result() ) {
|
||||
$data['authentication_result'] = $this->authentication_result()->to_array();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The PaymentSourcewallet.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class PaymentSourceWallet
|
||||
*/
|
||||
class PaymentSourceWallet {
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array();
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\AmountBreakdown;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
* The card authentication result factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
|
||||
|
||||
/**
|
||||
* Class CardAuthenticationResultFactory
|
||||
*/
|
||||
class CardAuthenticationResultFactory {
|
||||
|
||||
/**
|
||||
* Returns a card authentication result from the given response object.
|
||||
*
|
||||
* @param stdClass $authentication_result The authentication result object.
|
||||
* @return CardAuthenticationResult
|
||||
*/
|
||||
public function from_paypal_response( stdClass $authentication_result ): CardAuthenticationResult {
|
||||
return new CardAuthenticationResult(
|
||||
$authentication_result->liability_shift ?? '',
|
||||
$authentication_result->three_d_secure->enrollment_status ?? '',
|
||||
$authentication_result->three_d_secure->authentication_status ?? ''
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
|
||||
|
@ -48,13 +49,6 @@ class OrderFactory {
|
|||
*/
|
||||
private $application_context_factory;
|
||||
|
||||
/**
|
||||
* The PaymentSource factory.
|
||||
*
|
||||
* @var PaymentSourceFactory
|
||||
*/
|
||||
private $payment_source_factory;
|
||||
|
||||
/**
|
||||
* OrderFactory constructor.
|
||||
*
|
||||
|
@ -62,21 +56,18 @@ class OrderFactory {
|
|||
* @param PayerFactory $payer_factory The Payer factory.
|
||||
* @param ApplicationContextRepository $application_context_repository The Application Context repository.
|
||||
* @param ApplicationContextFactory $application_context_factory The Application Context factory.
|
||||
* @param PaymentSourceFactory $payment_source_factory The Payment Source factory.
|
||||
*/
|
||||
public function __construct(
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
PayerFactory $payer_factory,
|
||||
ApplicationContextRepository $application_context_repository,
|
||||
ApplicationContextFactory $application_context_factory,
|
||||
PaymentSourceFactory $payment_source_factory
|
||||
ApplicationContextFactory $application_context_factory
|
||||
) {
|
||||
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->application_context_repository = $application_context_repository;
|
||||
$this->application_context_factory = $application_context_factory;
|
||||
$this->payment_source_factory = $payment_source_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,9 +143,23 @@ class OrderFactory {
|
|||
$application_context = ( isset( $order_data->application_context ) ) ?
|
||||
$this->application_context_factory->from_paypal_response( $order_data->application_context )
|
||||
: null;
|
||||
$payment_source = ( isset( $order_data->payment_source ) ) ?
|
||||
$this->payment_source_factory->from_paypal_response( $order_data->payment_source ) :
|
||||
null;
|
||||
|
||||
$payment_source = null;
|
||||
if ( isset( $order_data->payment_source ) ) {
|
||||
$json_encoded_payment_source = wp_json_encode( $order_data->payment_source );
|
||||
if ( $json_encoded_payment_source ) {
|
||||
$payment_source_as_array = json_decode( $json_encoded_payment_source, true );
|
||||
if ( $payment_source_as_array ) {
|
||||
$name = array_key_first( $payment_source_as_array );
|
||||
if ( $name ) {
|
||||
$payment_source = new PaymentSource(
|
||||
$name,
|
||||
$order_data->payment_source->$name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Order(
|
||||
$order_data->id,
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The PaymentSource factory.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSourceCard;
|
||||
|
||||
/**
|
||||
* Class PaymentSourceFactory
|
||||
*/
|
||||
class PaymentSourceFactory {
|
||||
|
||||
/**
|
||||
* Returns a PaymentSource for a PayPal Response.
|
||||
*
|
||||
* @param \stdClass $data The JSON object.
|
||||
*
|
||||
* @return PaymentSource
|
||||
*/
|
||||
public function from_paypal_response( \stdClass $data ): PaymentSource {
|
||||
|
||||
$card = null;
|
||||
$wallet = null;
|
||||
if ( isset( $data->card ) ) {
|
||||
$authentication_result = null;
|
||||
if ( isset( $data->card->authentication_result ) ) {
|
||||
$authentication_result = new CardAuthenticationResult(
|
||||
isset( $data->card->authentication_result->liability_shift ) ?
|
||||
(string) $data->card->authentication_result->liability_shift : '',
|
||||
isset( $data->card->authentication_result->three_d_secure->enrollment_status ) ?
|
||||
(string) $data->card->authentication_result->three_d_secure->enrollment_status : '',
|
||||
isset( $data->card->authentication_result->three_d_secure->authentication_status ) ?
|
||||
(string) $data->card->authentication_result->three_d_secure->authentication_status : ''
|
||||
);
|
||||
}
|
||||
$card = new PaymentSourceCard(
|
||||
isset( $data->card->last_digits ) ? (string) $data->card->last_digits : '',
|
||||
isset( $data->card->brand ) ? (string) $data->card->brand : '',
|
||||
isset( $data->card->type ) ? (string) $data->card->type : '',
|
||||
$authentication_result
|
||||
);
|
||||
}
|
||||
return new PaymentSource( $card, $wallet );
|
||||
}
|
||||
}
|
|
@ -49,7 +49,9 @@ return array(
|
|||
|
||||
// Domain validation.
|
||||
$domain_validation_text = __( 'Status: Domain validation failed ❌', 'woocommerce-paypal-payments' );
|
||||
if ( $container->get( 'applepay.is_validated' ) ) {
|
||||
if ( ! $container->get( 'applepay.has_validated' ) ) {
|
||||
$domain_validation_text = __( 'The domain has not yet been validated. Use the Apple Pay button to validate the domain ❌', 'woocommerce-paypal-payments' );
|
||||
} elseif ( $container->get( 'applepay.is_validated' ) ) {
|
||||
$domain_validation_text = __( 'Status: Domain successfully validated ✔️', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
|
@ -157,6 +159,7 @@ return array(
|
|||
->action_visible( 'applepay_button_color' )
|
||||
->action_visible( 'applepay_button_type' )
|
||||
->action_visible( 'applepay_button_language' )
|
||||
->action_visible( 'applepay_checkout_data_mode' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
|
|
|
@ -1,63 +1,44 @@
|
|||
#applepay-container, .ppcp-button-applepay {
|
||||
.ppcp-button-applepay {
|
||||
// Should replicate apm-button.scss sizes.
|
||||
--apple-pay-button-height: 45px;
|
||||
--apple-pay-button-min-height: 40px;
|
||||
--apple-pay-button-min-height: 35px;
|
||||
--apple-pay-button-width: 100%;
|
||||
--apple-pay-button-max-width: 750px;
|
||||
--apple-pay-button-border-radius: 4px;
|
||||
--apple-pay-button-overflow: hidden;
|
||||
margin:7px 0;
|
||||
|
||||
.ppcp-width-min & {
|
||||
--apple-pay-button-height: 35px;
|
||||
}
|
||||
.ppcp-width-300 & {
|
||||
--apple-pay-button-height: 45px;
|
||||
}
|
||||
.ppcp-width-500 & {
|
||||
--apple-pay-button-height: 55px;
|
||||
}
|
||||
|
||||
&.ppcp-button-pill {
|
||||
--apple-pay-button-border-radius: 50px;
|
||||
}
|
||||
|
||||
&.ppcp-button-minicart {
|
||||
--apple-pay-button-display: block;
|
||||
--apple-pay-button-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-checkout {
|
||||
#applepay-container, .ppcp-button-applepay {
|
||||
margin-top: 0.5em;
|
||||
--apple-pay-button-border-radius: 4px;
|
||||
--apple-pay-button-height: 45px;
|
||||
&.ppcp-button-pill {
|
||||
--apple-pay-button-border-radius: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-has-applepay-block {
|
||||
|
||||
.wp-block-woocommerce-checkout {
|
||||
#applepay-container, .ppcp-button-applepay {
|
||||
.wp-block-woocommerce-checkout, .wp-block-woocommerce-cart {
|
||||
.ppcp-button-applepay {
|
||||
--apple-pay-button-margin: 0;
|
||||
--apple-pay-button-height: 40px;
|
||||
&.ppcp-button-pill {
|
||||
--apple-pay-button-border-radius: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart {
|
||||
#applepay-container, .ppcp-button-applepay {
|
||||
--apple-pay-button-margin: 0;
|
||||
--apple-pay-button-height: 40px;
|
||||
}
|
||||
/* Workaround for blocks grid */
|
||||
.wc-block-components-express-payment__event-buttons {
|
||||
--apple-pay-button-display: block;
|
||||
li[id*="express-payment-method-ppcp-"] {
|
||||
--apple-pay-button-padding-bottom: 0;
|
||||
}
|
||||
apple-pay-button {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
--apple-pay-button-width-default: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wp-admin {
|
||||
.ppcp-button-applepay {
|
||||
pointer-events: none;
|
||||
}
|
||||
&.ppcp-non-ios-device {
|
||||
.ppcp-button-applepay {
|
||||
apple-pay-button {
|
||||
|
|
|
@ -5,10 +5,13 @@ import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/Butto
|
|||
import FormValidator from "../../../ppcp-button/resources/js/modules/Helper/FormValidator";
|
||||
import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler';
|
||||
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
|
||||
import {apmButtonsInit} from "../../../ppcp-button/resources/js/modules/Helper/ApmButtons";
|
||||
|
||||
class ApplepayButton {
|
||||
|
||||
constructor(context, externalHandler, buttonConfig, ppcpConfig) {
|
||||
apmButtonsInit(ppcpConfig);
|
||||
|
||||
this.isInitialized = false;
|
||||
|
||||
this.context = context;
|
||||
|
@ -60,7 +63,7 @@ class ApplepayButton {
|
|||
this.initEventHandlers();
|
||||
this.isInitialized = true;
|
||||
this.applePayConfig = config;
|
||||
const isEligible = this.applePayConfig.isEligible;
|
||||
const isEligible = (this.applePayConfig.isEligible && window.ApplePaySession) || this.buttonConfig.is_admin;
|
||||
|
||||
if (isEligible) {
|
||||
this.fetchTransactionInfo().then(() => {
|
||||
|
@ -84,6 +87,10 @@ class ApplepayButton {
|
|||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
jQuery('#' + this.buttonConfig.button.wrapper).hide();
|
||||
jQuery('#' + this.buttonConfig.button.mini_cart_wrapper).hide();
|
||||
jQuery('#express-payment-method-ppcp-applepay').hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +114,7 @@ class ApplepayButton {
|
|||
let config = {
|
||||
wrapper: this.buttonConfig.button.wrapper,
|
||||
ppcpStyle: this.ppcpConfig.button.style,
|
||||
//buttonStyle: this.buttonConfig.button.style,
|
||||
buttonStyle: this.buttonConfig.button.style,
|
||||
ppcpButtonWrapper: this.ppcpConfig.button.wrapper
|
||||
}
|
||||
|
||||
|
@ -119,7 +126,7 @@ class ApplepayButton {
|
|||
}
|
||||
|
||||
if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) {
|
||||
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway';
|
||||
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway-paypal';
|
||||
}
|
||||
|
||||
return config;
|
||||
|
@ -167,14 +174,8 @@ class ApplepayButton {
|
|||
addButton() {
|
||||
this.log('addButton', this.context);
|
||||
|
||||
const wrapper =
|
||||
(this.context === 'mini-cart')
|
||||
? this.buttonConfig.button.mini_cart_wrapper
|
||||
: this.buttonConfig.button.wrapper;
|
||||
const shape =
|
||||
(this.context === 'mini-cart')
|
||||
? this.ppcpConfig.button.mini_cart_style.shape
|
||||
: this.ppcpConfig.button.style.shape;
|
||||
const { wrapper, ppcpStyle } = this.contextConfig();
|
||||
|
||||
const appleContainer = document.getElementById(wrapper);
|
||||
const type = this.buttonConfig.button.type;
|
||||
const language = this.buttonConfig.button.lang;
|
||||
|
@ -185,8 +186,13 @@ class ApplepayButton {
|
|||
appleContainer.innerHTML = `<apple-pay-button id="${id}" buttonstyle="${color}" type="${type}" locale="${language}">`;
|
||||
}
|
||||
|
||||
jQuery('#' + wrapper).addClass('ppcp-button-' + shape);
|
||||
jQuery(wrapper).append(appleContainer);
|
||||
const $wrapper = jQuery('#' + wrapper);
|
||||
$wrapper.addClass('ppcp-button-' + ppcpStyle.shape);
|
||||
|
||||
if (ppcpStyle.height) {
|
||||
$wrapper.css('--apple-pay-button-height', `${ppcpStyle.height}px`)
|
||||
$wrapper.css('height', `${ppcpStyle.height}px`)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
|
|
@ -65,7 +65,7 @@ import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/Wi
|
|||
buttonConfig.button.wrapper = selector.replace('#', '');
|
||||
applyConfigOptions(buttonConfig);
|
||||
|
||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-applepay"></div>`;
|
||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-apm ppcp-button-applepay"></div>`;
|
||||
|
||||
if (!jQuery(selector).length) {
|
||||
jQuery(ppcpConfig.button.wrapper).after(wrapperElement);
|
||||
|
|
|
@ -23,12 +23,6 @@ const ApplePayComponent = () => {
|
|||
const manager = new ApplepayManager(buttonConfig, ppcpConfig);
|
||||
manager.init();
|
||||
};
|
||||
useEffect(() => {
|
||||
const bodyClass = 'ppcp-has-applepay-block';
|
||||
if (!document.body.classList.contains(bodyClass)) {
|
||||
document.body.classList.add(bodyClass);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Load ApplePay SDK
|
||||
|
@ -50,14 +44,13 @@ const ApplePayComponent = () => {
|
|||
}, [paypalLoaded, applePayLoaded]);
|
||||
|
||||
return (
|
||||
<div id={buttonConfig.button.wrapper.replace('#', '')}></div>
|
||||
<div id={buttonConfig.button.wrapper.replace('#', '')} className="ppcp-button-apm ppcp-button-applepay"></div>
|
||||
);
|
||||
}
|
||||
|
||||
const features = ['products'];
|
||||
let registerMethod = registerExpressPaymentMethod;
|
||||
|
||||
registerMethod({
|
||||
registerExpressPaymentMethod({
|
||||
name: buttonData.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: buttonData.title}}/>,
|
||||
content: <ApplePayComponent isEditing={false}/>,
|
||||
|
|
|
@ -62,6 +62,11 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'applepay.has_validated' => static function ( ContainerInterface $container ): bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return $settings->has( 'applepay_validated' );
|
||||
},
|
||||
|
||||
'applepay.is_validated' => static function ( ContainerInterface $container ): bool {
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
return $settings->has( 'applepay_validated' ) ? $settings->get( 'applepay_validated' ) === true : false;
|
||||
|
|
|
@ -968,7 +968,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
add_action(
|
||||
$render_placeholder,
|
||||
function () {
|
||||
echo '<span id="applepay-container-minicart" class="ppcp-button-applepay ppcp-button-minicart"></span>';
|
||||
echo '<span id="applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -981,7 +981,7 @@ class ApplePayButton implements ButtonInterface {
|
|||
*/
|
||||
protected function applepay_button(): void {
|
||||
?>
|
||||
<div id="applepay-container">
|
||||
<div id="applepay-container" class="ppcp-button-apm ppcp-button-applepay">
|
||||
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -149,6 +149,7 @@ class DataToAppleButtonScripts {
|
|||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
||||
'is_admin' => false,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
|
@ -204,6 +205,7 @@ class DataToAppleButtonScripts {
|
|||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
||||
'is_admin' => false,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
|
@ -252,6 +254,7 @@ class DataToAppleButtonScripts {
|
|||
return array(
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false,
|
||||
'is_admin' => true,
|
||||
'preferences' => array(
|
||||
'checkout_data_mode' => $checkout_data_mode,
|
||||
),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* ApmApplies helper.
|
||||
* Checks if ApplePay is available for a given country and currency.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApplePay\Helper
|
||||
*/
|
||||
|
@ -15,7 +16,7 @@ namespace WooCommerce\PayPalCommerce\Applepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for DCC.
|
||||
* The matrix which countries and currency combinations can be used for ApplePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -38,7 +39,7 @@ class ApmApplies {
|
|||
/**
|
||||
* ApmApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for DCC.
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for ApplePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
|
|
15
modules/ppcp-blocks/resources/css/gateway.scss
Normal file
15
modules/ppcp-blocks/resources/css/gateway.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
@use "../../../ppcp-button/resources/css/mixins/apm-button" as apm-button;
|
||||
|
||||
li[id^="express-payment-method-ppcp-"] {
|
||||
line-height: 0;
|
||||
|
||||
// Set min-width to 0 as the buttons need to fit in a tight grid.
|
||||
.paypal-buttons {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-button-apm {
|
||||
@include apm-button.button;
|
||||
}
|
||||
|
|
@ -76,8 +76,8 @@ export const paypalShippingToWc = (shipping) => {
|
|||
* @returns {Object}
|
||||
*/
|
||||
export const paypalPayerToWc = (payer) => {
|
||||
const firstName = payer.name.given_name;
|
||||
const lastName = payer.name.surname;
|
||||
const firstName = payer?.name?.given_name ?? '';
|
||||
const lastName = payer?.name?.surname ?? '';
|
||||
const address = payer.address ? paypalAddressToWc(payer.address) : {};
|
||||
return {
|
||||
...address,
|
||||
|
@ -100,11 +100,13 @@ export const paypalOrderToWcShippingAddress = (order) => {
|
|||
const res = paypalShippingToWc(shipping);
|
||||
|
||||
// use the name from billing if the same, to avoid possible mistakes when splitting full_name
|
||||
if (order.payer) {
|
||||
const billingAddress = paypalPayerToWc(order.payer);
|
||||
if (`${res.first_name} ${res.last_name}` === `${billingAddress.first_name} ${billingAddress.last_name}`) {
|
||||
res.first_name = billingAddress.first_name;
|
||||
res.last_name = billingAddress.last_name;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -116,11 +118,14 @@ export const paypalOrderToWcShippingAddress = (order) => {
|
|||
*/
|
||||
export const paypalOrderToWcAddresses = (order) => {
|
||||
const shippingAddress = paypalOrderToWcShippingAddress(order);
|
||||
let billingAddress = paypalPayerToWc(order.payer);
|
||||
let billingAddress = shippingAddress;
|
||||
if (order.payer) {
|
||||
billingAddress = paypalPayerToWc(order.payer);
|
||||
// no billing address, such as if billing address retrieval is not allowed in the merchant account
|
||||
if (!billingAddress.address_line_1) {
|
||||
billingAddress = {...shippingAddress, ...paypalPayerToWc(order.payer)};
|
||||
}
|
||||
}
|
||||
|
||||
return {billingAddress, shippingAddress};
|
||||
}
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import {useEffect, useState} from '@wordpress/element';
|
||||
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {mergeWcAddress, paypalAddressToWc, paypalOrderToWcAddresses} from "./Helper/Address";
|
||||
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
|
||||
import {
|
||||
loadPaypalScriptPromise
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
|
||||
import {
|
||||
normalizeStyleForFundingSource
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/Style'
|
||||
import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher";
|
||||
|
||||
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
|
||||
|
||||
window.ppcpFundingSource = config.fundingSource;
|
||||
|
||||
let registeredContext = false;
|
||||
|
||||
const PayPalComponent = ({
|
||||
onClick,
|
||||
onClose,
|
||||
|
@ -18,17 +25,22 @@ const PayPalComponent = ({
|
|||
activePaymentMethod,
|
||||
shippingData,
|
||||
isEditing,
|
||||
fundingSource,
|
||||
}) => {
|
||||
const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration;
|
||||
const {responseTypes} = emitResponse;
|
||||
|
||||
const [paypalOrder, setPaypalOrder] = useState(null);
|
||||
const [gotoContinuationOnError, setGotoContinuationOnError] = useState(false);
|
||||
|
||||
const methodId = fundingSource ? `${config.id}-${fundingSource}` : config.id;
|
||||
|
||||
useEffect(() => {
|
||||
// fill the form if in continuation (for product or mini-cart buttons)
|
||||
if (!config.scriptData.continuation || !config.scriptData.continuation.order || window.ppcpContinuationFilled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const paypalAddresses = paypalOrderToWcAddresses(config.scriptData.continuation.order);
|
||||
const wcAddresses = wp.data.select('wc/store/cart').getCustomerData();
|
||||
const addresses = mergeWcAddress(wcAddresses, paypalAddresses);
|
||||
|
@ -36,28 +48,14 @@ const PayPalComponent = ({
|
|||
if (shippingData.needsShipping) {
|
||||
wp.data.dispatch('wc/store/cart').setShippingAddress(addresses.shippingAddress);
|
||||
}
|
||||
} catch (err) {
|
||||
// sometimes the PayPal address is missing, skip in this case.
|
||||
console.log(err);
|
||||
}
|
||||
// this useEffect should run only once, but adding this in case of some kind of full re-rendering
|
||||
window.ppcpContinuationFilled = true;
|
||||
}, [])
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
useEffect(() => {
|
||||
if (!loaded) {
|
||||
loadPaypalScript(config.scriptData, () => {
|
||||
setLoaded(true);
|
||||
|
||||
buttonModuleWatcher.registerContextBootstrap(config.scriptData.context, {
|
||||
createOrder: () => {
|
||||
return createOrder();
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
return handleApprove(data, actions);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [loaded]);
|
||||
|
||||
const createOrder = async () => {
|
||||
try {
|
||||
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
|
||||
|
@ -155,6 +153,7 @@ const PayPalComponent = ({
|
|||
if (config.finalReviewEnabled) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError(true);
|
||||
onSubmit();
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -173,7 +172,7 @@ const PayPalComponent = ({
|
|||
if (config.scriptData.continuation) {
|
||||
return true;
|
||||
}
|
||||
if (wp.data.select('wc/store/validation').hasValidationErrors()) {
|
||||
if (gotoContinuationOnError && wp.data.select('wc/store/validation').hasValidationErrors()) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
return { type: responseTypes.ERROR };
|
||||
}
|
||||
|
@ -181,7 +180,7 @@ const PayPalComponent = ({
|
|||
return true;
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [onCheckoutValidation] );
|
||||
}, [onCheckoutValidation, gotoContinuationOnError] );
|
||||
|
||||
const handleClick = (data, actions) => {
|
||||
if (isEditing) {
|
||||
|
@ -233,7 +232,7 @@ const PayPalComponent = ({
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (activePaymentMethod !== config.id) {
|
||||
if (activePaymentMethod !== methodId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -269,7 +268,7 @@ const PayPalComponent = ({
|
|||
}, [onPaymentSetup, paypalOrder, activePaymentMethod]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activePaymentMethod !== config.id) {
|
||||
if (activePaymentMethod !== methodId) {
|
||||
return;
|
||||
}
|
||||
const unsubscribe = onCheckoutFail(({ processingResponse }) => {
|
||||
|
@ -296,15 +295,26 @@ const PayPalComponent = ({
|
|||
)
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
return null;
|
||||
if (!registeredContext) {
|
||||
buttonModuleWatcher.registerContextBootstrap(config.scriptData.context, {
|
||||
createOrder: () => {
|
||||
return createOrder();
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
return handleApprove(data, actions);
|
||||
},
|
||||
});
|
||||
registeredContext = true;
|
||||
}
|
||||
|
||||
const style = normalizeStyleForFundingSource(config.scriptData.button.style, fundingSource);
|
||||
|
||||
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
|
||||
|
||||
return (
|
||||
<PayPalButton
|
||||
style={config.scriptData.button.style}
|
||||
fundingSource={fundingSource}
|
||||
style={style}
|
||||
onClick={handleClick}
|
||||
onCancel={onClose}
|
||||
onError={onClose}
|
||||
|
@ -316,20 +326,61 @@ const PayPalComponent = ({
|
|||
}
|
||||
|
||||
const features = ['products'];
|
||||
let registerMethod = registerExpressPaymentMethod;
|
||||
if (config.scriptData.continuation) {
|
||||
features.push('ppcp_continuation');
|
||||
registerMethod = registerPaymentMethod;
|
||||
}
|
||||
|
||||
registerMethod({
|
||||
if ((config.addPlaceOrderMethod || config.usePlaceOrder) && !config.scriptData.continuation) {
|
||||
let descriptionElement = <div dangerouslySetInnerHTML={{__html: config.description}}></div>;
|
||||
if (config.placeOrderButtonDescription) {
|
||||
descriptionElement = <div>
|
||||
<p dangerouslySetInnerHTML={{__html: config.description}}></p>
|
||||
<p style={{textAlign: 'center'}} className={'ppcp-place-order-description'} dangerouslySetInnerHTML={{__html: config.placeOrderButtonDescription}}></p>
|
||||
</div>;
|
||||
}
|
||||
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <PayPalComponent isEditing={false}/>,
|
||||
edit: <PayPalComponent isEditing={true}/>,
|
||||
content: descriptionElement,
|
||||
edit: descriptionElement,
|
||||
placeOrderButtonLabel: config.placeOrderButtonText,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => config.enabled,
|
||||
supports: {
|
||||
features: features,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (config.scriptData.continuation) {
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <PayPalComponent isEditing={false}/>,
|
||||
edit: <PayPalComponent isEditing={true}/>,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => true,
|
||||
supports: {
|
||||
features: [...features, 'ppcp_continuation'],
|
||||
},
|
||||
});
|
||||
} else if (!config.usePlaceOrder) {
|
||||
const paypalScriptPromise = loadPaypalScriptPromise(config.scriptData);
|
||||
|
||||
for (const fundingSource of ['paypal', ...config.enabledFundingSources]) {
|
||||
registerExpressPaymentMethod({
|
||||
name: `${config.id}-${fundingSource}`,
|
||||
paymentMethodId: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <PayPalComponent isEditing={false} fundingSource={fundingSource}/>,
|
||||
edit: <PayPalComponent isEditing={true} fundingSource={fundingSource}/>,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: async () => {
|
||||
await paypalScriptPromise;
|
||||
|
||||
return paypal.Buttons({fundingSource}).isEligible();
|
||||
},
|
||||
supports: {
|
||||
features: features,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,12 @@ return array(
|
|||
$container->get( 'wcgateway.paypal-gateway' ),
|
||||
$container->get( 'blocks.settings.final_review_enabled' ),
|
||||
$container->get( 'session.cancellation.view' ),
|
||||
$container->get( 'session.handler' )
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'blocks.add-place-order-method' ),
|
||||
$container->get( 'wcgateway.use-place-order-button' ),
|
||||
$container->get( 'wcgateway.place-order-button-text' ),
|
||||
$container->get( 'wcgateway.place-order-button-description' ),
|
||||
$container->get( 'wcgateway.all-funding-sources' )
|
||||
);
|
||||
},
|
||||
'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool {
|
||||
|
@ -54,4 +59,14 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool {
|
||||
/**
|
||||
* Whether to create a non-express method with the standard "Place order" button redirecting to PayPal.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_blocks_add_place_order_method',
|
||||
true
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -96,6 +96,27 @@ class BlocksModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
|||
}
|
||||
);
|
||||
|
||||
// Enqueue frontend scripts.
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
static function () use ( $c ) {
|
||||
if ( ! has_block( 'woocommerce/checkout' ) && ! has_block( 'woocommerce/cart' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module_url = $c->get( 'blocks.url' );
|
||||
$asset_version = $c->get( 'ppcp.asset-version' );
|
||||
|
||||
wp_register_style(
|
||||
'wc-ppcp-blocks',
|
||||
untrailingslashit( $module_url ) . '/assets/css/gateway.css',
|
||||
array(),
|
||||
$asset_version
|
||||
);
|
||||
wp_enqueue_style( 'wc-ppcp-blocks' );
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,41 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* Whether to create a non-express method with the standard "Place order" button.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $add_place_order_method;
|
||||
|
||||
/**
|
||||
* Whether to use the standard "Place order" button instead of PayPal buttons.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $use_place_order;
|
||||
|
||||
/**
|
||||
* The text for the standard "Place order" button.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $place_order_button_text;
|
||||
|
||||
/**
|
||||
* The text for additional "Place order" description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $place_order_button_description;
|
||||
|
||||
/**
|
||||
* All existing funding sources for PayPal buttons.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $all_funding_sources;
|
||||
|
||||
/**
|
||||
* Assets constructor.
|
||||
*
|
||||
|
@ -99,6 +134,11 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
* @param bool $final_review_enabled Whether the final review is enabled.
|
||||
* @param CancelView $cancellation_view The cancellation view.
|
||||
* @param SessionHandler $session_handler The Session handler.
|
||||
* @param bool $add_place_order_method Whether to create a non-express method with the standard "Place order" button.
|
||||
* @param bool $use_place_order Whether to use the standard "Place order" button instead of PayPal buttons.
|
||||
* @param string $place_order_button_text The text for the standard "Place order" button.
|
||||
* @param string $place_order_button_description The text for additional "Place order" description.
|
||||
* @param array $all_funding_sources All existing funding sources for PayPal buttons.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
|
@ -109,7 +149,12 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
PayPalGateway $gateway,
|
||||
bool $final_review_enabled,
|
||||
CancelView $cancellation_view,
|
||||
SessionHandler $session_handler
|
||||
SessionHandler $session_handler,
|
||||
bool $add_place_order_method,
|
||||
bool $use_place_order,
|
||||
string $place_order_button_text,
|
||||
string $place_order_button_description,
|
||||
array $all_funding_sources
|
||||
) {
|
||||
$this->name = PayPalGateway::ID;
|
||||
$this->module_url = $module_url;
|
||||
|
@ -121,6 +166,11 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
$this->final_review_enabled = $final_review_enabled;
|
||||
$this->cancellation_view = $cancellation_view;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->add_place_order_method = $add_place_order_method;
|
||||
$this->use_place_order = $use_place_order;
|
||||
$this->place_order_button_text = $place_order_button_text;
|
||||
$this->place_order_button_description = $place_order_button_description;
|
||||
$this->all_funding_sources = $all_funding_sources;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,6 +224,14 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
}
|
||||
}
|
||||
|
||||
$disabled_funding_sources = explode( ',', $script_data['url_params']['disable-funding'] ?? '' ) ?: array();
|
||||
$funding_sources = array_values(
|
||||
array_diff(
|
||||
array_keys( $this->all_funding_sources ),
|
||||
$disabled_funding_sources
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
'id' => $this->gateway->id,
|
||||
'title' => $this->gateway->title,
|
||||
|
@ -181,6 +239,11 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ),
|
||||
'fundingSource' => $this->session_handler->funding_source(),
|
||||
'finalReviewEnabled' => $this->final_review_enabled,
|
||||
'addPlaceOrderMethod' => $this->add_place_order_method,
|
||||
'usePlaceOrder' => $this->use_place_order,
|
||||
'placeOrderButtonText' => $this->place_order_button_text,
|
||||
'placeOrderButtonDescription' => $this->place_order_button_description,
|
||||
'enabledFundingSources' => $funding_sources,
|
||||
'ajax' => array(
|
||||
'update_shipping' => array(
|
||||
'endpoint' => WC_AJAX::get_endpoint( UpdateShippingEndpoint::ENDPOINT ),
|
||||
|
|
|
@ -9,7 +9,8 @@ module.exports = {
|
|||
target: 'web',
|
||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||
entry: {
|
||||
'checkout-block': path.resolve('./resources/js/checkout-block.js')
|
||||
'checkout-block': path.resolve('./resources/js/checkout-block.js'),
|
||||
"gateway": path.resolve('./resources/css/gateway.scss')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@use "mixins/apm-button" as apm-button;
|
||||
|
||||
#place_order.ppcp-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -15,3 +17,24 @@
|
|||
.ppc-button-wrapper #ppcp-messages:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
// Prevents spacing after button group.
|
||||
#ppc-button-ppcp-gateway {
|
||||
line-height: 0;
|
||||
|
||||
div[class^="item-"] {
|
||||
margin-top: 14px;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ppc-button-minicart {
|
||||
line-height: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ppcp-button-apm {
|
||||
@include apm-button.button;
|
||||
}
|
||||
|
|
42
modules/ppcp-button/resources/css/mixins/apm-button.scss
Normal file
42
modules/ppcp-button/resources/css/mixins/apm-button.scss
Normal file
|
@ -0,0 +1,42 @@
|
|||
|
||||
@mixin button {
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
max-width: 750px;
|
||||
line-height: 0;
|
||||
border-radius: 4px;
|
||||
|
||||
// Defaults
|
||||
height: 45px;
|
||||
margin-top: 14px;
|
||||
|
||||
&.ppcp-button-pill {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
&.ppcp-button-minicart {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ppcp-width-min & {
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.ppcp-width-300 & {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.ppcp-width-500 & {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
// No margin on block layout.
|
||||
.wp-block-woocommerce-checkout &, .wp-block-woocommerce-cart & {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wp-admin & {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import FormValidator from "./modules/Helper/FormValidator";
|
|||
import {loadPaypalScript} from "./modules/Helper/ScriptLoading";
|
||||
import buttonModuleWatcher from "./modules/ButtonModuleWatcher";
|
||||
import MessagesBootstrap from "./modules/ContextBootstrap/MessagesBootstap";
|
||||
import {apmButtonsInit} from "./modules/Helper/ApmButtons";
|
||||
|
||||
// TODO: could be a good idea to have a separate spinner for each gateway,
|
||||
// but I think we care mainly about the script loading, so one spinner should be enough.
|
||||
|
@ -145,6 +146,7 @@ const bootstrap = () => {
|
|||
};
|
||||
|
||||
const onSmartButtonsInit = () => {
|
||||
jQuery(document).trigger('ppcp-smart-buttons-init', this);
|
||||
buttonsSpinner.unblock();
|
||||
};
|
||||
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
|
||||
|
@ -217,6 +219,8 @@ const bootstrap = () => {
|
|||
messageRenderer,
|
||||
);
|
||||
messagesBootstrap.init();
|
||||
|
||||
apmButtonsInit(PayPalCommerceGateway);
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
|
@ -279,11 +283,12 @@ document.addEventListener(
|
|||
});
|
||||
|
||||
let bootstrapped = false;
|
||||
let failed = false;
|
||||
|
||||
hideOrderButtonIfPpcpGateway();
|
||||
|
||||
jQuery(document.body).on('updated_checkout payment_method_selected', () => {
|
||||
if (bootstrapped) {
|
||||
if (bootstrapped || failed) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -294,6 +299,12 @@ document.addEventListener(
|
|||
bootstrapped = true;
|
||||
|
||||
bootstrap();
|
||||
}, () => {
|
||||
failed = true;
|
||||
|
||||
setVisibleByClass(ORDER_BUTTON_SELECTOR, true, 'ppcp-hidden');
|
||||
buttonsSpinner.unblock();
|
||||
cardsSpinner.unblock();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'formdata-polyfill';
|
|||
import onApprove from '../OnApproveHandler/onApproveForPayNow.js';
|
||||
import {payerData} from "../Helper/PayerData";
|
||||
import {getCurrentPaymentMethod} from "../Helper/CheckoutMethodState";
|
||||
import validateCheckoutForm from "../Helper/CheckoutFormValidation";
|
||||
|
||||
class CheckoutActionHandler {
|
||||
|
||||
|
@ -13,7 +14,13 @@ class CheckoutActionHandler {
|
|||
|
||||
subscriptionsConfiguration() {
|
||||
return {
|
||||
createSubscription: (data, actions) => {
|
||||
createSubscription: async (data, actions) => {
|
||||
try {
|
||||
await validateCheckoutForm(this.config);
|
||||
} catch (error) {
|
||||
throw {type: 'form-validation-error'};
|
||||
}
|
||||
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
});
|
||||
|
@ -56,6 +63,8 @@ class CheckoutActionHandler {
|
|||
const paymentMethod = getCurrentPaymentMethod();
|
||||
const fundingSource = window.ppcpFundingSource;
|
||||
|
||||
const savePaymentMethod = !!document.getElementById('wc-ppcp-credit-card-gateway-new-payment-method')?.checked;
|
||||
|
||||
return fetch(this.config.ajax.create_order.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -72,7 +81,8 @@ class CheckoutActionHandler {
|
|||
funding_source: fundingSource,
|
||||
// send as urlencoded string to handle complex fields via PHP functions the same as normal form submit
|
||||
form_encoded: new URLSearchParams(formData).toString(),
|
||||
createaccount: createaccount
|
||||
createaccount: createaccount,
|
||||
save_payment_method: savePaymentMethod
|
||||
})
|
||||
}).then(function (res) {
|
||||
return res.json();
|
||||
|
|
|
@ -26,7 +26,7 @@ const storeToken = (token) => {
|
|||
sessionStorage.setItem(storageKey, JSON.stringify(token));
|
||||
}
|
||||
|
||||
const dataClientIdAttributeHandler = (scriptOptions, config, callback) => {
|
||||
const dataClientIdAttributeHandler = (scriptOptions, config, callback, errorCallback = null) => {
|
||||
fetch(config.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -51,6 +51,10 @@ const dataClientIdAttributeHandler = (scriptOptions, config, callback) => {
|
|||
if (typeof callback === 'function') {
|
||||
callback(paypal);
|
||||
}
|
||||
}).catch(err => {
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
114
modules/ppcp-button/resources/js/modules/Helper/ApmButtons.js
Normal file
114
modules/ppcp-button/resources/js/modules/Helper/ApmButtons.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
|
||||
export const apmButtonsInit = (config, selector = '.ppcp-button-apm') => {
|
||||
let selectorInContainer = selector;
|
||||
|
||||
if (window.ppcpApmButtons) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config && config.button) {
|
||||
|
||||
// If it's separate gateways, modify wrapper to account for the individual buttons as individual APMs.
|
||||
const wrapper = config.button.wrapper;
|
||||
const isSeparateGateways = jQuery(wrapper).children('div[class^="item-"]').length > 0;
|
||||
|
||||
if (isSeparateGateways) {
|
||||
selector += `, ${wrapper} div[class^="item-"]`;
|
||||
selectorInContainer += `, div[class^="item-"]`;
|
||||
}
|
||||
}
|
||||
|
||||
window.ppcpApmButtons = new ApmButtons(selector, selectorInContainer);
|
||||
}
|
||||
|
||||
export class ApmButtons {
|
||||
|
||||
constructor(selector, selectorInContainer) {
|
||||
this.selector = selector;
|
||||
this.selectorInContainer = selectorInContainer;
|
||||
this.containers = [];
|
||||
|
||||
// Reloads button containers.
|
||||
this.reloadContainers();
|
||||
|
||||
// Refresh button layout.
|
||||
jQuery(window).resize(() => {
|
||||
this.refresh();
|
||||
}).resize();
|
||||
|
||||
jQuery(document).on('ppcp-smart-buttons-init', () => {
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
// Observes for new buttons.
|
||||
(new MutationObserver(this.observeElementsCallback.bind(this)))
|
||||
.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
observeElementsCallback(mutationsList, observer) {
|
||||
const observeSelector = this.selector + ', .widget_shopping_cart, .widget_shopping_cart_content';
|
||||
|
||||
let shouldReload = false;
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
mutation.addedNodes.forEach(node => {
|
||||
if (node.matches && node.matches(observeSelector)) {
|
||||
shouldReload = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReload) {
|
||||
this.reloadContainers();
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
reloadContainers() {
|
||||
jQuery(this.selector).each((index, el) => {
|
||||
const parent = jQuery(el).parent();
|
||||
if (!this.containers.some($el => $el.is(parent))) {
|
||||
this.containers.push(parent);
|
||||
}
|
||||
});
|
||||
console.log('this.containers', this.containers);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
for (const container of this.containers) {
|
||||
const $container = jQuery(container);
|
||||
|
||||
// Check width and add classes
|
||||
const width = $container.width();
|
||||
|
||||
$container.removeClass('ppcp-width-500 ppcp-width-300 ppcp-width-min');
|
||||
|
||||
if (width >= 500) {
|
||||
$container.addClass('ppcp-width-500');
|
||||
} else if (width >= 300) {
|
||||
$container.addClass('ppcp-width-300');
|
||||
} else {
|
||||
$container.addClass('ppcp-width-min');
|
||||
}
|
||||
|
||||
// Check first apm button
|
||||
const $firstElement = $container.children(':visible').first();
|
||||
|
||||
// Assign margins to buttons
|
||||
$container.find(this.selectorInContainer).each((index, el) => {
|
||||
const $el = jQuery(el);
|
||||
|
||||
if ($el.is($firstElement)) {
|
||||
$el.css('margin-top', `0px`);
|
||||
return true;
|
||||
}
|
||||
|
||||
const height = $el.height();
|
||||
$el.css('margin-top', `${Math.round(height * 0.3)}px`);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
export const cardFieldStyles = (field) => {
|
||||
const allowedProperties = [
|
||||
'appearance',
|
||||
'color',
|
||||
'direction',
|
||||
'font',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-variant-alternates',
|
||||
'font-variant-caps',
|
||||
'font-variant-east-asian',
|
||||
'font-variant-ligatures',
|
||||
'font-variant-numeric',
|
||||
'font-weight',
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'opacity',
|
||||
'outline',
|
||||
'padding',
|
||||
'padding-bottom',
|
||||
'padding-left',
|
||||
'padding-right',
|
||||
'padding-top',
|
||||
'text-shadow',
|
||||
'transition',
|
||||
'-moz-appearance',
|
||||
'-moz-osx-font-smoothing',
|
||||
'-moz-tap-highlight-color',
|
||||
'-moz-transition',
|
||||
'-webkit-appearance',
|
||||
'-webkit-osx-font-smoothing',
|
||||
'-webkit-tap-highlight-color',
|
||||
'-webkit-transition',
|
||||
];
|
||||
|
||||
const stylesRaw = window.getComputedStyle(field);
|
||||
const styles = {};
|
||||
Object.values(stylesRaw).forEach((prop) => {
|
||||
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
|
||||
return;
|
||||
}
|
||||
styles[prop] = '' + stylesRaw[prop];
|
||||
});
|
||||
|
||||
return styles;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import Spinner from "./Spinner";
|
||||
import FormValidator from "./FormValidator";
|
||||
import ErrorHandler from "../ErrorHandler";
|
||||
|
||||
const validateCheckoutForm = function (config) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const spinner = new Spinner();
|
||||
const errorHandler = new ErrorHandler(
|
||||
config.labels.error.generic,
|
||||
document.querySelector('.woocommerce-notices-wrapper')
|
||||
);
|
||||
|
||||
const formSelector = config.context === 'checkout' ? 'form.checkout' : 'form#order_review';
|
||||
const formValidator = config.early_checkout_validation_enabled ?
|
||||
new FormValidator(
|
||||
config.ajax.validate_checkout.endpoint,
|
||||
config.ajax.validate_checkout.nonce,
|
||||
) : null;
|
||||
|
||||
if (!formValidator) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
formValidator.validate(document.querySelector(formSelector)).then((errors) => {
|
||||
if (errors.length > 0) {
|
||||
spinner.unblock();
|
||||
errorHandler.clear();
|
||||
errorHandler.messages(errors);
|
||||
|
||||
// fire WC event for other plugins
|
||||
jQuery( document.body ).trigger( 'checkout_error' , [ errorHandler.currentHtml() ] );
|
||||
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default validateCheckoutForm;
|
|
@ -7,10 +7,11 @@ import {keysToCamelCase} from "./Utils";
|
|||
// This component may be used by multiple modules. This assures that options are shared between all instances.
|
||||
let options = window.ppcpWidgetBuilder = window.ppcpWidgetBuilder || {
|
||||
isLoading: false,
|
||||
onLoadedCallbacks: []
|
||||
onLoadedCallbacks: [],
|
||||
onErrorCallbacks: [],
|
||||
};
|
||||
|
||||
export const loadPaypalScript = (config, onLoaded) => {
|
||||
export const loadPaypalScript = (config, onLoaded, onError = null) => {
|
||||
// If PayPal is already loaded call the onLoaded callback and return.
|
||||
if (typeof paypal !== 'undefined') {
|
||||
onLoaded();
|
||||
|
@ -19,6 +20,9 @@ export const loadPaypalScript = (config, onLoaded) => {
|
|||
|
||||
// Add the onLoaded callback to the onLoadedCallbacks stack.
|
||||
options.onLoadedCallbacks.push(onLoaded);
|
||||
if (onError) {
|
||||
options.onErrorCallbacks.push(onError);
|
||||
}
|
||||
|
||||
// Return if it's still loading.
|
||||
if (options.isLoading) {
|
||||
|
@ -26,6 +30,12 @@ export const loadPaypalScript = (config, onLoaded) => {
|
|||
}
|
||||
options.isLoading = true;
|
||||
|
||||
const resetState = () => {
|
||||
options.isLoading = false;
|
||||
options.onLoadedCallbacks = [];
|
||||
options.onErrorCallbacks = [];
|
||||
}
|
||||
|
||||
// Callback to be called once the PayPal script is loaded.
|
||||
const callback = (paypal) => {
|
||||
widgetBuilder.setPaypal(paypal);
|
||||
|
@ -34,8 +44,14 @@ export const loadPaypalScript = (config, onLoaded) => {
|
|||
onLoadedCallback();
|
||||
}
|
||||
|
||||
options.isLoading = false;
|
||||
options.onLoadedCallbacks = [];
|
||||
resetState();
|
||||
}
|
||||
const errorCallback = (err) => {
|
||||
for (const onErrorCallback of options.onErrorCallbacks) {
|
||||
onErrorCallback(err);
|
||||
}
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
// Build the PayPal script options.
|
||||
|
@ -44,12 +60,26 @@ export const loadPaypalScript = (config, onLoaded) => {
|
|||
|
||||
// Load PayPal script for special case with data-client-token
|
||||
if (config.data_client_id.set_attribute) {
|
||||
dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback);
|
||||
dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback, errorCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Adds data-user-id-token to script options.
|
||||
const userIdToken = config?.save_payment_methods?.id_token;
|
||||
if(userIdToken) {
|
||||
scriptOptions['data-user-id-token'] = userIdToken;
|
||||
}
|
||||
|
||||
// Load PayPal script
|
||||
loadScript(scriptOptions).then(callback);
|
||||
loadScript(scriptOptions)
|
||||
.then(callback)
|
||||
.catch(errorCallback);
|
||||
}
|
||||
|
||||
export const loadPaypalScriptPromise = (config) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
loadPaypalScript(config, resolve, reject)
|
||||
});
|
||||
}
|
||||
|
||||
export const loadPaypalJsScript = (options, buttons, container) => {
|
||||
|
@ -57,3 +87,11 @@ export const loadPaypalJsScript = (options, buttons, container) => {
|
|||
paypal.Buttons(buttons).render(container);
|
||||
});
|
||||
}
|
||||
|
||||
export const loadPaypalJsScriptPromise = (options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
loadScript(options)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
|
20
modules/ppcp-button/resources/js/modules/Helper/Style.js
Normal file
20
modules/ppcp-button/resources/js/modules/Helper/Style.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
export const normalizeStyleForFundingSource = (style, fundingSource) => {
|
||||
const commonProps = {};
|
||||
['shape', 'height'].forEach(prop => {
|
||||
if (style[prop]) {
|
||||
commonProps[prop] = style[prop];
|
||||
}
|
||||
});
|
||||
|
||||
switch (fundingSource) {
|
||||
case 'paypal':
|
||||
return style;
|
||||
case 'paylater':
|
||||
return {
|
||||
color: style.color,
|
||||
...commonProps
|
||||
};
|
||||
default:
|
||||
return commonProps;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import {show} from "../Helper/Hiding";
|
||||
import {cardFieldStyles} from "../Helper/CardFieldsHelper";
|
||||
|
||||
class CardFieldsRenderer {
|
||||
|
||||
|
@ -53,28 +54,28 @@ class CardFieldsRenderer {
|
|||
if (cardField.isEligible()) {
|
||||
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
|
||||
if (nameField) {
|
||||
let styles = this.cardFieldStyles(nameField);
|
||||
let styles = cardFieldStyles(nameField);
|
||||
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
|
||||
nameField.remove();
|
||||
}
|
||||
|
||||
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
|
||||
if (numberField) {
|
||||
let styles = this.cardFieldStyles(numberField);
|
||||
let styles = cardFieldStyles(numberField);
|
||||
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
|
||||
numberField.remove();
|
||||
}
|
||||
|
||||
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
|
||||
if (expiryField) {
|
||||
let styles = this.cardFieldStyles(expiryField);
|
||||
let styles = cardFieldStyles(expiryField);
|
||||
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
|
||||
expiryField.remove();
|
||||
}
|
||||
|
||||
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
|
||||
if (cvvField) {
|
||||
let styles = this.cardFieldStyles(cvvField);
|
||||
let styles = cardFieldStyles(cvvField);
|
||||
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
|
||||
cvvField.remove();
|
||||
}
|
||||
|
@ -91,65 +92,35 @@ class CardFieldsRenderer {
|
|||
this.spinner.block();
|
||||
this.errorHandler.clear();
|
||||
|
||||
const paymentToken = document.querySelector('input[name="wc-ppcp-credit-card-gateway-payment-token"]:checked')?.value
|
||||
if(paymentToken && paymentToken !== 'new') {
|
||||
fetch(this.defaultConfig.ajax.capture_card_payment.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: this.defaultConfig.ajax.capture_card_payment.nonce,
|
||||
payment_token: paymentToken
|
||||
})
|
||||
}).then((res) => {
|
||||
return res.json();
|
||||
}).then((data) => {
|
||||
document.querySelector('#place_order').click();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cardField.submit()
|
||||
.catch((error) => {
|
||||
this.spinner.unblock();
|
||||
console.error(error)
|
||||
this.errorHandler.message(this.defaultConfig.hosted_fields.labels.fields_not_valid);
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cardFieldStyles(field) {
|
||||
const allowedProperties = [
|
||||
'appearance',
|
||||
'color',
|
||||
'direction',
|
||||
'font',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-variant-alternates',
|
||||
'font-variant-caps',
|
||||
'font-variant-east-asian',
|
||||
'font-variant-ligatures',
|
||||
'font-variant-numeric',
|
||||
'font-weight',
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'opacity',
|
||||
'outline',
|
||||
'padding',
|
||||
'padding-bottom',
|
||||
'padding-left',
|
||||
'padding-right',
|
||||
'padding-top',
|
||||
'text-shadow',
|
||||
'transition',
|
||||
'-moz-appearance',
|
||||
'-moz-osx-font-smoothing',
|
||||
'-moz-tap-highlight-color',
|
||||
'-moz-transition',
|
||||
'-webkit-appearance',
|
||||
'-webkit-osx-font-smoothing',
|
||||
'-webkit-tap-highlight-color',
|
||||
'-webkit-transition',
|
||||
];
|
||||
|
||||
const stylesRaw = window.getComputedStyle(field);
|
||||
const styles = {};
|
||||
Object.values(stylesRaw).forEach((prop) => {
|
||||
if (!stylesRaw[prop] || !allowedProperties.includes(prop)) {
|
||||
return;
|
||||
}
|
||||
styles[prop] = '' + stylesRaw[prop];
|
||||
});
|
||||
|
||||
return styles;
|
||||
}
|
||||
disableFields() {}
|
||||
enableFields() {}
|
||||
}
|
||||
|
||||
export default CardFieldsRenderer;
|
||||
|
|
|
@ -2,6 +2,7 @@ import merge from "deepmerge";
|
|||
import {loadScript} from "@paypal/paypal-js";
|
||||
import {keysToCamelCase} from "../Helper/Utils";
|
||||
import widgetBuilder from "./WidgetBuilder";
|
||||
import {normalizeStyleForFundingSource} from "../Helper/Style";
|
||||
|
||||
class Renderer {
|
||||
constructor(creditCardRenderer, defaultSettings, onSmartButtonClick, onSmartButtonsInit) {
|
||||
|
@ -36,16 +37,7 @@ class Renderer {
|
|||
} else {
|
||||
// render each button separately
|
||||
for (const fundingSource of paypal.getFundingSources().filter(s => !(s in enabledSeparateGateways))) {
|
||||
let style = settings.button.style;
|
||||
if (fundingSource !== 'paypal') {
|
||||
style = {
|
||||
shape: style.shape,
|
||||
color: style.color,
|
||||
};
|
||||
if (fundingSource !== 'paylater') {
|
||||
delete style.color;
|
||||
}
|
||||
}
|
||||
const style = normalizeStyleForFundingSource(settings.button.style, fundingSource);
|
||||
|
||||
this.renderButtons(
|
||||
settings.button.wrapper,
|
||||
|
|
|
@ -15,8 +15,10 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\SimulateCartEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Button\Helper\CartProductsHelper;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
|
||||
|
@ -68,8 +70,41 @@ return array(
|
|||
|
||||
return $dummy_ids[ $shop_country ] ?? $container->get( 'button.client_id' );
|
||||
},
|
||||
// This service may not work correctly when called too early.
|
||||
'button.context' => static function ( ContainerInterface $container ): string {
|
||||
$obj = new class() {
|
||||
use ContextTrait;
|
||||
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
protected $session_handler;
|
||||
|
||||
/** Constructor. */
|
||||
public function __construct() {
|
||||
// phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.ThisFoundInStatic
|
||||
$this->session_handler = new SessionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a non-public function.
|
||||
*/
|
||||
public function get_context(): string {
|
||||
// phpcs:ignore PHPCompatibility.FunctionDeclarations.NewClosure.ThisFoundInStatic
|
||||
return $this->context();
|
||||
}
|
||||
};
|
||||
return $obj->get_context();
|
||||
},
|
||||
'button.smart-button' => static function ( ContainerInterface $container ): SmartButtonInterface {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( $container->get( 'wcgateway.use-place-order-button' )
|
||||
&& in_array( $container->get( 'button.context' ), array( 'checkout', 'pay-now' ), true )
|
||||
) {
|
||||
return new DisabledSmartButton();
|
||||
}
|
||||
if ( $state->current_state() !== State::STATE_ONBOARDED ) {
|
||||
return new DisabledSmartButton();
|
||||
}
|
||||
|
@ -84,7 +119,7 @@ return array(
|
|||
$request_data = $container->get( 'button.request-data' );
|
||||
$client_id = $container->get( 'button.client_id' );
|
||||
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||
$messages_apply = $container->get( 'button.helper.messages-apply' );
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
$payment_token_repository = $container->get( 'vaulting.repository.payment-token' );
|
||||
|
@ -259,8 +294,10 @@ return array(
|
|||
},
|
||||
|
||||
'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
return new ThreeDSecure( $logger );
|
||||
return new ThreeDSecure(
|
||||
$container->get( 'api.factory.card-authentication-result-factory' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'button.helper.messages-apply' => static function ( ContainerInterface $container ): MessagesApply {
|
||||
return new MessagesApply(
|
||||
|
|
|
@ -33,8 +33,8 @@ use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
|||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
|
@ -272,6 +272,8 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @return bool
|
||||
*/
|
||||
public function render_wrapper(): bool {
|
||||
$this->init_context();
|
||||
|
||||
if ( $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' ) ) {
|
||||
$this->render_button_wrapper_registrar();
|
||||
$this->render_message_wrapper_registrar();
|
||||
|
@ -303,7 +305,13 @@ class SmartButton implements SmartButtonInterface {
|
|||
add_filter(
|
||||
'woocommerce_credit_card_form_fields',
|
||||
function ( array $default_fields, $id ) use ( $subscription_helper ) : array {
|
||||
if ( is_user_logged_in() && $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ) && CreditCardGateway::ID === $id ) {
|
||||
if (
|
||||
is_user_logged_in()
|
||||
&& $this->settings->has( 'vault_enabled_dcc' )
|
||||
&& $this->settings->get( 'vault_enabled_dcc' )
|
||||
&& CreditCardGateway::ID === $id
|
||||
&& apply_filters( 'woocommerce_paypal_payments_should_render_card_custom_fields', true )
|
||||
) {
|
||||
|
||||
$default_fields['card-vault'] = sprintf(
|
||||
'<p class="form-row form-row-wide"><label for="ppcp-credit-card-vault"><input class="ppcp-credit-card-vault" type="checkbox" id="ppcp-credit-card-vault" name="vault">%s</label></p>',
|
||||
|
@ -638,7 +646,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
return $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' )
|
||||
&& $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' )
|
||||
&& $this->dcc_applies->for_country_currency()
|
||||
&& in_array( $this->context(), array( 'checkout', 'pay-now' ), true );
|
||||
&& in_array( $this->context(), array( 'checkout', 'pay-now', 'add-payment-method' ), true );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1049,30 +1057,39 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'mini_cart_wrapper' => '#ppc-button-minicart',
|
||||
'is_mini_cart_disabled' => $this->is_button_disabled( 'mini-cart' ),
|
||||
'cancel_wrapper' => '#ppcp-cancel',
|
||||
'mini_cart_style' => array(
|
||||
'mini_cart_style' => $this->normalize_style(
|
||||
array(
|
||||
'layout' => $this->style_for_context( 'layout', 'mini-cart' ),
|
||||
'color' => $this->style_for_context( 'color', 'mini-cart' ),
|
||||
'shape' => $this->style_for_context( 'shape', 'mini-cart' ),
|
||||
'label' => $this->style_for_context( 'label', 'mini-cart' ),
|
||||
'tagline' => $this->style_for_context( 'tagline', 'mini-cart' ),
|
||||
'height' => $this->settings->has( 'button_mini-cart_height' ) && $this->settings->get( 'button_mini-cart_height' ) ? $this->normalize_height( (int) $this->settings->get( 'button_mini-cart_height' ) ) : 35,
|
||||
'height' => $this->normalize_height( $this->style_for_context( 'height', 'mini-cart', 35 ), 25, 55 ),
|
||||
)
|
||||
),
|
||||
'style' => array(
|
||||
'style' => $this->normalize_style(
|
||||
array(
|
||||
'layout' => $this->style_for_context( 'layout', $this->context() ),
|
||||
'color' => $this->style_for_context( 'color', $this->context() ),
|
||||
'shape' => $this->style_for_context( 'shape', $this->context() ),
|
||||
'label' => $this->style_for_context( 'label', $this->context() ),
|
||||
'tagline' => $this->style_for_context( 'tagline', $this->context() ),
|
||||
'height' => in_array( $this->context(), array( 'cart-block', 'checkout-block' ), true )
|
||||
? $this->normalize_height( $this->style_for_context( 'height', $this->context(), 48 ), 40, 55 )
|
||||
: null,
|
||||
)
|
||||
),
|
||||
),
|
||||
'separate_buttons' => array(
|
||||
'card' => array(
|
||||
'id' => CardButtonGateway::ID,
|
||||
'wrapper' => '#ppc-button-' . CardButtonGateway::ID,
|
||||
'style' => array(
|
||||
'style' => $this->normalize_style(
|
||||
array(
|
||||
'shape' => $this->style_for_apm( 'shape', 'card' ),
|
||||
'color' => $this->style_for_apm( 'color', 'card', 'black' ),
|
||||
'layout' => $this->style_for_apm( 'poweredby_tagline', 'card', false ) === $this->normalize_style_value( true ) ? 'vertical' : 'horizontal',
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1143,13 +1160,6 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$localize['pay_now'] = $this->pay_now_script_data();
|
||||
}
|
||||
|
||||
if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) {
|
||||
$localize['button']['mini_cart_style']['tagline'] = false;
|
||||
}
|
||||
if ( $this->style_for_context( 'layout', $this->context() ) !== 'horizontal' ) {
|
||||
$localize['button']['style']['tagline'] = false;
|
||||
}
|
||||
|
||||
if ( $this->is_paypal_continuation() ) {
|
||||
$order = $this->session_handler->order();
|
||||
assert( $order !== null );
|
||||
|
@ -1160,7 +1170,8 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
}
|
||||
|
||||
$this->request_data->dequeue_nonce_fix();
|
||||
return $localize;
|
||||
|
||||
return apply_filters( 'woocommerce_paypal_payments_localized_script_data', $localize );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1269,9 +1280,12 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
}
|
||||
|
||||
if ( in_array( $context, array( 'checkout-block', 'cart-block' ), true ) ) {
|
||||
$disable_funding = array_diff(
|
||||
$disable_funding = array_merge(
|
||||
$disable_funding,
|
||||
array_diff(
|
||||
array_keys( $this->all_funding_sources ),
|
||||
array( 'venmo', 'paylater' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1410,12 +1424,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
*
|
||||
* @param string $style The name of the style property.
|
||||
* @param string $context The context.
|
||||
* @param ?mixed $default The default value.
|
||||
*
|
||||
* @return string
|
||||
* @return string|int
|
||||
*/
|
||||
private function style_for_context( string $style, string $context ): string {
|
||||
// Use the cart/checkout styles for blocks.
|
||||
$context = str_replace( '-block', '', $context );
|
||||
private function style_for_context( string $style, string $context, $default = null ) {
|
||||
if ( $context === 'checkout-block' ) {
|
||||
$context = 'checkout-block-express';
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'layout' => 'vertical',
|
||||
|
@ -1433,6 +1449,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
|
||||
return $this->get_style_value( "button_{$context}_${style}" )
|
||||
?? $this->get_style_value( "button_${style}" )
|
||||
?? ( $default ? $this->normalize_style_value( $default ) : null )
|
||||
?? $this->normalize_style_value( $defaults[ $style ] ?? '' );
|
||||
}
|
||||
|
||||
|
@ -1443,9 +1460,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* @param string $apm The APM name, such as 'card'.
|
||||
* @param ?mixed $default The default value.
|
||||
*
|
||||
* @return string
|
||||
* @return string|int
|
||||
*/
|
||||
private function style_for_apm( string $style, string $apm, $default = null ): string {
|
||||
private function style_for_apm( string $style, string $apm, $default = null ) {
|
||||
return $this->get_style_value( "${apm}_button_${style}" )
|
||||
?? ( $default ? $this->normalize_style_value( $default ) : null )
|
||||
?? $this->style_for_context( $style, 'checkout' );
|
||||
|
@ -1455,9 +1472,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* Returns the style property value or null.
|
||||
*
|
||||
* @param string $key The style property key in the settings.
|
||||
* @return string|null
|
||||
* @return string|int|null
|
||||
*/
|
||||
private function get_style_value( string $key ): ?string {
|
||||
private function get_style_value( string $key ) {
|
||||
if ( ! $this->settings->has( $key ) ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1468,27 +1485,49 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* Converts the style property value to string.
|
||||
*
|
||||
* @param mixed $value The style property value.
|
||||
* @return string
|
||||
* @return string|int
|
||||
*/
|
||||
private function normalize_style_value( $value ): string {
|
||||
private function normalize_style_value( $value ) {
|
||||
if ( is_bool( $value ) ) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
if ( is_int( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value between 25 and 55.
|
||||
* Fixes the style.
|
||||
*
|
||||
* @param int $height The input value.
|
||||
* @param array $style The style properties.
|
||||
* @return array
|
||||
*/
|
||||
private function normalize_style( array $style ): array {
|
||||
if ( array_key_exists( 'tagline', $style ) && ( ! array_key_exists( 'layout', $style ) || $style['layout'] !== 'horizontal' ) ) {
|
||||
$style['tagline'] = false;
|
||||
}
|
||||
if ( array_key_exists( 'height', $style ) && ! $style['height'] ) {
|
||||
unset( $style['height'] );
|
||||
}
|
||||
return $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a number between min and max.
|
||||
*
|
||||
* @param mixed $height The input value.
|
||||
* @param int $min The minimum value.
|
||||
* @param int $max The maximum value.
|
||||
* @return int The normalized output value.
|
||||
*/
|
||||
private function normalize_height( int $height ): int {
|
||||
if ( $height < 25 ) {
|
||||
return 25;
|
||||
private function normalize_height( $height, int $min, int $max ): int {
|
||||
$height = (int) $height;
|
||||
if ( $height < $min ) {
|
||||
return $min;
|
||||
}
|
||||
if ( $height > 55 ) {
|
||||
return 55;
|
||||
if ( $height > $max ) {
|
||||
return $max;
|
||||
}
|
||||
|
||||
return $height;
|
||||
|
|
|
@ -14,6 +14,7 @@ use Exception;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper;
|
||||
|
@ -144,10 +145,11 @@ class ApproveOrderEndpoint implements EndpointInterface {
|
|||
|
||||
$order = $this->api_endpoint->order( $data['order_id'] );
|
||||
|
||||
if ( $order->payment_source() && $order->payment_source()->card() ) {
|
||||
$payment_source = $order->payment_source();
|
||||
if ( $payment_source && $payment_source->name() === 'card' ) {
|
||||
if ( $this->settings->has( 'disable_cards' ) ) {
|
||||
$disabled_cards = (array) $this->settings->get( 'disable_cards' );
|
||||
$card = strtolower( $order->payment_source()->card()->brand() );
|
||||
$card = strtolower( $payment_source->properties()->brand ?? '' );
|
||||
if ( 'master_card' === $card ) {
|
||||
$card = 'mastercard';
|
||||
}
|
||||
|
|
|
@ -25,12 +25,11 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
|
||||
use WooCommerce\PayPalCommerce\Button\Exception\ValidationException;
|
||||
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\EarlyOrderHandler;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\CardBillingMode;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
@ -305,7 +304,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
}
|
||||
|
||||
try {
|
||||
$order = $this->create_paypal_order( $wc_order );
|
||||
$order = $this->create_paypal_order( $wc_order, $payment_method, $data );
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Order creation failed: ' . $exception->getMessage() );
|
||||
throw $exception;
|
||||
|
@ -416,6 +415,8 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* Creates the order in the PayPal, uses data from WC order if provided.
|
||||
*
|
||||
* @param \WC_Order|null $wc_order WC order to get data from.
|
||||
* @param string $payment_method WC payment method.
|
||||
* @param array $data Request data.
|
||||
*
|
||||
* @return Order Created PayPal order.
|
||||
*
|
||||
|
@ -423,7 +424,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* @throws PayPalApiException If create order request fails.
|
||||
* phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
private function create_paypal_order( \WC_Order $wc_order = null ): Order {
|
||||
private function create_paypal_order( \WC_Order $wc_order = null, string $payment_method = '', array $data = array() ): Order {
|
||||
assert( $this->purchase_unit instanceof PurchaseUnit );
|
||||
|
||||
$funding_source = $this->parsed_request_data['funding_source'] ?? '';
|
||||
|
@ -465,7 +466,9 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$payer,
|
||||
null,
|
||||
'',
|
||||
$action
|
||||
$action,
|
||||
$payment_method,
|
||||
$data
|
||||
);
|
||||
} catch ( PayPalApiException $exception ) {
|
||||
// Looks like currently there is no proper way to validate the shipping address for PayPal,
|
||||
|
|
|
@ -12,6 +12,39 @@ namespace WooCommerce\PayPalCommerce\Button\Helper;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
|
||||
trait ContextTrait {
|
||||
/**
|
||||
* Initializes context preconditions like is_cart() and is_checkout().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init_context(): void {
|
||||
if ( ! apply_filters( 'woocommerce_paypal_payments_block_classic_compat', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate is_checkout() on woocommerce/classic-shortcode checkout blocks.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
add_filter(
|
||||
'woocommerce_is_checkout',
|
||||
function ( $is_checkout ) {
|
||||
if ( $is_checkout ) {
|
||||
return $is_checkout;
|
||||
}
|
||||
return has_block( 'woocommerce/classic-shortcode {"shortcode":"checkout"}' );
|
||||
}
|
||||
);
|
||||
|
||||
// Activate is_cart() on woocommerce/classic-shortcode cart blocks.
|
||||
if ( ! is_cart() && is_callable( 'wc_maybe_define_constant' ) ) {
|
||||
if ( has_block( 'woocommerce/classic-shortcode' ) && ! has_block( 'woocommerce/classic-shortcode {"shortcode":"checkout"}' ) ) {
|
||||
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks WC is_checkout() + WC checkout ajax requests.
|
||||
*/
|
||||
|
@ -94,6 +127,10 @@ trait ContextTrait {
|
|||
return 'checkout';
|
||||
}
|
||||
|
||||
if ( $this->is_add_payment_method_page() ) {
|
||||
return 'add-payment-method';
|
||||
}
|
||||
|
||||
return 'mini-cart';
|
||||
}
|
||||
|
||||
|
@ -125,6 +162,11 @@ trait ContextTrait {
|
|||
* @return bool
|
||||
*/
|
||||
private function is_paypal_continuation(): bool {
|
||||
/**
|
||||
* Property is already defined in trait consumers.
|
||||
*
|
||||
* @psalm-suppress UndefinedThisPropertyFetch
|
||||
*/
|
||||
$order = $this->session_handler->order();
|
||||
if ( ! $order ) {
|
||||
return false;
|
||||
|
@ -137,7 +179,7 @@ trait ContextTrait {
|
|||
}
|
||||
|
||||
$source = $order->payment_source();
|
||||
if ( $source && $source->card() ) {
|
||||
if ( $source && $source->name() === 'card' ) {
|
||||
return false; // Ignore for DCC.
|
||||
}
|
||||
|
||||
|
@ -147,4 +189,22 @@ trait ContextTrait {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether current page is Add payment method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_add_payment_method_page(): bool {
|
||||
/**
|
||||
* Needed for WordPress `query_vars`.
|
||||
*
|
||||
* @psalm-suppress InvalidGlobal
|
||||
*/
|
||||
global $wp;
|
||||
|
||||
$page_id = wc_get_page_id( 'myaccount' );
|
||||
|
||||
return $page_id && is_page( $page_id ) && isset( $wp->query_vars['add-payment-method'] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Button\Helper;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CardAuthenticationResult as AuthResult;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\CardAuthenticationResultFactory;
|
||||
|
||||
/**
|
||||
* Class ThreeDSecure
|
||||
|
@ -23,6 +24,13 @@ class ThreeDSecure {
|
|||
const REJECT = 2;
|
||||
const RETRY = 3;
|
||||
|
||||
/**
|
||||
* Card authentication result factory.
|
||||
*
|
||||
* @var CardAuthenticationResultFactory
|
||||
*/
|
||||
private $card_authentication_result_factory;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
|
@ -33,10 +41,15 @@ class ThreeDSecure {
|
|||
/**
|
||||
* ThreeDSecure constructor.
|
||||
*
|
||||
* @param CardAuthenticationResultFactory $card_authentication_result_factory Card authentication result factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
public function __construct(
|
||||
CardAuthenticationResultFactory $card_authentication_result_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->card_authentication_result_factory = $card_authentication_result_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,17 +62,22 @@ class ThreeDSecure {
|
|||
* @return int
|
||||
*/
|
||||
public function proceed_with_order( Order $order ): int {
|
||||
if ( ! $order->payment_source() ) {
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
if ( ! $order->payment_source()->card() ) {
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
if ( ! $order->payment_source()->card()->authentication_result() ) {
|
||||
$payment_source = $order->payment_source();
|
||||
if ( ! $payment_source ) {
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
|
||||
$result = $order->payment_source()->card()->authentication_result();
|
||||
if ( ! $payment_source->properties()->brand ?? '' ) {
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
if ( ! $payment_source->properties()->authentication_result ?? '' ) {
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
|
||||
$authentication_result = $payment_source->properties()->authentication_result ?? null;
|
||||
if ( $authentication_result ) {
|
||||
$result = $this->card_authentication_result_factory->from_paypal_response( $authentication_result );
|
||||
|
||||
$this->logger->info( '3DS authentication result: ' . wc_print_r( $result->to_array(), true ) );
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) {
|
||||
|
@ -72,6 +90,8 @@ class ThreeDSecure {
|
|||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_NO ) {
|
||||
return $this->no_liability_shift( $result );
|
||||
}
|
||||
}
|
||||
|
||||
return self::NO_DECISION;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,12 @@ class CardFieldsModule implements ModuleInterface {
|
|||
add_filter(
|
||||
'ppcp_create_order_request_body_data',
|
||||
function( array $data ) use ( $c ): array {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$payment_method = wc_clean( wp_unslash( $_POST['payment_method'] ?? '' ) );
|
||||
if ( $payment_method !== CreditCardGateway::ID ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ return array(
|
|||
},
|
||||
|
||||
'compat.ppec.subscriptions-handler' => static function ( ContainerInterface $container ) {
|
||||
$ppcp_renewal_handler = $container->get( 'subscription.renewal-handler' );
|
||||
$ppcp_renewal_handler = $container->get( 'wc-subscriptions.renewal-handler' );
|
||||
$gateway = $container->get( 'compat.ppec.mock-gateway' );
|
||||
|
||||
return new PPEC\SubscriptionsHandler( $ppcp_renewal_handler, $gateway );
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace WooCommerce\PayPalCommerce\Compat\PPEC;
|
|||
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use stdClass;
|
||||
use WooCommerce\PayPalCommerce\Subscription\RenewalHandler;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\RenewalHandler;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,52 +1,9 @@
|
|||
.ppcp-button-googlepay {
|
||||
margin: 7px 0;
|
||||
overflow: hidden;
|
||||
min-height: 40px;
|
||||
height: 45px;
|
||||
|
||||
&.ppcp-button-pill {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
&.ppcp-button-minicart {
|
||||
display: block;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-checkout {
|
||||
.ppcp-button-googlepay {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-has-googlepay-block {
|
||||
|
||||
.wp-block-woocommerce-checkout {
|
||||
.ppcp-button-googlepay {
|
||||
margin: 0;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart {
|
||||
.ppcp-button-googlepay {
|
||||
margin: 0;
|
||||
height: 40px;
|
||||
}
|
||||
/* Workaround for blocks grid */
|
||||
.wc-block-components-express-payment__event-buttons {
|
||||
display: block;
|
||||
li[id*="express-payment-method-ppcp-"] {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.wp-admin {
|
||||
.ppcp-button-googlepay {
|
||||
pointer-events: none;
|
||||
.wp-block-woocommerce-checkout, .wp-block-woocommerce-cart {
|
||||
.gpay-button {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ import {setVisible} from '../../../ppcp-button/resources/js/modules/Helper/Hidin
|
|||
import {setEnabled} from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder";
|
||||
import UpdatePaymentData from "./Helper/UpdatePaymentData";
|
||||
import {apmButtonsInit} from "../../../ppcp-button/resources/js/modules/Helper/ApmButtons";
|
||||
|
||||
class GooglepayButton {
|
||||
|
||||
constructor(context, externalHandler, buttonConfig, ppcpConfig) {
|
||||
apmButtonsInit(ppcpConfig);
|
||||
|
||||
this.isInitialized = false;
|
||||
|
||||
this.context = context;
|
||||
|
@ -111,7 +114,7 @@ class GooglepayButton {
|
|||
}
|
||||
|
||||
if (['cart-block', 'checkout-block'].indexOf(this.context) !== -1) {
|
||||
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway';
|
||||
config.ppcpButtonWrapper = '#express-payment-method-ppcp-gateway-paypal';
|
||||
}
|
||||
|
||||
return config;
|
||||
|
@ -168,6 +171,10 @@ class GooglepayButton {
|
|||
this.waitForWrapper(wrapper, () => {
|
||||
jQuery(wrapper).addClass('ppcp-button-' + ppcpStyle.shape);
|
||||
|
||||
if (ppcpStyle.height) {
|
||||
jQuery(wrapper).css('height', `${ppcpStyle.height}px`)
|
||||
}
|
||||
|
||||
const button =
|
||||
this.paymentsClient.createButton({
|
||||
onClick: this.onButtonClick.bind(this),
|
||||
|
|
|
@ -67,7 +67,7 @@ import widgetBuilder from "../../../ppcp-button/resources/js/modules/Renderer/Wi
|
|||
buttonConfig.button.wrapper = selector;
|
||||
applyConfigOptions(buttonConfig);
|
||||
|
||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-googlepay"></div>`;
|
||||
const wrapperElement = `<div id="${selector.replace('#', '')}" class="ppcp-button-apm ppcp-button-googlepay"></div>`;
|
||||
|
||||
if (!jQuery(selector).length) {
|
||||
jQuery(ppcpConfig.button.wrapper).after(wrapperElement);
|
||||
|
|
|
@ -24,13 +24,6 @@ const GooglePayComponent = () => {
|
|||
manager.init();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const bodyClass = 'ppcp-has-googlepay-block';
|
||||
if (!document.body.classList.contains(bodyClass)) {
|
||||
document.body.classList.add(bodyClass);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Load GooglePay SDK
|
||||
loadCustomScript({ url: buttonConfig.sdk_url }).then(() => {
|
||||
|
@ -51,14 +44,13 @@ const GooglePayComponent = () => {
|
|||
}, [paypalLoaded, googlePayLoaded]);
|
||||
|
||||
return (
|
||||
<div id={buttonConfig.button.wrapper.replace('#', '')} className="ppcp-button-googlepay"></div>
|
||||
<div id={buttonConfig.button.wrapper.replace('#', '')} className="ppcp-button-apm ppcp-button-googlepay"></div>
|
||||
);
|
||||
}
|
||||
|
||||
const features = ['products'];
|
||||
let registerMethod = registerExpressPaymentMethod;
|
||||
|
||||
registerMethod({
|
||||
registerExpressPaymentMethod({
|
||||
name: buttonData.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: buttonData.title}}/>,
|
||||
content: <GooglePayComponent isEditing={false}/>,
|
||||
|
|
|
@ -311,7 +311,7 @@ class Button implements ButtonInterface {
|
|||
add_action(
|
||||
$render_placeholder,
|
||||
function () {
|
||||
echo '<span id="ppc-button-googlepay-container-minicart" class="ppcp-button-googlepay ppcp-button-minicart"></span>';
|
||||
echo '<span id="ppc-button-googlepay-container-minicart" class="ppcp-button-apm ppcp-button-googlepay ppcp-button-minicart"></span>';
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -325,7 +325,7 @@ class Button implements ButtonInterface {
|
|||
*/
|
||||
private function googlepay_button(): void {
|
||||
?>
|
||||
<div id="ppc-button-googlepay-container" class="ppcp-button-googlepay">
|
||||
<div id="ppc-button-googlepay-container" class="ppcp-button-apm ppcp-button-googlepay">
|
||||
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* Properties of the GooglePay module.
|
||||
* ApmApplies helper.
|
||||
* Checks if GooglePay is available for a given country and currency.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Googlepay\Helper
|
||||
*/
|
||||
|
@ -15,7 +16,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for DCC.
|
||||
* The matrix which countries and currency combinations can be used for GooglePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -38,7 +39,7 @@ class ApmApplies {
|
|||
/**
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for DCC.
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ document.addEventListener(
|
|||
|
||||
const smartButtonLocationsSelector = '#field-smart_button_locations';
|
||||
const smartButtonLocationsSelect = smartButtonLocationsSelector + ' select';
|
||||
const smartButtonSelectableLocations = ['product', 'cart', 'checkout', 'mini-cart'];
|
||||
const smartButtonSelectableLocations = ['product', 'cart', 'checkout', 'mini-cart', 'cart-block', 'checkout-block-express'];
|
||||
|
||||
const groupToggle = (selector, group) => {
|
||||
const toggleElement = document.querySelector(selector);
|
||||
|
@ -317,6 +317,7 @@ document.addEventListener(
|
|||
'#field-button' + locationPrefix + '_label',
|
||||
'#field-button' + locationPrefix + '_color',
|
||||
'#field-button' + locationPrefix + '_shape',
|
||||
'#field-button' + locationPrefix + '_height',
|
||||
'#field-button' + locationPrefix + '_preview',
|
||||
]
|
||||
|
||||
|
@ -324,11 +325,7 @@ document.addEventListener(
|
|||
inputSelectors.push('#field-button_' + location + '_heading');
|
||||
}
|
||||
|
||||
if (location === 'mini-cart') {
|
||||
inputSelectors.push('#field-button' + locationPrefix + '_height');
|
||||
}
|
||||
|
||||
return inputSelectors
|
||||
return inputSelectors.filter(selector => document.querySelector(selector));
|
||||
}
|
||||
|
||||
const allPayLaterMessaginginputSelectors = () => {
|
||||
|
|
|
@ -71,6 +71,12 @@ return array(
|
|||
'api.paypal-host-sandbox' => static function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_SANDBOX_API_URL;
|
||||
},
|
||||
'api.paypal-website-url-production' => static function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_URL;
|
||||
},
|
||||
'api.paypal-website-url-sandbox' => static function( ContainerInterface $container ) : string {
|
||||
return PAYPAL_SANDBOX_URL;
|
||||
},
|
||||
'api.partner_merchant_id-production' => static function( ContainerInterface $container ) : string {
|
||||
return CONNECT_WOO_MERCHANT_ID;
|
||||
},
|
||||
|
@ -89,6 +95,15 @@ return array(
|
|||
}
|
||||
return $container->get( 'api.paypal-host-production' );
|
||||
|
||||
},
|
||||
'api.paypal-website-url' => function( ContainerInterface $container ) : string {
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
assert( $environment instanceof Environment );
|
||||
if ( $environment->current_environment_is( Environment::SANDBOX ) ) {
|
||||
return $container->get( 'api.paypal-website-url-sandbox' );
|
||||
}
|
||||
return $container->get( 'api.paypal-website-url-production' );
|
||||
|
||||
},
|
||||
|
||||
'api.bearer' => static function ( ContainerInterface $container ): Bearer {
|
||||
|
|
File diff suppressed because it is too large
Load diff
3
modules/ppcp-paypal-subscriptions/.gitignore
vendored
Normal file
3
modules/ppcp-paypal-subscriptions/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
assets/js
|
||||
assets/css
|
17
modules/ppcp-paypal-subscriptions/composer.json
Normal file
17
modules/ppcp-paypal-subscriptions/composer.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "woocommerce/ppcp-paypal-subscriptions",
|
||||
"type": "dhii-mod",
|
||||
"description": "Module for PayPal Subscriptions API integration",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\PayPalSubscriptions\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
14
modules/ppcp-paypal-subscriptions/extensions.php
Normal file
14
modules/ppcp-paypal-subscriptions/extensions.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* The PayPalSubscriptions module extensions.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array();
|
16
modules/ppcp-paypal-subscriptions/module.php
Normal file
16
modules/ppcp-paypal-subscriptions/module.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* The PayPalSubscriptions module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return new PayPalSubscriptionsModule();
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "ppcp-subscription",
|
||||
"name": "ppcp-paypal-subscriptions",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"browserslist": [
|
43
modules/ppcp-paypal-subscriptions/services.php
Normal file
43
modules/ppcp-paypal-subscriptions/services.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* The PayPalSubscriptions module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'paypal-subscriptions.deactivate-plan-endpoint' => static function ( ContainerInterface $container ): DeactivatePlanEndpoint {
|
||||
return new DeactivatePlanEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.billing-plans' )
|
||||
);
|
||||
},
|
||||
'paypal-subscriptions.api-handler' => static function( ContainerInterface $container ): SubscriptionsApiHandler {
|
||||
return new SubscriptionsApiHandler(
|
||||
$container->get( 'api.endpoint.catalog-products' ),
|
||||
$container->get( 'api.factory.product' ),
|
||||
$container->get( 'api.endpoint.billing-plans' ),
|
||||
$container->get( 'api.factory.billing-cycle' ),
|
||||
$container->get( 'api.factory.payment-preferences' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'paypal-subscriptions.module.url' => static function ( ContainerInterface $container ): string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-paypal-subscriptions/',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
);
|
|
@ -2,12 +2,12 @@
|
|||
/**
|
||||
* The deactivate Subscription Plan Endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\OrderTracking\Endpoint
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use Exception;
|
||||
use WC_Product;
|
|
@ -1,50 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* The subscription module.
|
||||
* The PayPalSubscriptions module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use ActionScheduler_Store;
|
||||
use Exception;
|
||||
use WC_Order;
|
||||
use WC_Product;
|
||||
use WC_Product_Subscription;
|
||||
use WC_Product_Subscription_Variation;
|
||||
use WC_Product_Variable;
|
||||
use WC_Product_Variable_Subscription;
|
||||
use WC_Subscription;
|
||||
use WC_Subscriptions_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WC_Subscription;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WP_Post;
|
||||
|
||||
/**
|
||||
* Class SubscriptionModule
|
||||
* Class SavedPaymentCheckerModule
|
||||
*/
|
||||
class SubscriptionModule implements ModuleInterface {
|
||||
|
||||
use TransactionIdHandlingTrait;
|
||||
class PayPalSubscriptionsModule implements ModuleInterface {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
@ -60,470 +50,6 @@ class SubscriptionModule implements ModuleInterface {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
add_action(
|
||||
'woocommerce_scheduled_subscription_payment_' . PayPalGateway::ID,
|
||||
function ( $amount, $order ) use ( $c ) {
|
||||
$this->renew( $order, $c );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_scheduled_subscription_payment_' . CreditCardGateway::ID,
|
||||
function ( $amount, $order ) use ( $c ) {
|
||||
$this->renew( $order, $c );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_subscription_payment_complete',
|
||||
function ( $subscription ) use ( $c ) {
|
||||
if ( ! in_array( $subscription->get_payment_method(), array( PayPalGateway::ID, CreditCardGateway::ID, CardButtonGateway::ID ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paypal_subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $paypal_subscription_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_token_repository = $c->get( 'vaulting.repository.payment-token' );
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
|
||||
$this->add_payment_token_id( $subscription, $payment_token_repository, $logger );
|
||||
|
||||
if ( count( $subscription->get_related_orders() ) === 1 ) {
|
||||
$parent_order = $subscription->get_parent();
|
||||
if ( is_a( $parent_order, WC_Order::class ) ) {
|
||||
$order_repository = $c->get( 'api.repository.order' );
|
||||
$order = $order_repository->for_wc_order( $parent_order );
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
if ( $transaction_id ) {
|
||||
$subscription->update_meta_data( 'ppcp_previous_transaction_reference', $transaction_id );
|
||||
$subscription->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_gateway_description',
|
||||
function ( $description, $id ) use ( $c ) {
|
||||
$payment_token_repository = $c->get( 'vaulting.repository.payment-token' );
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
|
||||
return $this->display_saved_paypal_payments( $settings, (string) $id, $payment_token_repository, (string) $description, $subscription_helper );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_credit_card_form_fields',
|
||||
function ( $default_fields, $id ) use ( $c ) {
|
||||
$payment_token_repository = $c->get( 'vaulting.repository.payment-token' );
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
|
||||
return $this->display_saved_credit_cards( $settings, $id, $payment_token_repository, $default_fields, $subscription_helper );
|
||||
},
|
||||
20,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'ppcp_create_order_request_body_data',
|
||||
function( array $data ) use ( $c ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$wc_order_action = wc_clean( wp_unslash( $_POST['wc_order_action'] ?? '' ) );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$subscription_id = wc_clean( wp_unslash( $_POST['post_ID'] ?? '' ) );
|
||||
if ( ! $subscription_id ) {
|
||||
return $data;
|
||||
}
|
||||
$subscription = wc_get_order( $subscription_id );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if (
|
||||
$wc_order_action === 'wcs_process_renewal' && $subscription->get_payment_method() === CreditCardGateway::ID
|
||||
&& isset( $data['payment_source']['token'] ) && $data['payment_source']['token']['type'] === 'PAYMENT_METHOD_TOKEN'
|
||||
&& isset( $data['payment_source']['token']['source']->card )
|
||||
) {
|
||||
$data['payment_source'] = array(
|
||||
'card' => array(
|
||||
'vault_id' => $data['payment_source']['token']['id'],
|
||||
'stored_credential' => array(
|
||||
'payment_initiator' => 'MERCHANT',
|
||||
'payment_type' => 'RECURRING',
|
||||
'usage' => 'SUBSEQUENT',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$previous_transaction_reference = $subscription->get_meta( 'ppcp_previous_transaction_reference' );
|
||||
if ( $previous_transaction_reference ) {
|
||||
$data['payment_source']['card']['stored_credential']['previous_transaction_reference'] = $previous_transaction_reference;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
);
|
||||
|
||||
$this->subscriptions_api_integration( $c );
|
||||
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $hook ) use ( $c ) {
|
||||
if ( ! is_string( $hook ) ) {
|
||||
return;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$subscription_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( $hook !== 'post.php' || $subscription_mode !== 'subscriptions_api' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) );
|
||||
$product = wc_get_product( $post_id );
|
||||
if ( ! ( is_a( $product, WC_Product::class ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriptions_helper = $c->get( 'subscription.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
if (
|
||||
! $subscriptions_helper->plugin_is_active()
|
||||
|| ! (
|
||||
is_a( $product, WC_Product_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Variable_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Subscription_Variation::class )
|
||||
)
|
||||
|| ! WC_Subscriptions_Product::is_subscription( $product )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module_url = $c->get( 'subscription.module.url' );
|
||||
wp_enqueue_script(
|
||||
'ppcp-paypal-subscription',
|
||||
untrailingslashit( $module_url ) . '/assets/js/paypal-subscription.js',
|
||||
array( 'jquery' ),
|
||||
$c->get( 'ppcp.asset-version' ),
|
||||
true
|
||||
);
|
||||
|
||||
$products = array( $this->set_product_config( $product ) );
|
||||
if ( $product->get_type() === 'variable-subscription' ) {
|
||||
$products = array();
|
||||
|
||||
/**
|
||||
* Suppress pslam.
|
||||
*
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*
|
||||
* WC_Product_Variable_Subscription extends WC_Product_Variable.
|
||||
*/
|
||||
assert( $product instanceof WC_Product_Variable );
|
||||
$available_variations = $product->get_available_variations();
|
||||
foreach ( $available_variations as $variation ) {
|
||||
/**
|
||||
* The method is defined in WooCommerce.
|
||||
*
|
||||
* @psalm-suppress UndefinedMethod
|
||||
*/
|
||||
$variation = wc_get_product_object( 'variation', $variation['variation_id'] );
|
||||
$products[] = $this->set_product_config( $variation );
|
||||
}
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-paypal-subscription',
|
||||
'PayPalCommerceGatewayPayPalSubscriptionProducts',
|
||||
$products
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$endpoint = $c->get( 'subscription.deactivate-plan-endpoint' );
|
||||
assert( $endpoint instanceof DeactivatePlanEndpoint );
|
||||
add_action(
|
||||
'wc_ajax_' . DeactivatePlanEndpoint::ENDPOINT,
|
||||
array( $endpoint, 'handle_request' )
|
||||
);
|
||||
|
||||
add_action(
|
||||
'add_meta_boxes',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( string $post_type, $post_or_order_object ) use ( $c ) {
|
||||
if ( ! function_exists( 'wcs_get_subscription' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = ( $post_or_order_object instanceof WP_Post )
|
||||
? wc_get_order( $post_or_order_object->ID )
|
||||
: $post_or_order_object;
|
||||
|
||||
if ( ! is_a( $order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription = wcs_get_subscription( $order->get_id() );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen_id = wc_get_page_screen_id( 'shop_subscription' );
|
||||
remove_meta_box( 'woocommerce-subscription-schedule', $screen_id, 'side' );
|
||||
|
||||
$environment = $c->get( 'onboarding.environment' );
|
||||
add_meta_box(
|
||||
'ppcp_paypal_subscription',
|
||||
__( 'PayPal Subscription', 'woocommerce-paypal-payments' ),
|
||||
function() use ( $subscription_id, $environment ) {
|
||||
$host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||
$url = trailingslashit( $host ) . 'billing/subscriptions/' . $subscription_id;
|
||||
echo '<p>' . esc_html__( 'This subscription is linked to a PayPal Subscription, Cancel it to unlink.', 'woocommerce-paypal-payments' ) . '</p>';
|
||||
echo '<p><strong>' . esc_html__( 'Subscription:', 'woocommerce-paypal-payments' ) . '</strong> <a href="' . esc_url( $url ) . '" target="_blank">' . esc_attr( $subscription_id ) . '</a></p>';
|
||||
},
|
||||
$post_type,
|
||||
'side'
|
||||
);
|
||||
|
||||
},
|
||||
30,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'action_scheduler_before_execute',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $action_id ) {
|
||||
/**
|
||||
* Class exist in WooCommerce.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
$store = ActionScheduler_Store::instance();
|
||||
$action = $store->fetch_action( $action_id );
|
||||
|
||||
$subscription_id = $action->get_args()['subscription_id'] ?? null;
|
||||
if ( $subscription_id ) {
|
||||
$subscription = wcs_get_subscription( $subscription_id );
|
||||
if ( is_a( $subscription, WC_Subscription::class ) ) {
|
||||
$paypal_subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $paypal_subscription_id ) {
|
||||
as_unschedule_action( $action->get_hook(), $action->get_args() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a Subscription product renewal.
|
||||
*
|
||||
* @param \WC_Order $order WooCommerce order.
|
||||
* @param ContainerInterface|null $container The container.
|
||||
* @return void
|
||||
*/
|
||||
protected function renew( $order, $container ) {
|
||||
if ( ! ( $order instanceof \WC_Order ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$handler = $container->get( 'subscription.renewal-handler' );
|
||||
$handler->renew( $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Payment token ID to subscription.
|
||||
*
|
||||
* @param \WC_Subscription $subscription The subscription.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment repository.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
protected function add_payment_token_id(
|
||||
\WC_Subscription $subscription,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
try {
|
||||
$tokens = $payment_token_repository->all_for_user_id( $subscription->get_customer_id() );
|
||||
if ( $tokens ) {
|
||||
$latest_token_id = end( $tokens )->id() ? end( $tokens )->id() : '';
|
||||
$subscription->update_meta_data( 'payment_token_id', $latest_token_id );
|
||||
$subscription->save();
|
||||
}
|
||||
} catch ( RuntimeException $error ) {
|
||||
$message = sprintf(
|
||||
// translators: %1$s is the payment token Id, %2$s is the error message.
|
||||
__(
|
||||
'Could not add token Id to subscription %1$s: %2$s',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
$subscription->get_id(),
|
||||
$error->getMessage()
|
||||
);
|
||||
|
||||
$logger->log( 'warning', $message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays saved PayPal payments.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
* @param string $id The payment gateway Id.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param string $description The payment gateway description.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @return string
|
||||
*/
|
||||
protected function display_saved_paypal_payments(
|
||||
Settings $settings,
|
||||
string $id,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
string $description,
|
||||
SubscriptionHelper $subscription_helper
|
||||
): string {
|
||||
if ( $settings->has( 'vault_enabled' )
|
||||
&& $settings->get( 'vault_enabled' )
|
||||
&& PayPalGateway::ID === $id
|
||||
&& $subscription_helper->is_subscription_change_payment()
|
||||
) {
|
||||
$tokens = $payment_token_repository->all_for_user_id( get_current_user_id() );
|
||||
if ( ! $tokens || ! $payment_token_repository->tokens_contains_paypal( $tokens ) ) {
|
||||
return esc_html__(
|
||||
'No PayPal payments saved, in order to use a saved payment you first need to create it through a purchase.',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
|
||||
$output = sprintf(
|
||||
'<p class="form-row form-row-wide"><label>%1$s</label><select id="saved-paypal-payment" name="saved_paypal_payment">',
|
||||
esc_html__( 'Select a saved PayPal payment', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( isset( $token->source()->paypal ) ) {
|
||||
$output .= sprintf(
|
||||
'<option value="%1$s">%2$s</option>',
|
||||
$token->id(),
|
||||
$token->source()->paypal->payer->email_address
|
||||
);
|
||||
}
|
||||
}
|
||||
$output .= '</select></p>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays saved credit cards.
|
||||
*
|
||||
* @param Settings $settings The settings.
|
||||
* @param string $id The payment gateway Id.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param array $default_fields Default payment gateway fields.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @return array|mixed|string
|
||||
* @throws NotFoundException When setting was not found.
|
||||
*/
|
||||
protected function display_saved_credit_cards(
|
||||
Settings $settings,
|
||||
string $id,
|
||||
PaymentTokenRepository $payment_token_repository,
|
||||
array $default_fields,
|
||||
SubscriptionHelper $subscription_helper
|
||||
) {
|
||||
|
||||
if ( $settings->has( 'vault_enabled_dcc' )
|
||||
&& $settings->get( 'vault_enabled_dcc' )
|
||||
&& $subscription_helper->is_subscription_change_payment()
|
||||
&& CreditCardGateway::ID === $id
|
||||
) {
|
||||
$tokens = $payment_token_repository->all_for_user_id( get_current_user_id() );
|
||||
if ( ! $tokens || ! $payment_token_repository->tokens_contains_card( $tokens ) ) {
|
||||
$default_fields = array();
|
||||
$default_fields['saved-credit-card'] = esc_html__(
|
||||
'No Credit Card saved, in order to use a saved Credit Card you first need to create it through a purchase.',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
return $default_fields;
|
||||
}
|
||||
|
||||
$output = sprintf(
|
||||
'<p class="form-row form-row-wide"><label>%1$s</label><select id="saved-credit-card" name="saved_credit_card">',
|
||||
esc_html__( 'Select a saved Credit Card payment', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( isset( $token->source()->card ) ) {
|
||||
$output .= sprintf(
|
||||
'<option value="%1$s">%2$s ...%3$s</option>',
|
||||
$token->id(),
|
||||
$token->source()->card->brand,
|
||||
$token->source()->card->last_digits
|
||||
);
|
||||
}
|
||||
}
|
||||
$output .= '</select></p>';
|
||||
|
||||
$default_fields = array();
|
||||
$default_fields['saved-credit-card'] = $output;
|
||||
return $default_fields;
|
||||
}
|
||||
|
||||
return $default_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds PayPal subscriptions API integration.
|
||||
*
|
||||
* @param ContainerInterface $c The container.
|
||||
* @return void
|
||||
* @throws Exception When something went wrong.
|
||||
*/
|
||||
protected function subscriptions_api_integration( ContainerInterface $c ): void {
|
||||
add_action(
|
||||
'save_post',
|
||||
/**
|
||||
|
@ -554,7 +80,7 @@ class SubscriptionModule implements ModuleInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$subscriptions_api_handler = $c->get( 'subscription.api-handler' );
|
||||
$subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' );
|
||||
assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler );
|
||||
$this->update_subscription_product_meta( $product, $subscriptions_api_handler );
|
||||
},
|
||||
|
@ -571,7 +97,7 @@ class SubscriptionModule implements ModuleInterface {
|
|||
function( $variation_id ) use ( $c ) {
|
||||
$wcsnonce_save_variations = wc_clean( wp_unslash( $_POST['_wcsnonce_save_variations'] ?? '' ) );
|
||||
|
||||
$subscriptions_helper = $c->get( 'subscription.helper' );
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
if (
|
||||
|
@ -588,7 +114,7 @@ class SubscriptionModule implements ModuleInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$subscriptions_api_handler = $c->get( 'subscription.api-handler' );
|
||||
$subscriptions_api_handler = $c->get( 'paypal-subscriptions.api-handler' );
|
||||
assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler );
|
||||
$this->update_subscription_product_meta( $product, $subscriptions_api_handler );
|
||||
},
|
||||
|
@ -947,6 +473,236 @@ class SubscriptionModule implements ModuleInterface {
|
|||
10,
|
||||
3
|
||||
);
|
||||
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $hook ) use ( $c ) {
|
||||
if ( ! is_string( $hook ) ) {
|
||||
return;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$subscription_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( $hook !== 'post.php' || $subscription_mode !== 'subscriptions_api' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) );
|
||||
$product = wc_get_product( $post_id );
|
||||
if ( ! ( is_a( $product, WC_Product::class ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
if (
|
||||
! $subscriptions_helper->plugin_is_active()
|
||||
|| ! (
|
||||
is_a( $product, WC_Product_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Variable_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Subscription_Variation::class )
|
||||
)
|
||||
|| ! WC_Subscriptions_Product::is_subscription( $product )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module_url = $c->get( 'paypal-subscriptions.module.url' );
|
||||
wp_enqueue_script(
|
||||
'ppcp-paypal-subscription',
|
||||
untrailingslashit( $module_url ) . '/assets/js/paypal-subscription.js',
|
||||
array( 'jquery' ),
|
||||
$c->get( 'ppcp.asset-version' ),
|
||||
true
|
||||
);
|
||||
|
||||
$products = array( $this->set_product_config( $product ) );
|
||||
if ( $product->get_type() === 'variable-subscription' ) {
|
||||
$products = array();
|
||||
|
||||
/**
|
||||
* Suppress pslam.
|
||||
*
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*
|
||||
* WC_Product_Variable_Subscription extends WC_Product_Variable.
|
||||
*/
|
||||
assert( $product instanceof WC_Product_Variable );
|
||||
$available_variations = $product->get_available_variations();
|
||||
foreach ( $available_variations as $variation ) {
|
||||
/**
|
||||
* The method is defined in WooCommerce.
|
||||
*
|
||||
* @psalm-suppress UndefinedMethod
|
||||
*/
|
||||
$variation = wc_get_product_object( 'variation', $variation['variation_id'] );
|
||||
$products[] = $this->set_product_config( $variation );
|
||||
}
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-paypal-subscription',
|
||||
'PayPalCommerceGatewayPayPalSubscriptionProducts',
|
||||
$products
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$endpoint = $c->get( 'paypal-subscriptions.deactivate-plan-endpoint' );
|
||||
assert( $endpoint instanceof DeactivatePlanEndpoint );
|
||||
add_action(
|
||||
'wc_ajax_' . DeactivatePlanEndpoint::ENDPOINT,
|
||||
array( $endpoint, 'handle_request' )
|
||||
);
|
||||
|
||||
add_action(
|
||||
'add_meta_boxes',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( string $post_type, $post_or_order_object ) use ( $c ) {
|
||||
if ( ! function_exists( 'wcs_get_subscription' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = ( $post_or_order_object instanceof WP_Post )
|
||||
? wc_get_order( $post_or_order_object->ID )
|
||||
: $post_or_order_object;
|
||||
|
||||
if ( ! is_a( $order, WC_Order::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription = wcs_get_subscription( $order->get_id() );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen_id = wc_get_page_screen_id( 'shop_subscription' );
|
||||
remove_meta_box( 'woocommerce-subscription-schedule', $screen_id, 'side' );
|
||||
|
||||
$host = $c->get( 'api.paypal-website-url' );
|
||||
|
||||
add_meta_box(
|
||||
'ppcp_paypal_subscription',
|
||||
__( 'PayPal Subscription', 'woocommerce-paypal-payments' ),
|
||||
function() use ( $subscription_id, $host ) {
|
||||
$url = trailingslashit( $host ) . 'billing/subscriptions/' . $subscription_id;
|
||||
echo '<p>' . esc_html__( 'This subscription is linked to a PayPal Subscription, Cancel it to unlink.', 'woocommerce-paypal-payments' ) . '</p>';
|
||||
echo '<p><strong>' . esc_html__( 'Subscription:', 'woocommerce-paypal-payments' ) . '</strong> <a href="' . esc_url( $url ) . '" target="_blank">' . esc_attr( $subscription_id ) . '</a></p>';
|
||||
},
|
||||
$post_type,
|
||||
'side'
|
||||
);
|
||||
|
||||
},
|
||||
30,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'action_scheduler_before_execute',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $action_id ) {
|
||||
/**
|
||||
* Class exist in WooCommerce.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
$store = ActionScheduler_Store::instance();
|
||||
$action = $store->fetch_action( $action_id );
|
||||
|
||||
$subscription_id = $action->get_args()['subscription_id'] ?? null;
|
||||
if ( $subscription_id ) {
|
||||
$subscription = wcs_get_subscription( $subscription_id );
|
||||
if ( is_a( $subscription, WC_Subscription::class ) ) {
|
||||
$paypal_subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $paypal_subscription_id ) {
|
||||
as_unschedule_action( $action->get_hook(), $action->get_args() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates subscription product meta.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @param SubscriptionsApiHandler $subscriptions_api_handler The subscription api handler.
|
||||
* @return void
|
||||
*/
|
||||
private function update_subscription_product_meta( WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler ): void {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) );
|
||||
$product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product );
|
||||
$product->save();
|
||||
|
||||
if ( ( $product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation' ) && $enable_subscription_product === 'yes' ) {
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$subscriptions_api_handler->update_product( $product );
|
||||
$subscriptions_api_handler->update_plan( $product );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) {
|
||||
$subscriptions_api_handler->create_product( $product );
|
||||
}
|
||||
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) );
|
||||
if ( ! is_string( $subscription_plan_name ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name );
|
||||
$product->save();
|
||||
|
||||
$subscriptions_api_handler->create_plan( $subscription_plan_name, $product );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subscription product configuration.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @return array
|
||||
*/
|
||||
private function set_product_config( WC_Product $product ): array {
|
||||
$plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array();
|
||||
$plan_id = $plan['id'] ?? '';
|
||||
|
||||
return array(
|
||||
'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '',
|
||||
'plan_id' => $plan_id,
|
||||
'product_id' => $product->get_id(),
|
||||
'ajax' => array(
|
||||
'deactivate_plan' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1019,66 +775,4 @@ class SubscriptionModule implements ModuleInterface {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates subscription product meta.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @param SubscriptionsApiHandler $subscriptions_api_handler The subscription api handler.
|
||||
* @return void
|
||||
*/
|
||||
private function update_subscription_product_meta( WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler ): void {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) );
|
||||
$product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product );
|
||||
$product->save();
|
||||
|
||||
if ( ( $product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation' ) && $enable_subscription_product === 'yes' ) {
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
$subscriptions_api_handler->update_product( $product );
|
||||
$subscriptions_api_handler->update_plan( $product );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) {
|
||||
$subscriptions_api_handler->create_product( $product );
|
||||
}
|
||||
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) );
|
||||
if ( ! is_string( $subscription_plan_name ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name );
|
||||
$product->save();
|
||||
|
||||
$subscriptions_api_handler->create_plan( $subscription_plan_name, $product );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subscription product configuration.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @return array
|
||||
*/
|
||||
private function set_product_config( WC_Product $product ): array {
|
||||
$plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array();
|
||||
$plan_id = $plan['id'] ?? '';
|
||||
|
||||
return array(
|
||||
'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '',
|
||||
'plan_id' => $plan_id,
|
||||
'product_id' => $product->get_id(),
|
||||
'ajax' => array(
|
||||
'deactivate_plan' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@
|
|||
/**
|
||||
* The subscription module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Product;
|
14
modules/ppcp-save-payment-methods/.babelrc
Normal file
14
modules/ppcp-save-payment-methods/.babelrc
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react"
|
||||
]
|
||||
]
|
||||
}
|
3
modules/ppcp-save-payment-methods/.gitignore
vendored
Normal file
3
modules/ppcp-save-payment-methods/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
assets/js
|
||||
assets/css
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "woocommerce/ppcp-subscription",
|
||||
"name": "woocommerce/ppcp-save-payment-methods",
|
||||
"type": "dhii-mod",
|
||||
"description": "Subscription module for PPCP",
|
||||
"description": "Save payment methods module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\Subscription\\": "src"
|
||||
"WooCommerce\\PayPalCommerce\\SavePaymentMethods\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
14
modules/ppcp-save-payment-methods/extensions.php
Normal file
14
modules/ppcp-save-payment-methods/extensions.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* The save payment methods module extensions.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\SavePaymentMethods
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array();
|
16
modules/ppcp-save-payment-methods/module.php
Normal file
16
modules/ppcp-save-payment-methods/module.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* The save payment methods module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\SavePaymentMethods
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return new SavePaymentMethodsModule();
|
||||
};
|
34
modules/ppcp-save-payment-methods/package.json
Normal file
34
modules/ppcp-save-payment-methods/package.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "ppcp-save-payment-methods",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"browserslist": [
|
||||
"> 0.5%",
|
||||
"Safari >= 8",
|
||||
"Chrome >= 41",
|
||||
"Firefox >= 43",
|
||||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"core-js": "^3.25.0",
|
||||
"@paypal/paypal-js": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19",
|
||||
"@babel/preset-env": "^7.19",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@woocommerce/dependency-extraction-webpack-plugin": "^2.2.0",
|
||||
"babel-loader": "^8.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.76",
|
||||
"webpack-cli": "^4.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
import {
|
||||
getCurrentPaymentMethod,
|
||||
ORDER_BUTTON_SELECTOR,
|
||||
PaymentMethods
|
||||
} from "../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState";
|
||||
import {loadScript} from "@paypal/paypal-js";
|
||||
import {
|
||||
setVisible,
|
||||
setVisibleByClass
|
||||
} from "../../../ppcp-button/resources/js/modules/Helper/Hiding";
|
||||
import ErrorHandler from "../../../ppcp-button/resources/js/modules/ErrorHandler";
|
||||
import {cardFieldStyles} from "../../../ppcp-button/resources/js/modules/Helper/CardFieldsHelper";
|
||||
|
||||
const errorHandler = new ErrorHandler(
|
||||
PayPalCommerceGateway.labels.error.generic,
|
||||
document.querySelector('.woocommerce-notices-wrapper')
|
||||
);
|
||||
|
||||
const init = () => {
|
||||
setVisibleByClass(ORDER_BUTTON_SELECTOR, getCurrentPaymentMethod() !== PaymentMethods.PAYPAL, 'ppcp-hidden');
|
||||
setVisible(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`, getCurrentPaymentMethod() === PaymentMethods.PAYPAL);
|
||||
}
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
jQuery(document.body).on('click init_add_payment_method', '.payment_methods input.input-radio', function () {
|
||||
init()
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
loadScript({
|
||||
clientId: ppcp_add_payment_method.client_id,
|
||||
merchantId: ppcp_add_payment_method.merchant_id,
|
||||
dataUserIdToken: ppcp_add_payment_method.id_token,
|
||||
components: 'buttons,card-fields',
|
||||
})
|
||||
.then((paypal) => {
|
||||
errorHandler.clear();
|
||||
|
||||
paypal.Buttons(
|
||||
{
|
||||
createVaultSetupToken: async () => {
|
||||
const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce,
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (result.data.id) {
|
||||
return result.data.id
|
||||
}
|
||||
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
},
|
||||
onApprove: async ({vaultSetupToken}) => {
|
||||
const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce,
|
||||
vault_setup_token: vaultSetupToken,
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json();
|
||||
if(result.success === true) {
|
||||
window.location.href = ppcp_add_payment_method.payment_methods_page;
|
||||
return;
|
||||
}
|
||||
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error)
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
}
|
||||
},
|
||||
).render(`#ppc-button-${PaymentMethods.PAYPAL}-save-payment-method`);
|
||||
|
||||
const cardField = paypal.CardFields({
|
||||
createVaultSetupToken: async () => {
|
||||
const response = await fetch(ppcp_add_payment_method.ajax.create_setup_token.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: ppcp_add_payment_method.ajax.create_setup_token.nonce,
|
||||
payment_method: PaymentMethods.CARDS,
|
||||
verification_method: ppcp_add_payment_method.verification_method
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (result.data.id) {
|
||||
return result.data.id
|
||||
}
|
||||
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
},
|
||||
onApprove: async ({vaultSetupToken}) => {
|
||||
const response = await fetch(ppcp_add_payment_method.ajax.create_payment_token.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: ppcp_add_payment_method.ajax.create_payment_token.nonce,
|
||||
vault_setup_token: vaultSetupToken,
|
||||
payment_method: PaymentMethods.CARDS
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json();
|
||||
if(result.success === true) {
|
||||
window.location.href = ppcp_add_payment_method.payment_methods_page;
|
||||
return;
|
||||
}
|
||||
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error)
|
||||
errorHandler.message(ppcp_add_payment_method.error_message);
|
||||
}
|
||||
});
|
||||
|
||||
if (cardField.isEligible()) {
|
||||
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
|
||||
if (nameField) {
|
||||
let styles = cardFieldStyles(nameField);
|
||||
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
|
||||
nameField.hidden = true;
|
||||
}
|
||||
|
||||
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
|
||||
if (numberField) {
|
||||
let styles = cardFieldStyles(numberField);
|
||||
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
|
||||
numberField.hidden = true;
|
||||
}
|
||||
|
||||
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
|
||||
if (expiryField) {
|
||||
let styles = cardFieldStyles(expiryField);
|
||||
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
|
||||
expiryField.hidden = true;
|
||||
}
|
||||
|
||||
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
|
||||
if (cvvField) {
|
||||
let styles = cardFieldStyles(cvvField);
|
||||
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
|
||||
cvvField.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#place_order').addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
cardField.submit()
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
});
|
||||
});
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
);
|
||||
|
90
modules/ppcp-save-payment-methods/services.php
Normal file
90
modules/ppcp-save-payment-methods/services.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* The save payment methods module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\SavePaymentMethods
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
||||
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CaptureCardPayment;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Helper\SavePaymentMethodsApplies;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'save-payment-methods.eligible' => static function ( ContainerInterface $container ): bool {
|
||||
$save_payment_methods_applies = $container->get( 'save-payment-methods.helpers.save-payment-methods-applies' );
|
||||
assert( $save_payment_methods_applies instanceof SavePaymentMethodsApplies );
|
||||
|
||||
return $save_payment_methods_applies->for_country_currency();
|
||||
},
|
||||
'save-payment-methods.helpers.save-payment-methods-applies' => static function ( ContainerInterface $container ) : SavePaymentMethodsApplies {
|
||||
return new SavePaymentMethodsApplies(
|
||||
$container->get( 'save-payment-methods.supported-country-currency-matrix' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
'save-payment-methods.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_save_payment_methods_supported_country_currency_matrix',
|
||||
array(
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
'save-payment-methods.module.url' => static function ( ContainerInterface $container ): string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-save-payment-methods/',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'save-payment-methods.endpoint.create-setup-token' => static function ( ContainerInterface $container ): CreateSetupToken {
|
||||
return new CreateSetupToken(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.payment-method-tokens' )
|
||||
);
|
||||
},
|
||||
'save-payment-methods.wc-payment-tokens' => static function( ContainerInterface $container ): WooCommercePaymentTokens {
|
||||
return new WooCommercePaymentTokens(
|
||||
$container->get( 'vaulting.payment-token-helper' ),
|
||||
$container->get( 'vaulting.payment-token-factory' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'save-payment-methods.endpoint.create-payment-token' => static function ( ContainerInterface $container ): CreatePaymentToken {
|
||||
return new CreatePaymentToken(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.payment-method-tokens' ),
|
||||
$container->get( 'save-payment-methods.wc-payment-tokens' )
|
||||
);
|
||||
},
|
||||
'save-payment-methods.endpoint.capture-card-payment' => static function( ContainerInterface $container ): CaptureCardPayment {
|
||||
return new CaptureCardPayment(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'api.factory.order' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
/**
|
||||
* The Capture Card Payment endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class CaptureCardPayment
|
||||
*/
|
||||
class CaptureCardPayment implements EndpointInterface {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
const ENDPOINT = 'ppc-capture-card-payment';
|
||||
|
||||
/**
|
||||
* The request data.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The order factory.
|
||||
*
|
||||
* @var OrderFactory
|
||||
*/
|
||||
private $order_factory;
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
private $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
private $order_endpoint;
|
||||
|
||||
/**
|
||||
* The session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* CaptureCardPayment constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param OrderFactory $order_factory The order factory.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param SessionHandler $session_handler The session handler.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
string $host,
|
||||
Bearer $bearer,
|
||||
OrderFactory $order_factory,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
OrderEndpoint $order_endpoint,
|
||||
SessionHandler $session_handler,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->order_factory = $order_factory;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->logger = $logger;
|
||||
$this->session_handler = $session_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id() );
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( $token->get_id() === (int) $data['payment_token'] ) {
|
||||
try {
|
||||
$order = $this->create_order( $token->get_token() );
|
||||
|
||||
$id = $order->id ?? '';
|
||||
$status = $order->status ?? '';
|
||||
$payment_source = isset( $order->payment_source->card ) ? 'card' : '';
|
||||
if ( $id && $status && $payment_source ) {
|
||||
WC()->session->set(
|
||||
'ppcp_saved_payment_card',
|
||||
array(
|
||||
'order_id' => $id,
|
||||
'status' => $status,
|
||||
'payment_source' => $payment_source,
|
||||
)
|
||||
);
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates PayPal order from the given card vault id.
|
||||
*
|
||||
* @param string $vault_id Vault id.
|
||||
* @return stdClass
|
||||
* @throws RuntimeException When request fails.
|
||||
*/
|
||||
private function create_order( string $vault_id ): stdClass {
|
||||
$items = array( $this->purchase_unit_factory->from_wc_cart() );
|
||||
|
||||
$data = array(
|
||||
'intent' => 'CAPTURE',
|
||||
'purchase_units' => array_map(
|
||||
static function ( PurchaseUnit $item ): array {
|
||||
return $item->to_array( true, false );
|
||||
},
|
||||
$items
|
||||
),
|
||||
'payment_source' => array(
|
||||
'card' => array(
|
||||
'vault_id' => $vault_id,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/checkout/orders';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'PayPal-Request-Id' => uniqid( 'ppcp-', true ),
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( $response instanceof WP_Error ) {
|
||||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
return json_decode( $response['body'] );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* The Create Payment Token endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\WooCommercePaymentTokens;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class CreatePaymentToken
|
||||
*/
|
||||
class CreatePaymentToken implements EndpointInterface {
|
||||
|
||||
const ENDPOINT = 'ppc-create-payment-token';
|
||||
|
||||
/**
|
||||
* The request data.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The payment method tokens endpoint.
|
||||
*
|
||||
* @var PaymentMethodTokensEndpoint
|
||||
*/
|
||||
private $payment_method_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* The WC payment tokens.
|
||||
*
|
||||
* @var WooCommercePaymentTokens
|
||||
*/
|
||||
private $wc_payment_tokens;
|
||||
|
||||
/**
|
||||
* CreatePaymentToken constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param PaymentMethodTokensEndpoint $payment_method_tokens_endpoint The payment method tokens endpoint.
|
||||
* @param WooCommercePaymentTokens $wc_payment_tokens The WC payment tokens.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
PaymentMethodTokensEndpoint $payment_method_tokens_endpoint,
|
||||
WooCommercePaymentTokens $wc_payment_tokens
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->payment_method_tokens_endpoint = $payment_method_tokens_endpoint;
|
||||
$this->wc_payment_tokens = $wc_payment_tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception On Error.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
/**
|
||||
* Suppress ArgumentTypeCoercion
|
||||
*
|
||||
* @psalm-suppress ArgumentTypeCoercion
|
||||
*/
|
||||
$payment_source = new PaymentSource(
|
||||
'token',
|
||||
(object) array(
|
||||
'id' => $data['vault_setup_token'],
|
||||
'type' => 'SETUP_TOKEN',
|
||||
)
|
||||
);
|
||||
|
||||
$result = $this->payment_method_tokens_endpoint->payment_tokens( $payment_source );
|
||||
|
||||
if ( is_user_logged_in() && isset( $result->customer->id ) ) {
|
||||
update_user_meta( get_current_user_id(), '_ppcp_target_customer_id', $result->customer->id );
|
||||
|
||||
if ( isset( $result->payment_source->paypal ) ) {
|
||||
$email = '';
|
||||
if ( isset( $result->payment_source->paypal->email_address ) ) {
|
||||
$email = $result->payment_source->paypal->email_address;
|
||||
}
|
||||
|
||||
$this->wc_payment_tokens->create_payment_token_paypal(
|
||||
get_current_user_id(),
|
||||
$result->id,
|
||||
$email
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $result->payment_source->card ) ) {
|
||||
$token = new \WC_Payment_Token_CC();
|
||||
$token->set_token( $result->id );
|
||||
$token->set_user_id( get_current_user_id() );
|
||||
$token->set_gateway_id( CreditCardGateway::ID );
|
||||
|
||||
$token->set_last4( $result->payment_source->card->last_digits ?? '' );
|
||||
$expiry = explode( '-', $result->payment_source->card->expiry ?? '' );
|
||||
$token->set_expiry_year( $expiry[0] ?? '' );
|
||||
$token->set_expiry_month( $expiry[1] ?? '' );
|
||||
|
||||
$brand = $result->payment_source->card->brand ?? __( 'N/A', 'woocommerce-paypal-payments' );
|
||||
if ( $brand ) {
|
||||
$token->set_card_type( $brand );
|
||||
}
|
||||
|
||||
$token->save();
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
return true;
|
||||
} catch ( Exception $exception ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/**
|
||||
* The Create Setup Token endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
||||
/**
|
||||
* Class CreateSetupToken
|
||||
*/
|
||||
class CreateSetupToken implements EndpointInterface {
|
||||
|
||||
const ENDPOINT = 'ppc-create-setup-token';
|
||||
|
||||
/**
|
||||
* The request data helper.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* Payment Method Tokens endpoint.
|
||||
*
|
||||
* @var PaymentMethodTokensEndpoint
|
||||
*/
|
||||
private $payment_method_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* CreateSetupToken constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param PaymentMethodTokensEndpoint $payment_method_tokens_endpoint Payment Method Tokens endpoint.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
PaymentMethodTokensEndpoint $payment_method_tokens_endpoint
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->payment_method_tokens_endpoint = $payment_method_tokens_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception On Error.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
try {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$payment_source = new PaymentSource(
|
||||
'paypal',
|
||||
(object) array(
|
||||
'usage_type' => 'MERCHANT',
|
||||
'experience_context' => (object) array(
|
||||
'return_url' => esc_url( wc_get_account_endpoint_url( 'payment-methods' ) ),
|
||||
'cancel_url' => esc_url( wc_get_account_endpoint_url( 'add-payment-method' ) ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$payment_method = $data['payment_method'] ?? '';
|
||||
if ( $payment_method === CreditCardGateway::ID ) {
|
||||
$properties = (object) array();
|
||||
|
||||
$verification_method = $data['verification_method'] ?? '';
|
||||
if ( $verification_method === 'SCA_WHEN_REQUIRED' || $verification_method === 'SCA_ALWAYS' ) {
|
||||
$properties = (object) array(
|
||||
'verification_method' => $verification_method,
|
||||
'usage_type' => 'MERCHANT',
|
||||
'experience_context' => (object) array(
|
||||
'return_url' => esc_url( wc_get_account_endpoint_url( 'payment-methods' ) ),
|
||||
'cancel_url' => esc_url( wc_get_account_endpoint_url( 'add-payment-method' ) ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$payment_source = new PaymentSource(
|
||||
'card',
|
||||
$properties
|
||||
);
|
||||
}
|
||||
|
||||
$result = $this->payment_method_tokens_endpoint->setup_tokens( $payment_source );
|
||||
|
||||
wp_send_json_success( $result );
|
||||
return true;
|
||||
} catch ( Exception $exception ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/**
|
||||
* Properties of the Save Payment Methods module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\SavePaymentMethods\Helper
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Helper;
|
||||
|
||||
/**
|
||||
* Class SavePaymentMethodsApplies
|
||||
*/
|
||||
class SavePaymentMethodsApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for Save Payment Methods.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* SavePaymentMethodsApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for Save Payment Methods.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether Save Payment Methods can be used in the current country and the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,377 @@
|
|||
<?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 WC_Payment_Tokens;
|
||||
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\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CaptureCardPayment;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class SavePaymentMethodsModule
|
||||
*/
|
||||
class SavePaymentMethodsModule implements ModuleInterface {
|
||||
|
||||
use ContextTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
if ( ! $c->get( 'save-payment-methods.eligible' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_localized_script_data',
|
||||
function( array $localized_script_data ) use ( $c ) {
|
||||
$api = $c->get( 'api.user-id-token' );
|
||||
assert( $api instanceof UserIdToken );
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
$localized_script_data = $this->add_id_token_to_script_data( $api, $logger, $localized_script_data );
|
||||
|
||||
$localized_script_data['ajax']['capture_card_payment'] = array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CaptureCardPayment::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( CaptureCardPayment::nonce() ),
|
||||
);
|
||||
|
||||
return $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 ): array {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$wc_order_action = wc_clean( wp_unslash( $_POST['wc_order_action'] ?? '' ) );
|
||||
if ( $wc_order_action === 'wcs_process_renewal' ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$subscription_id = wc_clean( wp_unslash( $_POST['post_ID'] ?? '' ) );
|
||||
$subscription = wcs_get_subscription( (int) $subscription_id );
|
||||
if ( $subscription ) {
|
||||
$customer_id = $subscription->get_customer_id();
|
||||
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
|
||||
foreach ( $wc_tokens as $token ) {
|
||||
$data['payment_source'] = array(
|
||||
'paypal' => array(
|
||||
'vault_id' => $token->get_token(),
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $payment_method === CreditCardGateway::ID ) {
|
||||
$save_payment_method = $request_data['save_payment_method'] ?? false;
|
||||
if ( $save_payment_method ) {
|
||||
$data['payment_source'] = array(
|
||||
'card' => array(
|
||||
'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 ) {
|
||||
$data['payment_source']['card']['attributes']['customer'] = array(
|
||||
'id' => $target_customer_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $payment_method === PayPalGateway::ID ) {
|
||||
$data['payment_source'] = array(
|
||||
'paypal' => array(
|
||||
'attributes' => array(
|
||||
'vault' => array(
|
||||
'store_in_vault' => 'ON_SUCCESS',
|
||||
'usage_type' => 'MERCHANT',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
},
|
||||
10,
|
||||
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( 'save-payment-methods.wc-payment-tokens' );
|
||||
assert( $wc_payment_tokens instanceof WooCommercePaymentTokens );
|
||||
|
||||
if ( $wc_order->get_payment_method() === CreditCardGateway::ID ) {
|
||||
$token = new \WC_Payment_Token_CC();
|
||||
$token->set_token( $token_id );
|
||||
$token->set_user_id( $wc_order->get_customer_id() );
|
||||
$token->set_gateway_id( CreditCardGateway::ID );
|
||||
|
||||
$token->set_last4( $payment_source->properties()->last_digits ?? '' );
|
||||
$expiry = explode( '-', $payment_source->properties()->expiry ?? '' );
|
||||
$token->set_expiry_year( $expiry[0] ?? '' );
|
||||
$token->set_expiry_month( $expiry[1] ?? '' );
|
||||
$token->set_card_type( $payment_source->properties()->brand ?? '' );
|
||||
|
||||
$token->save();
|
||||
}
|
||||
|
||||
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
|
||||
$wc_payment_tokens->create_payment_token_paypal(
|
||||
$wc_order->get_customer_id(),
|
||||
$token_id,
|
||||
$payment_source->properties()->email_address ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter( 'woocommerce_paypal_payments_disable_add_payment_method', '__return_false' );
|
||||
add_filter( 'woocommerce_paypal_payments_subscription_renewal_return_before_create_order_without_token', '__return_false' );
|
||||
add_filter( 'woocommerce_paypal_payments_should_render_card_custom_fields', '__return_false' );
|
||||
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
function() use ( $c ) {
|
||||
if ( ! is_user_logged_in() || ! $this->is_add_payment_method_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module_url = $c->get( 'save-payment-methods.module.url' );
|
||||
wp_enqueue_script(
|
||||
'ppcp-add-payment-method',
|
||||
untrailingslashit( $module_url ) . '/assets/js/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 = '';
|
||||
if ( is_user_logged_in() ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
||||
}
|
||||
|
||||
$id_token = $api->id_token( $target_customer_id );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
$verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : '';
|
||||
|
||||
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' ),
|
||||
'error_message' => __( 'Could not save payment method.', 'woocommerce-paypal-payments' ),
|
||||
'verification_method' => $verification_method,
|
||||
'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() ),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger->error( $error );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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(
|
||||
'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 ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger->error( $error );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_credit_card_gateway_vault_supports',
|
||||
function( array $supports ) use ( $c ): array {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof ContainerInterface );
|
||||
|
||||
if ( $settings->has( 'vault_enabled_dcc' ) && $settings->get( 'vault_enabled_dcc' ) ) {
|
||||
$supports[] = 'tokenization';
|
||||
}
|
||||
|
||||
return $supports;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . CaptureCardPayment::ENDPOINT,
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'save-payment-methods.endpoint.capture-card-payment' );
|
||||
assert( $endpoint instanceof CaptureCardPayment );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
|
||||
$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 ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger->error( $error );
|
||||
}
|
||||
|
||||
return $localized_script_data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
/**
|
||||
* Service to create WC Payment Tokens.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Applepay
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
|
||||
/**
|
||||
* Class WooCommercePaymentTokens
|
||||
*/
|
||||
class WooCommercePaymentTokens {
|
||||
|
||||
/**
|
||||
* The payment token helper.
|
||||
*
|
||||
* @var PaymentTokenHelper
|
||||
*/
|
||||
private $payment_token_helper;
|
||||
|
||||
/**
|
||||
* The payment token factory.
|
||||
*
|
||||
* @var PaymentTokenFactory
|
||||
*/
|
||||
private $payment_token_factory;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* WooCommercePaymentTokens constructor.
|
||||
*
|
||||
* @param PaymentTokenHelper $payment_token_helper The payment token helper.
|
||||
* @param PaymentTokenFactory $payment_token_factory The payment token factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
PaymentTokenHelper $payment_token_helper,
|
||||
PaymentTokenFactory $payment_token_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->payment_token_helper = $payment_token_helper;
|
||||
$this->payment_token_factory = $payment_token_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WC Payment Token for PayPal payment.
|
||||
*
|
||||
* @param int $customer_id The WC customer ID.
|
||||
* @param string $token The PayPal payment token.
|
||||
* @param string $email The PayPal customer email.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create_payment_token_paypal(
|
||||
int $customer_id,
|
||||
string $token,
|
||||
string $email
|
||||
): void {
|
||||
|
||||
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
|
||||
if ( $this->payment_token_helper->token_exist( $wc_tokens, $token ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
|
||||
assert( $payment_token_paypal instanceof PaymentTokenPayPal );
|
||||
|
||||
$payment_token_paypal->set_token( $token );
|
||||
$payment_token_paypal->set_user_id( $customer_id );
|
||||
$payment_token_paypal->set_gateway_id( PayPalGateway::ID );
|
||||
|
||||
if ( $email && is_email( $email ) ) {
|
||||
$payment_token_paypal->set_email( $email );
|
||||
}
|
||||
|
||||
try {
|
||||
$payment_token_paypal->save();
|
||||
} catch ( Exception $exception ) {
|
||||
$this->logger->error(
|
||||
"Could not create WC payment token PayPal for customer {$customer_id}. " . $exception->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
38
modules/ppcp-save-payment-methods/webpack.config.js
Normal file
38
modules/ppcp-save-payment-methods/webpack.config.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
const path = require('path');
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
|
||||
|
||||
module.exports = {
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||
entry: {
|
||||
'add-payment-method': path.resolve('./resources/js/add-payment-method.js')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'css/[name].css',
|
||||
}
|
||||
},
|
||||
{loader:'sass-loader'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
2247
modules/ppcp-save-payment-methods/yarn.lock
Normal file
2247
modules/ppcp-save-payment-methods/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,12 +10,12 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\SavedPaymentChecker;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
$subscription_helper = $container->get( 'subscription.helper' );
|
||||
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
|
||||
$insert_after = function( array $array, string $key, array $new ): array {
|
||||
|
|
|
@ -16,7 +16,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentsEndpoint;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokenEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository;
|
||||
use WooCommerce\PayPalCommerce\Subscription\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace WooCommerce\PayPalCommerce\SavedPaymentChecker;
|
|||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
|
@ -43,7 +43,7 @@ class SavedPaymentCheckerModule implements ModuleInterface {
|
|||
add_filter(
|
||||
'woocommerce_paypal_payments_order_intent',
|
||||
function( string $intent ) use ( $c ) {
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
|
||||
if ( $subscription_helper->cart_contains_subscription() || $subscription_helper->current_product_is_subscription() ) {
|
||||
|
@ -60,7 +60,7 @@ class SavedPaymentCheckerModule implements ModuleInterface {
|
|||
add_action(
|
||||
'woocommerce_paypal_payments_before_handle_payment_success',
|
||||
function( WC_Order $wc_order ) use ( $c ) {
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
|
||||
if ( $subscription_helper->has_subscription( $wc_order->get_id() ) ) {
|
||||
|
@ -93,7 +93,7 @@ class SavedPaymentCheckerModule implements ModuleInterface {
|
|||
add_action(
|
||||
'woocommerce_email_before_order_table',
|
||||
function( WC_Order $order ) use ( $c ) {
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
@ -119,7 +119,7 @@ class SavedPaymentCheckerModule implements ModuleInterface {
|
|||
add_action(
|
||||
'woocommerce_email_after_order_table',
|
||||
function( WC_Order $order ) use ( $c ) {
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscription_helper instanceof SubscriptionHelper );
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
|
|
@ -9,7 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\StatusReport;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
|
@ -65,7 +65,7 @@ class StatusReportModule implements ModuleInterface {
|
|||
$messages_apply = $c->get( 'button.helper.messages-apply' );
|
||||
|
||||
/* @var SubscriptionHelper $subscription_helper The subscription helper class. */
|
||||
$subscription_helper = $c->get( 'subscription.helper' );
|
||||
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
|
||||
$last_webhook_storage = $c->get( 'webhook.last-webhook-storage' );
|
||||
assert( $last_webhook_storage instanceof WebhookEventStorage );
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* The services
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Subscription
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
|
||||
return array(
|
||||
'subscription.helper' => static function ( ContainerInterface $container ): SubscriptionHelper {
|
||||
return new SubscriptionHelper( $container->get( 'wcgateway.settings' ) );
|
||||
},
|
||||
'subscription.renewal-handler' => static function ( ContainerInterface $container ): RenewalHandler {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
$repository = $container->get( 'vaulting.repository.payment-token' );
|
||||
$endpoint = $container->get( 'api.endpoint.order' );
|
||||
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
|
||||
$payer_factory = $container->get( 'api.factory.payer' );
|
||||
$environment = $container->get( 'onboarding.environment' );
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
|
||||
return new RenewalHandler(
|
||||
$logger,
|
||||
$repository,
|
||||
$endpoint,
|
||||
$purchase_unit_factory,
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$payer_factory,
|
||||
$environment,
|
||||
$settings,
|
||||
$authorized_payments_processor
|
||||
);
|
||||
},
|
||||
'subscription.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
|
||||
$factory = $container->get( 'api.factory.payment-token' );
|
||||
$endpoint = $container->get( 'api.endpoint.payment-token' );
|
||||
return new PaymentTokenRepository( $factory, $endpoint );
|
||||
},
|
||||
'subscription.api-handler' => static function( ContainerInterface $container ): SubscriptionsApiHandler {
|
||||
return new SubscriptionsApiHandler(
|
||||
$container->get( 'api.endpoint.catalog-products' ),
|
||||
$container->get( 'api.factory.product' ),
|
||||
$container->get( 'api.endpoint.billing-plans' ),
|
||||
$container->get( 'api.factory.billing-cycle' ),
|
||||
$container->get( 'api.factory.payment-preferences' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'subscription.module.url' => static function ( ContainerInterface $container ): string {
|
||||
/**
|
||||
* The path cannot be false.
|
||||
*
|
||||
* @psalm-suppress PossiblyFalseArgument
|
||||
*/
|
||||
return plugins_url(
|
||||
'/modules/ppcp-subscription/',
|
||||
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
'subscription.deactivate-plan-endpoint' => static function ( ContainerInterface $container ): DeactivatePlanEndpoint {
|
||||
return new DeactivatePlanEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.billing-plans' )
|
||||
);
|
||||
},
|
||||
);
|
|
@ -31,7 +31,7 @@ return array(
|
|||
},
|
||||
'vaulting.credit-card-handler' => function( ContainerInterface $container ): VaultedCreditCardHandler {
|
||||
return new VaultedCreditCardHandler(
|
||||
$container->get( 'subscription.helper' ),
|
||||
$container->get( 'wc-subscriptions.helper' ),
|
||||
$container->get( 'vaulting.repository.payment-token' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'api.factory.payer' ),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue