mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
Merge branch 'trunk' into fix/PCP-696-improve-error-message
This commit is contained in:
commit
e762a17bdc
253 changed files with 21563 additions and 4172 deletions
|
@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\AdminNotices;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
|
@ -40,6 +42,34 @@ class AdminNotices implements ModuleInterface {
|
|||
$renderer->render();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
Repository::NOTICES_FILTER,
|
||||
/**
|
||||
* Adds persisted notices to the notices array.
|
||||
*
|
||||
* @param array $notices The notices.
|
||||
* @return array
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $notices ) use ( $c ) {
|
||||
if ( ! is_array( $notices ) ) {
|
||||
return $notices;
|
||||
}
|
||||
|
||||
$admin_notices = $c->get( 'admin-notices.repository' );
|
||||
assert( $admin_notices instanceof Repository );
|
||||
|
||||
$persisted_notices = $admin_notices->get_persisted_and_clear();
|
||||
|
||||
if ( $persisted_notices ) {
|
||||
$notices = array_merge( $notices, $persisted_notices );
|
||||
}
|
||||
|
||||
return $notices;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -92,4 +92,18 @@ class Message {
|
|||
public function wrapper(): string {
|
||||
return $this->wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'type' => $this->type,
|
||||
'message' => $this->message,
|
||||
'dismissable' => $this->dismissable,
|
||||
'wrapper' => $this->wrapper,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
|||
*/
|
||||
class Repository implements RepositoryInterface {
|
||||
|
||||
const NOTICES_FILTER = 'ppcp.admin-notices.current-notices';
|
||||
const NOTICES_FILTER = 'ppcp.admin-notices.current-notices';
|
||||
const PERSISTED_NOTICES_OPTION = 'woocommerce_ppcp-admin-notices';
|
||||
|
||||
/**
|
||||
* Returns the current messages.
|
||||
|
@ -37,4 +38,40 @@ class Repository implements RepositoryInterface {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to persist between page reloads.
|
||||
*
|
||||
* @param Message $message The message.
|
||||
* @return void
|
||||
*/
|
||||
public function persist( Message $message ): void {
|
||||
$persisted_notices = get_option( self::PERSISTED_NOTICES_OPTION ) ?: array();
|
||||
|
||||
$persisted_notices[] = $message->to_array();
|
||||
|
||||
update_option( self::PERSISTED_NOTICES_OPTION, $persisted_notices );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to persist between page reloads.
|
||||
*
|
||||
* @return array|Message[]
|
||||
*/
|
||||
public function get_persisted_and_clear(): array {
|
||||
$notices = array();
|
||||
|
||||
$persisted_data = get_option( self::PERSISTED_NOTICES_OPTION ) ?: array();
|
||||
foreach ( $persisted_data as $notice_data ) {
|
||||
$notices[] = new Message(
|
||||
(string) ( $notice_data['message'] ?? '' ),
|
||||
(string) ( $notice_data['type'] ?? '' ),
|
||||
(bool) ( $notice_data['dismissable'] ?? true ),
|
||||
(string) ( $notice_data['wrapper'] ?? '' )
|
||||
);
|
||||
}
|
||||
|
||||
update_option( self::PERSISTED_NOTICES_OPTION, array(), true );
|
||||
return $notices;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
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;
|
||||
|
||||
|
@ -174,12 +175,15 @@ class OrderEndpoint {
|
|||
/**
|
||||
* Creates an order.
|
||||
*
|
||||
* @param PurchaseUnit[] $items The purchase unit items for the order.
|
||||
* @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 $user_action The user action.
|
||||
* @param PurchaseUnit[] $items The purchase unit items for the order.
|
||||
* @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 $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 create_payment_token( 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;
|
||||
}
|
||||
}
|
145
modules/ppcp-api-client/src/Endpoint/PaymentTokensEndpoint.php
Normal file
145
modules/ppcp-api-client/src/Endpoint/PaymentTokensEndpoint.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?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\Entity\PaymentSource;
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all payment tokens for the given customer.
|
||||
*
|
||||
* @param string $customer_id PayPal customer id.
|
||||
* @return array
|
||||
*
|
||||
* @throws RuntimeException When something went wrong with the request.
|
||||
* @throws PayPalApiException When something went wrong getting the payment tokens.
|
||||
*/
|
||||
public function payment_tokens_for_customer( string $customer_id ): array {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v3/vault/payment-tokens?customer_id=' . $customer_id;
|
||||
$args = array(
|
||||
'method' => 'GET',
|
||||
'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 ( 200 !== $status_code ) {
|
||||
throw new PayPalApiException( $json, $status_code );
|
||||
}
|
||||
|
||||
$tokens = array();
|
||||
$payment_tokens = $json->payment_tokens ?? array();
|
||||
foreach ( $payment_tokens as $payment_token ) {
|
||||
$name = array_key_first( (array) $payment_token->payment_source ) ?? '';
|
||||
if ( $name ) {
|
||||
$tokens[] = array(
|
||||
'id' => $payment_token->id,
|
||||
'payment_source' => new PaymentSource(
|
||||
$name,
|
||||
$payment_token->payment_source->$name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
}
|
|
@ -193,6 +193,53 @@ class PaymentsEndpoint {
|
|||
return $this->capture_factory->from_paypal_response( $json );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reauthorizes an order.
|
||||
*
|
||||
* @param string $authorization_id The id.
|
||||
* @param Money|null $amount The amount to capture. If not specified, the whole authorized amount is captured.
|
||||
*
|
||||
* @return string
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function reauthorize( string $authorization_id, ?Money $amount = null ) : string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v2/payments/authorizations/' . $authorization_id . '/reauthorize';
|
||||
|
||||
$data = array();
|
||||
if ( $amount ) {
|
||||
$data['amount'] = $amount->to_array();
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
'Prefer' => 'return=representation',
|
||||
),
|
||||
'body' => wp_json_encode( $data, JSON_FORCE_OBJECT ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
$json = json_decode( $response['body'] );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new RuntimeException( 'Could not reauthorize authorized payment.' );
|
||||
}
|
||||
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 201 !== $status_code || ! is_object( $json ) ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
|
||||
return $json->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds a payment.
|
||||
*
|
||||
|
|
|
@ -28,19 +28,29 @@ class Authorization {
|
|||
*/
|
||||
private $authorization_status;
|
||||
|
||||
/**
|
||||
* The fraud processor response (AVS, CVV ...).
|
||||
*
|
||||
* @var FraudProcessorResponse|null
|
||||
*/
|
||||
protected $fraud_processor_response;
|
||||
|
||||
/**
|
||||
* Authorization constructor.
|
||||
*
|
||||
* @param string $id The id.
|
||||
* @param AuthorizationStatus $authorization_status The status.
|
||||
* @param string $id The id.
|
||||
* @param AuthorizationStatus $authorization_status The status.
|
||||
* @param FraudProcessorResponse|null $fraud_processor_response The fraud processor response (AVS, CVV ...).
|
||||
*/
|
||||
public function __construct(
|
||||
string $id,
|
||||
AuthorizationStatus $authorization_status
|
||||
AuthorizationStatus $authorization_status,
|
||||
?FraudProcessorResponse $fraud_processor_response
|
||||
) {
|
||||
|
||||
$this->id = $id;
|
||||
$this->authorization_status = $authorization_status;
|
||||
$this->id = $id;
|
||||
$this->authorization_status = $authorization_status;
|
||||
$this->fraud_processor_response = $fraud_processor_response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,15 +81,30 @@ class Authorization {
|
|||
$this->authorization_status->is( AuthorizationStatus::PENDING );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fraud processor response (AVS, CVV ...).
|
||||
*
|
||||
* @return FraudProcessorResponse|null
|
||||
*/
|
||||
public function fraud_processor_response() : ?FraudProcessorResponse {
|
||||
return $this->fraud_processor_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
$data = array(
|
||||
'id' => $this->id,
|
||||
'status' => $this->authorization_status->name(),
|
||||
);
|
||||
|
||||
if ( $this->fraud_processor_response ) {
|
||||
$data['fraud_processor_response'] = $this->fraud_processor_response->to_array();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -15,14 +15,15 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|||
* Class OrderStatus
|
||||
*/
|
||||
class OrderStatus {
|
||||
const INTERNAL = 'INTERNAL';
|
||||
const CREATED = 'CREATED';
|
||||
const SAVED = 'SAVED';
|
||||
const APPROVED = 'APPROVED';
|
||||
const VOIDED = 'VOIDED';
|
||||
const COMPLETED = 'COMPLETED';
|
||||
const PENDING_APPROVAL = 'PENDING_APPROVAL';
|
||||
const VALID_STATUS = array(
|
||||
const INTERNAL = 'INTERNAL';
|
||||
const CREATED = 'CREATED';
|
||||
const SAVED = 'SAVED';
|
||||
const APPROVED = 'APPROVED';
|
||||
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,
|
||||
self::SAVED,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -21,19 +21,37 @@ class SellerStatus {
|
|||
*/
|
||||
private $products;
|
||||
|
||||
/**
|
||||
* The capabilities.
|
||||
*
|
||||
* @var SellerStatusCapability[]
|
||||
*/
|
||||
private $capabilities;
|
||||
|
||||
/**
|
||||
* SellerStatus constructor.
|
||||
*
|
||||
* @param SellerStatusProduct[] $products The products.
|
||||
* @param SellerStatusProduct[] $products The products.
|
||||
* @param SellerStatusCapability[] $capabilities The capabilities.
|
||||
*
|
||||
* @psalm-suppress RedundantConditionGivenDocblockType
|
||||
*/
|
||||
public function __construct( array $products ) {
|
||||
public function __construct( array $products, array $capabilities ) {
|
||||
foreach ( $products as $key => $product ) {
|
||||
if ( is_a( $product, SellerStatusProduct::class ) ) {
|
||||
continue;
|
||||
}
|
||||
unset( $products[ $key ] );
|
||||
}
|
||||
$this->products = $products;
|
||||
foreach ( $capabilities as $key => $capability ) {
|
||||
if ( is_a( $capability, SellerStatusCapability::class ) ) {
|
||||
continue;
|
||||
}
|
||||
unset( $capabilities[ $key ] );
|
||||
}
|
||||
|
||||
$this->products = $products;
|
||||
$this->capabilities = $capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,6 +63,15 @@ class SellerStatus {
|
|||
return $this->products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capabilities.
|
||||
*
|
||||
* @return SellerStatusCapability[]
|
||||
*/
|
||||
public function capabilities() : array {
|
||||
return $this->capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enitity as array.
|
||||
*
|
||||
|
@ -58,8 +85,16 @@ class SellerStatus {
|
|||
$this->products()
|
||||
);
|
||||
|
||||
$capabilities = array_map(
|
||||
function( SellerStatusCapability $capability ) : array {
|
||||
return $capability->to_array();
|
||||
},
|
||||
$this->capabilities()
|
||||
);
|
||||
|
||||
return array(
|
||||
'products' => $products,
|
||||
'products' => $products,
|
||||
'capabilities' => $capabilities,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* The capabilities of a seller status.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
|
||||
*/
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* Class SellerStatusCapability
|
||||
*/
|
||||
class SellerStatusCapability {
|
||||
|
||||
const STATUS_ACTIVE = 'ACTIVE';
|
||||
|
||||
/**
|
||||
* The name of the product.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* The status of the capability.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* SellerStatusCapability constructor.
|
||||
*
|
||||
* @param string $name The name of the product.
|
||||
* @param string $status The status of the capability.
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $status
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the product.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name() : string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status for this capability.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function status() : string {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function to_array() : array {
|
||||
return array(
|
||||
'name' => $this->name(),
|
||||
'status' => $this->status(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -19,6 +19,22 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
|||
*/
|
||||
class AuthorizationFactory {
|
||||
|
||||
/**
|
||||
* The FraudProcessorResponseFactory factory.
|
||||
*
|
||||
* @var FraudProcessorResponseFactory
|
||||
*/
|
||||
protected $fraud_processor_response_factory;
|
||||
|
||||
/**
|
||||
* AuthorizationFactory constructor.
|
||||
*
|
||||
* @param FraudProcessorResponseFactory $fraud_processor_response_factory The FraudProcessorResponseFactory factory.
|
||||
*/
|
||||
public function __construct( FraudProcessorResponseFactory $fraud_processor_response_factory ) {
|
||||
$this->fraud_processor_response_factory = $fraud_processor_response_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Authorization based off a PayPal response.
|
||||
*
|
||||
|
@ -42,12 +58,17 @@ class AuthorizationFactory {
|
|||
|
||||
$reason = $data->status_details->reason ?? null;
|
||||
|
||||
$fraud_processor_response = isset( $data->processor_response ) ?
|
||||
$this->fraud_processor_response_factory->from_paypal_response( $data->processor_response )
|
||||
: null;
|
||||
|
||||
return new Authorization(
|
||||
$data->id,
|
||||
new AuthorizationStatus(
|
||||
$data->status,
|
||||
$reason ? new AuthorizationStatusDetails( $reason ) : null
|
||||
)
|
||||
),
|
||||
$fraud_processor_response
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ?? ''
|
||||
);
|
||||
}
|
||||
}
|
|
@ -25,8 +25,8 @@ class FraudProcessorResponseFactory {
|
|||
* @return FraudProcessorResponse
|
||||
*/
|
||||
public function from_paypal_response( stdClass $data ): FraudProcessorResponse {
|
||||
$avs_code = $data->avs_code ?: null;
|
||||
$cvv_code = $data->cvv_code ?: null;
|
||||
$avs_code = ( $data->avs_code ?? null ) ?: null;
|
||||
$cvv_code = ( $data->cvv_code ?? null ) ?: null;
|
||||
|
||||
return new FraudProcessorResponse( $avs_code, $cvv_code );
|
||||
}
|
||||
|
|
|
@ -61,12 +61,12 @@ class ItemFactory {
|
|||
|
||||
$price = (float) $item['line_subtotal'] / (float) $item['quantity'];
|
||||
return new Item(
|
||||
mb_substr( $product->get_name(), 0, 127 ),
|
||||
$this->prepare_item_string( $product->get_name() ),
|
||||
new Money( $price, $this->currency ),
|
||||
$quantity,
|
||||
$this->prepare_description( $product->get_description() ),
|
||||
$this->prepare_item_string( $product->get_description() ),
|
||||
null,
|
||||
$product->get_sku(),
|
||||
$this->prepare_sku( $product->get_sku() ),
|
||||
( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS,
|
||||
$product->get_permalink(),
|
||||
$image[0] ?? '',
|
||||
|
@ -138,12 +138,12 @@ class ItemFactory {
|
|||
$image = $product instanceof WC_Product ? wp_get_attachment_image_src( (int) $product->get_image_id(), 'full' ) : '';
|
||||
|
||||
return new Item(
|
||||
mb_substr( $item->get_name(), 0, 127 ),
|
||||
$this->prepare_item_string( $item->get_name() ),
|
||||
new Money( $price_without_tax_rounded, $currency ),
|
||||
$quantity,
|
||||
$product instanceof WC_Product ? $this->prepare_description( $product->get_description() ) : '',
|
||||
$product instanceof WC_Product ? $this->prepare_item_string( $product->get_description() ) : '',
|
||||
null,
|
||||
$product instanceof WC_Product ? $product->get_sku() : '',
|
||||
$product instanceof WC_Product ? $this->prepare_sku( $product->get_sku() ) : '',
|
||||
( $product instanceof WC_Product && $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS,
|
||||
$product instanceof WC_Product ? $product->get_permalink() : '',
|
||||
$image[0] ?? ''
|
||||
|
@ -160,7 +160,7 @@ class ItemFactory {
|
|||
*/
|
||||
private function from_wc_order_fee( \WC_Order_Item_Fee $item, \WC_Order $order ): Item {
|
||||
return new Item(
|
||||
$item->get_name(),
|
||||
$this->prepare_item_string( $item->get_name() ),
|
||||
new Money( (float) $item->get_amount(), $order->get_currency() ),
|
||||
$item->get_quantity(),
|
||||
'',
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
|||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusProduct;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusCapability;
|
||||
|
||||
/**
|
||||
* Class SellerStatusFactory
|
||||
|
@ -37,6 +38,17 @@ class SellerStatusFactory {
|
|||
isset( $json->products ) ? (array) $json->products : array()
|
||||
);
|
||||
|
||||
return new SellerStatus( $products );
|
||||
$capabilities = array_map(
|
||||
function( $json ) : SellerStatusCapability {
|
||||
$capability = new SellerStatusCapability(
|
||||
isset( $json->name ) ? (string) $json->name : '',
|
||||
isset( $json->status ) ? (string) $json->status : ''
|
||||
);
|
||||
return $capability;
|
||||
},
|
||||
isset( $json->capabilities ) ? (array) $json->capabilities : array()
|
||||
);
|
||||
|
||||
return new SellerStatus( $products, $capabilities );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,23 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
|
|||
trait ItemTrait {
|
||||
|
||||
/**
|
||||
* Cleanups the description and prepares it for sending to PayPal.
|
||||
* Cleans up item strings (title and description for example) and prepares them for sending to PayPal.
|
||||
*
|
||||
* @param string $description Item description.
|
||||
* @param string $string Item string.
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare_description( string $description ): string {
|
||||
$description = strip_shortcodes( wp_strip_all_tags( $description ) );
|
||||
return substr( $description, 0, 127 ) ?: '';
|
||||
protected function prepare_item_string( string $string ): string {
|
||||
$string = strip_shortcodes( wp_strip_all_tags( $string ) );
|
||||
return substr( $string, 0, 127 ) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the sku for sending to PayPal.
|
||||
*
|
||||
* @param string $sku Item sku.
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare_sku( string $sku ): string {
|
||||
return substr( wp_strip_all_tags( $sku ), 0, 127 ) ?: '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class ApplicationContextRepository {
|
|||
$payment_preference = $this->settings->has( 'payee_preferred' ) && $this->settings->get( 'payee_preferred' ) ?
|
||||
ApplicationContext::PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED : ApplicationContext::PAYMENT_METHOD_UNRESTRICTED;
|
||||
$context = new ApplicationContext(
|
||||
network_home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
|
||||
home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
|
||||
(string) wc_get_checkout_url(),
|
||||
(string) $brand_name,
|
||||
$locale,
|
||||
|
|
|
@ -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' );
|
||||
}
|
||||
|
||||
|
@ -69,7 +71,7 @@ return array(
|
|||
// Connection tab fields.
|
||||
$fields = $insert_after(
|
||||
$fields,
|
||||
'ppcp_dcc_status',
|
||||
'ppcp_reference_transactions_status',
|
||||
array(
|
||||
'applepay_status' => array(
|
||||
'title' => __( 'Apple Pay Payments', '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,58 +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;
|
||||
--apple-pay-button-border-radius: 4px;
|
||||
--apple-pay-button-height: 45px;
|
||||
&.ppcp-button-pill {
|
||||
--apple-pay-button-border-radius: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wp-block-woocommerce-checkout, .wp-block-woocommerce-cart {
|
||||
.ppcp-button-applepay {
|
||||
--apple-pay-button-margin: 0;
|
||||
|
||||
.ppcp-has-applepay-block {
|
||||
|
||||
.wp-block-woocommerce-checkout {
|
||||
#applepay-container, .ppcp-button-applepay {
|
||||
margin: 0;
|
||||
--apple-pay-button-margin: 0;
|
||||
--apple-pay-button-height: 48px;
|
||||
&.ppcp-button-pill {
|
||||
--apple-pay-button-border-radius: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart {
|
||||
#applepay-container, .ppcp-button-applepay {
|
||||
margin: 0;
|
||||
--apple-pay-button-margin: 0;
|
||||
--apple-pay-button-height: 48px;
|
||||
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;
|
||||
|
@ -31,6 +34,9 @@ class ApplepayButton {
|
|||
// Stores initialization data sent to the button.
|
||||
this.initialPaymentRequest = null;
|
||||
|
||||
// Default eligibility status.
|
||||
this.isEligible = true;
|
||||
|
||||
this.log = function() {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
console.log('[ApplePayButton]', ...arguments);
|
||||
|
@ -60,14 +66,10 @@ class ApplepayButton {
|
|||
this.initEventHandlers();
|
||||
this.isInitialized = true;
|
||||
this.applePayConfig = config;
|
||||
const isEligible = this.applePayConfig.isEligible;
|
||||
this.isEligible = (this.applePayConfig.isEligible && window.ApplePaySession) || this.buttonConfig.is_admin;
|
||||
|
||||
if (isEligible) {
|
||||
if (this.isEligible) {
|
||||
this.fetchTransactionInfo().then(() => {
|
||||
const isSubscriptionProduct = this.ppcpConfig?.data_client_id?.has_subscriptions === true;
|
||||
if (isSubscriptionProduct) {
|
||||
return;
|
||||
}
|
||||
this.addButton();
|
||||
const id_minicart = "#apple-" + this.buttonConfig.button.mini_cart_wrapper;
|
||||
const id = "#apple-" + this.buttonConfig.button.wrapper;
|
||||
|
@ -84,6 +86,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,6 +136,10 @@ class ApplepayButton {
|
|||
const wrapper_id = '#' + wrapper;
|
||||
|
||||
const syncButtonVisibility = () => {
|
||||
if (!this.isEligible) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $ppcpButtonWrapper = jQuery(ppcpButtonWrapper);
|
||||
setVisible(wrapper_id, $ppcpButtonWrapper.is(':visible'));
|
||||
setEnabled(wrapper_id, !$ppcpButtonWrapper.hasClass('ppcp-disabled'));
|
||||
|
@ -179,13 +189,13 @@ class ApplepayButton {
|
|||
appleContainer.innerHTML = `<apple-pay-button id="${id}" buttonstyle="${color}" type="${type}" locale="${language}">`;
|
||||
}
|
||||
|
||||
jQuery('#' + wrapper).addClass('ppcp-button-' + ppcpStyle.shape);
|
||||
const $wrapper = jQuery('#' + wrapper);
|
||||
$wrapper.addClass('ppcp-button-' + ppcpStyle.shape);
|
||||
|
||||
if (ppcpStyle.height) {
|
||||
jQuery('#' + wrapper).css('--apple-pay-button-height', `${ppcpStyle.height}px`)
|
||||
$wrapper.css('--apple-pay-button-height', `${ppcpStyle.height}px`)
|
||||
$wrapper.css('height', `${ppcpStyle.height}px`)
|
||||
}
|
||||
|
||||
jQuery(wrapper).append(appleContainer);
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -200,6 +210,8 @@ class ApplepayButton {
|
|||
|
||||
const paymentRequest = this.paymentRequest();
|
||||
|
||||
window.ppcpFundingSource = 'apple_pay'; // Do this on another place like on create order endpoint handler.
|
||||
|
||||
// Trigger woocommerce validation if we are in the checkout page.
|
||||
if (this.context === 'checkout') {
|
||||
const checkoutFormSelector = 'form.woocommerce-checkout';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ErrorHandler from "../../../../ppcp-button/resources/js/modules/ErrorHandler";
|
||||
import CartActionHandler
|
||||
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler";
|
||||
import {isPayPalSubscription} from "../../../../ppcp-blocks/resources/js/Helper/Subscription";
|
||||
|
||||
class BaseHandler {
|
||||
|
||||
|
@ -9,9 +10,15 @@ class BaseHandler {
|
|||
this.ppcpConfig = ppcpConfig;
|
||||
}
|
||||
|
||||
isVaultV3Mode() {
|
||||
return this.ppcpConfig?.save_payment_methods?.id_token // vault v3
|
||||
&& ! this.ppcpConfig?.data_client_id?.paypal_subscriptions_enabled // not PayPal Subscriptions mode
|
||||
&& this.ppcpConfig?.can_save_vault_token; // vault is enabled
|
||||
}
|
||||
|
||||
validateContext() {
|
||||
if ( this.ppcpConfig?.locations_with_subscription_product?.cart ) {
|
||||
return false;
|
||||
return this.isVaultV3Mode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ class PayNowHandler extends BaseHandler {
|
|||
|
||||
validateContext() {
|
||||
if ( this.ppcpConfig?.locations_with_subscription_product?.payorder ) {
|
||||
return false;
|
||||
return this.isVaultV3Mode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class SingleProductHandler extends BaseHandler {
|
|||
|
||||
validateContext() {
|
||||
if ( this.ppcpConfig?.locations_with_subscription_product?.product ) {
|
||||
return false;
|
||||
return this.isVaultV3Mode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import {useEffect, useState} from '@wordpress/element';
|
||||
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {registerExpressPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
|
||||
import {cartHasSubscriptionProducts} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
|
||||
import ApplepayManager from "./ApplepayManager";
|
||||
import {loadCustomScript} from "@paypal/paypal-js";
|
||||
import CheckoutHandler from "./Context/CheckoutHandler";
|
||||
|
||||
const ppcpData = wc.wcSettings.getSetting('ppcp-gateway_data');
|
||||
const ppcpConfig = ppcpData.scriptData;
|
||||
|
@ -23,12 +25,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,12 +46,19 @@ 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'];
|
||||
|
||||
if (
|
||||
cartHasSubscriptionProducts(ppcpConfig)
|
||||
&& (new CheckoutHandler(buttonConfig, ppcpConfig)).isVaultV3Mode()
|
||||
) {
|
||||
features.push('subscriptions');
|
||||
}
|
||||
|
||||
registerExpressPaymentMethod({
|
||||
name: buttonData.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: buttonData.title}}/>,
|
||||
|
|
|
@ -21,6 +21,15 @@ import ApplepayManager from "./ApplepayManager";
|
|||
}
|
||||
});
|
||||
|
||||
// Use set timeout as it's unnecessary to refresh upon Minicart initial render.
|
||||
setTimeout(() => {
|
||||
jQuery(document.body).on('wc_fragments_loaded wc_fragments_refreshed', () => {
|
||||
if (manager) {
|
||||
manager.reinit();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
|
|
|
@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\Applepay\Assets\BlocksPaymentMethod;
|
|||
use WooCommerce\PayPalCommerce\Applepay\Assets\PropertiesDictionary;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Helper\ApmApplies;
|
||||
use WooCommerce\PayPalCommerce\Applepay\Helper\AvailabilityNotice;
|
||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
@ -62,19 +63,26 @@ 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;
|
||||
},
|
||||
|
||||
'applepay.apple-product-status' => static function( ContainerInterface $container ): AppleProductStatus {
|
||||
return new AppleProductStatus(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'api.endpoint.partners' ),
|
||||
$container->get( 'onboarding.state' ),
|
||||
$container->get( 'api.helper.failure-registry' )
|
||||
);
|
||||
},
|
||||
'applepay.apple-product-status' => SingletonDecorator::make(
|
||||
static function( ContainerInterface $container ): AppleProductStatus {
|
||||
return new AppleProductStatus(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'api.endpoint.partners' ),
|
||||
$container->get( 'onboarding.state' ),
|
||||
$container->get( 'api.helper.failure-registry' )
|
||||
);
|
||||
}
|
||||
),
|
||||
'applepay.available' => static function ( ContainerInterface $container ): bool {
|
||||
if ( apply_filters( 'woocommerce_paypal_payments_applepay_validate_product_status', true ) ) {
|
||||
$status = $container->get( 'applepay.apple-product-status' );
|
||||
|
@ -166,8 +174,9 @@ return array(
|
|||
return apply_filters(
|
||||
'woocommerce_paypal_payments_applepay_supported_country_currency_matrix',
|
||||
array(
|
||||
'CA' => array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -176,16 +185,22 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -194,16 +209,334 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -212,12 +545,377 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Applepay\Assets;
|
|||
|
||||
use Throwable;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusCapability;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
@ -100,6 +101,11 @@ class AppleProductStatus {
|
|||
return false;
|
||||
}
|
||||
|
||||
$status_override = apply_filters( 'woocommerce_paypal_payments_apple_pay_product_status', null );
|
||||
if ( null !== $status_override ) {
|
||||
return $status_override;
|
||||
}
|
||||
|
||||
// If status was already checked on this request return the same result.
|
||||
if ( null !== $this->current_status ) {
|
||||
return $this->current_status;
|
||||
|
@ -128,21 +134,32 @@ class AppleProductStatus {
|
|||
}
|
||||
|
||||
// Check the seller status for the intended capability.
|
||||
$has_capability = false;
|
||||
foreach ( $seller_status->products() as $product ) {
|
||||
if ( $product->name() !== 'PAYMENT_METHODS' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( in_array( self::CAPABILITY_NAME, $product->capabilities(), true ) ) {
|
||||
// Capability found, persist status and return true.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||
$this->settings->persist();
|
||||
|
||||
$this->current_status = true;
|
||||
return $this->current_status;
|
||||
$has_capability = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $seller_status->capabilities() as $capability ) {
|
||||
if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) {
|
||||
$has_capability = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_capability ) {
|
||||
// Capability found, persist status and return true.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||
$this->settings->persist();
|
||||
|
||||
$this->current_status = true;
|
||||
return $this->current_status;
|
||||
}
|
||||
|
||||
// Capability not found, persist status and return false.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED );
|
||||
$this->settings->persist();
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -273,7 +273,7 @@ class AvailabilityNotice {
|
|||
$message = sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag for the well-known file, %3$s and %4$s are the opening and closing of HTML <a> tag for the help document.
|
||||
__(
|
||||
'Apple Pay Validation Error. Please ensure the presentment of the correct %1$sdomain association file%2$s for Apple to validate your domain. %3$sLearn more%4$s about the Apple Pay requirements',
|
||||
'Apple Pay has not yet been validated. Use the Apple Pay button in your shop for Apple to validate your domain. If this message persists after using the button, please verify your website displays the correct %1$sdomain association file%2$s. More details about the Apple Pay setup can be found in the %3$sPayPal Payments documentation%4$s.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a href="/.well-known/apple-developer-merchantid-domain-association" target="_blank">',
|
||||
|
|
|
@ -17,8 +17,8 @@ return array(
|
|||
return array_merge(
|
||||
$locations,
|
||||
array(
|
||||
'checkout-block-express' => _x( 'Block Express Checkout', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
'cart-block' => _x( 'Block Cart', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
'checkout-block-express' => _x( 'Express Checkout', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
'cart-block' => _x( 'Cart', 'Name of Buttons Location', 'woocommerce-paypal-payments' ),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ return array(
|
|||
'title' => __( 'Require final confirmation on checkout', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __(
|
||||
'Require customers to confirm express payments from the Block Cart and Block Express Checkout on the checkout page.
|
||||
'Require customers to confirm express payments from the Cart and Express Checkout on the checkout page.
|
||||
<p class="description">If this setting is not enabled, <a href="https://woocommerce.com/document/woocommerce-paypal-payments/#blocks-faq" target="_blank">payment confirmation on the checkout will be skipped</a>.
|
||||
Skipping the final confirmation on the checkout page may impact the buyer experience during the PayPal payment process.</p>',
|
||||
'woocommerce-paypal-payments'
|
||||
|
|
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import MessagesBootstrap from "../../../../ppcp-button/resources/js/modules/ContextBootstrap/MessagesBootstap";
|
||||
import {debounce} from "../Helper/debounce";
|
||||
|
||||
class BlockCheckoutMessagesBootstrap {
|
||||
constructor(scriptData) {
|
||||
this.messagesBootstrap = new MessagesBootstrap(scriptData, null);
|
||||
this.lastCartTotal = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.messagesBootstrap.init();
|
||||
|
||||
this._updateCartTotal();
|
||||
|
||||
if (wp.data?.subscribe) {
|
||||
wp.data.subscribe(debounce(() => {
|
||||
this._updateCartTotal();
|
||||
}, 300));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_getCartTotal() {
|
||||
if (!wp.data.select) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cart = wp.data.select('wc/store/cart')
|
||||
if (!cart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totals = cart.getCartTotals();
|
||||
return parseInt(totals.total_price, 10) / 10 ** totals.currency_minor_unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateCartTotal() {
|
||||
const currentTotal = this._getCartTotal();
|
||||
if (currentTotal === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTotal !== this.lastCartTotal) {
|
||||
this.lastCartTotal = currentTotal;
|
||||
jQuery(document.body).trigger('ppcp_block_cart_total_updated', [currentTotal]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default BlockCheckoutMessagesBootstrap;
|
|
@ -63,7 +63,7 @@ export const paypalAddressToWc = (address) => {
|
|||
* @returns {Object}
|
||||
*/
|
||||
export const paypalShippingToWc = (shipping) => {
|
||||
const [firstName, lastName] = splitFullName(shipping.name.full_name);
|
||||
const [firstName, lastName] = (shipping.name ? splitFullName(shipping.name.full_name) : ['','']);
|
||||
return {
|
||||
...paypalAddressToWc(shipping.address),
|
||||
first_name: firstName,
|
||||
|
@ -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,
|
||||
|
@ -87,6 +87,22 @@ export const paypalPayerToWc = (payer) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} subscriber
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const paypalSubscriberToWc = (subscriber) => {
|
||||
const firstName = subscriber?.name?.given_name ?? '';
|
||||
const lastName = subscriber?.name?.surname ?? '';
|
||||
const address = subscriber.address ? paypalAddressToWc(subscriber.shipping_address.address) : {};
|
||||
return {
|
||||
...address,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email: subscriber.email_address,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} order
|
||||
* @returns {Object}
|
||||
|
@ -100,10 +116,12 @@ export const paypalOrderToWcShippingAddress = (order) => {
|
|||
const res = paypalShippingToWc(shipping);
|
||||
|
||||
// use the name from billing if the same, to avoid possible mistakes when splitting full_name
|
||||
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;
|
||||
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,15 +134,29 @@ export const paypalOrderToWcShippingAddress = (order) => {
|
|||
*/
|
||||
export const paypalOrderToWcAddresses = (order) => {
|
||||
const shippingAddress = paypalOrderToWcShippingAddress(order);
|
||||
let 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)};
|
||||
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};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param subscription
|
||||
* @returns {{shippingAddress: Object, billingAddress: Object}}
|
||||
*/
|
||||
export const paypalSubscriptionToWcAddresses = (subscription) => {
|
||||
const shippingAddress = paypalSubscriberToWc(subscription.subscriber);
|
||||
let billingAddress = shippingAddress;
|
||||
return {billingAddress, shippingAddress};
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two WC addresses.
|
||||
* The objects can contain either the WC form fields or billingAddress, shippingAddress objects.
|
||||
|
|
22
modules/ppcp-blocks/resources/js/Helper/Helper.js
Normal file
22
modules/ppcp-blocks/resources/js/Helper/Helper.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @param str
|
||||
* @returns {string}
|
||||
*/
|
||||
export const toSnakeCase = (str) => {
|
||||
return str.replace(/[\w]([A-Z])/g, function(m) {
|
||||
return m[0] + "_" + m[1];
|
||||
}).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param obj
|
||||
* @returns {{}}
|
||||
*/
|
||||
export const convertKeysToSnakeCase = (obj) => {
|
||||
const newObj = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const newKey = toSnakeCase(key);
|
||||
newObj[newKey] = obj[key];
|
||||
});
|
||||
return newObj;
|
||||
}
|
16
modules/ppcp-blocks/resources/js/Helper/Subscription.js
Normal file
16
modules/ppcp-blocks/resources/js/Helper/Subscription.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @param {Object} scriptData
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const isPayPalSubscription = (scriptData) => {
|
||||
return scriptData.data_client_id.has_subscriptions
|
||||
&& scriptData.data_client_id.paypal_subscriptions_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} scriptData
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const cartHasSubscriptionProducts = (scriptData) => {
|
||||
return !! scriptData?.locations_with_subscription_product?.cart;
|
||||
}
|
9
modules/ppcp-blocks/resources/js/Helper/debounce.js
Normal file
9
modules/ppcp-blocks/resources/js/Helper/debounce.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
export const debounce = (callback, delayMs) => {
|
||||
let timeoutId = null;
|
||||
return (...args) => {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = window.setTimeout(() => {
|
||||
callback.apply(null, args);
|
||||
}, delayMs);
|
||||
};
|
||||
};
|
|
@ -1,6 +1,18 @@
|
|||
import {useEffect, useState} from '@wordpress/element';
|
||||
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
|
||||
import {mergeWcAddress, paypalAddressToWc, paypalOrderToWcAddresses} from "./Helper/Address";
|
||||
import {
|
||||
mergeWcAddress,
|
||||
paypalAddressToWc,
|
||||
paypalOrderToWcAddresses,
|
||||
paypalSubscriptionToWcAddresses
|
||||
} from "./Helper/Address";
|
||||
import {
|
||||
convertKeysToSnakeCase
|
||||
} from "./Helper/Helper";
|
||||
import {
|
||||
cartHasSubscriptionProducts,
|
||||
isPayPalSubscription
|
||||
} from "./Helper/Subscription";
|
||||
import {
|
||||
loadPaypalScriptPromise
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
|
||||
|
@ -8,13 +20,16 @@ import {
|
|||
normalizeStyleForFundingSource
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/Style'
|
||||
import buttonModuleWatcher from "../../../ppcp-button/resources/js/modules/ButtonModuleWatcher";
|
||||
|
||||
import BlockCheckoutMessagesBootstrap from "./Bootstrap/BlockCheckoutMessagesBootstrap";
|
||||
import {keysToCamelCase} from "../../../ppcp-button/resources/js/modules/Helper/Utils";
|
||||
const config = wc.wcSettings.getSetting('ppcp-gateway_data');
|
||||
|
||||
window.ppcpFundingSource = config.fundingSource;
|
||||
|
||||
let registeredContext = false;
|
||||
|
||||
let paypalScriptPromise = null;
|
||||
|
||||
const PayPalComponent = ({
|
||||
onClick,
|
||||
onClose,
|
||||
|
@ -31,6 +46,17 @@ const PayPalComponent = ({
|
|||
const {responseTypes} = emitResponse;
|
||||
|
||||
const [paypalOrder, setPaypalOrder] = useState(null);
|
||||
const [gotoContinuationOnError, setGotoContinuationOnError] = useState(false);
|
||||
|
||||
const [paypalScriptLoaded, setPaypalScriptLoaded] = useState(false);
|
||||
|
||||
if (!paypalScriptLoaded) {
|
||||
if (!paypalScriptPromise) {
|
||||
// for editor, since canMakePayment was not called
|
||||
paypalScriptPromise = loadPaypalScriptPromise(config.scriptData)
|
||||
}
|
||||
paypalScriptPromise.then(() => setPaypalScriptLoaded(true));
|
||||
}
|
||||
|
||||
const methodId = fundingSource ? `${config.id}-${fundingSource}` : config.id;
|
||||
|
||||
|
@ -39,12 +65,17 @@ const PayPalComponent = ({
|
|||
if (!config.scriptData.continuation || !config.scriptData.continuation.order || window.ppcpContinuationFilled) {
|
||||
return;
|
||||
}
|
||||
const paypalAddresses = paypalOrderToWcAddresses(config.scriptData.continuation.order);
|
||||
const wcAddresses = wp.data.select('wc/store/cart').getCustomerData();
|
||||
const addresses = mergeWcAddress(wcAddresses, paypalAddresses);
|
||||
wp.data.dispatch('wc/store/cart').setBillingAddress(addresses.billingAddress);
|
||||
if (shippingData.needsShipping) {
|
||||
wp.data.dispatch('wc/store/cart').setShippingAddress(addresses.shippingAddress);
|
||||
try {
|
||||
const paypalAddresses = paypalOrderToWcAddresses(config.scriptData.continuation.order);
|
||||
const wcAddresses = wp.data.select('wc/store/cart').getCustomerData();
|
||||
const addresses = mergeWcAddress(wcAddresses, paypalAddresses);
|
||||
wp.data.dispatch('wc/store/cart').setBillingAddress(addresses.billingAddress);
|
||||
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;
|
||||
|
@ -60,6 +91,7 @@ const PayPalComponent = ({
|
|||
bn_code: '',
|
||||
context: config.scriptData.context,
|
||||
payment_method: 'ppcp-gateway',
|
||||
funding_source: window.ppcpFundingSource ?? 'paypal',
|
||||
createaccount: false
|
||||
}),
|
||||
});
|
||||
|
@ -87,6 +119,83 @@ const PayPalComponent = ({
|
|||
}
|
||||
};
|
||||
|
||||
const createSubscription = async (data, actions) => {
|
||||
let planId = config.scriptData.subscription_plan_id;
|
||||
if (config.scriptData.variable_paypal_subscription_variation_from_cart !== '') {
|
||||
planId = config.scriptData.variable_paypal_subscription_variation_from_cart;
|
||||
}
|
||||
|
||||
return actions.subscription.create({
|
||||
'plan_id': planId
|
||||
});
|
||||
};
|
||||
|
||||
const handleApproveSubscription = async (data, actions) => {
|
||||
try {
|
||||
const subscription = await actions.subscription.get();
|
||||
|
||||
if (subscription) {
|
||||
const addresses = paypalSubscriptionToWcAddresses(subscription);
|
||||
|
||||
let promises = [
|
||||
// save address on server
|
||||
wp.data.dispatch('wc/store/cart').updateCustomerData({
|
||||
billing_address: addresses.billingAddress,
|
||||
shipping_address: addresses.shippingAddress,
|
||||
}),
|
||||
];
|
||||
if (!config.finalReviewEnabled) {
|
||||
// set address in UI
|
||||
promises.push(wp.data.dispatch('wc/store/cart').setBillingAddress(addresses.billingAddress));
|
||||
if (shippingData.needsShipping) {
|
||||
promises.push(wp.data.dispatch('wc/store/cart').setShippingAddress(addresses.shippingAddress))
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
setPaypalOrder(subscription);
|
||||
|
||||
const res = await fetch(config.scriptData.ajax.approve_subscription.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.scriptData.ajax.approve_subscription.nonce,
|
||||
order_id: data.orderID,
|
||||
subscription_id: data.subscriptionID
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
if (typeof actions !== 'undefined' && typeof actions.restart !== 'undefined') {
|
||||
return actions.restart();
|
||||
}
|
||||
if (json.data?.message) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
|
||||
throw new Error(config.scriptData.labels.error.generic)
|
||||
}
|
||||
|
||||
if (config.finalReviewEnabled) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError(true);
|
||||
onSubmit();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
onError(err.message);
|
||||
|
||||
onClose();
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getCheckoutRedirectUrl = () => {
|
||||
const checkoutUrl = new URL(config.scriptData.redirect);
|
||||
// sometimes some browsers may load some kind of cached version of the page,
|
||||
|
@ -147,6 +256,7 @@ const PayPalComponent = ({
|
|||
if (config.finalReviewEnabled) {
|
||||
location.href = getCheckoutRedirectUrl();
|
||||
} else {
|
||||
setGotoContinuationOnError(true);
|
||||
onSubmit();
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -165,7 +275,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 };
|
||||
}
|
||||
|
@ -173,7 +283,7 @@ const PayPalComponent = ({
|
|||
return true;
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [onCheckoutValidation] );
|
||||
}, [onCheckoutValidation, gotoContinuationOnError] );
|
||||
|
||||
const handleClick = (data, actions) => {
|
||||
if (isEditing) {
|
||||
|
@ -185,16 +295,43 @@ const PayPalComponent = ({
|
|||
onClick();
|
||||
};
|
||||
|
||||
let handleShippingChange = null;
|
||||
let handleShippingOptionsChange = null;
|
||||
let handleShippingAddressChange = null;
|
||||
let handleSubscriptionShippingOptionsChange = null;
|
||||
let handleSubscriptionShippingAddressChange = null;
|
||||
if (shippingData.needsShipping && !config.finalReviewEnabled) {
|
||||
handleShippingChange = async (data, actions) => {
|
||||
handleShippingOptionsChange = async (data, actions) => {
|
||||
try {
|
||||
const shippingOptionId = data.selected_shipping_option?.id;
|
||||
const shippingOptionId = data.selectedShippingOption?.id;
|
||||
if (shippingOptionId) {
|
||||
await wp.data.dispatch('wc/store/cart').selectShippingRate(shippingOptionId);
|
||||
await shippingData.setSelectedRates(shippingOptionId);
|
||||
}
|
||||
|
||||
const address = paypalAddressToWc(data.shipping_address);
|
||||
const res = await fetch(config.ajax.update_shipping.endpoint, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({
|
||||
nonce: config.ajax.update_shipping.nonce,
|
||||
order_id: data.orderID,
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error(json.data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
actions.reject();
|
||||
}
|
||||
};
|
||||
|
||||
handleShippingAddressChange = async (data, actions) => {
|
||||
try {
|
||||
const address = paypalAddressToWc(convertKeysToSnakeCase(data.shippingAddress));
|
||||
|
||||
await wp.data.dispatch('wc/store/cart').updateCustomerData({
|
||||
shipping_address: address,
|
||||
|
@ -222,6 +359,37 @@ const PayPalComponent = ({
|
|||
actions.reject();
|
||||
}
|
||||
};
|
||||
|
||||
handleSubscriptionShippingOptionsChange = async (data, actions) => {
|
||||
try {
|
||||
const shippingOptionId = data.selectedShippingOption?.id;
|
||||
if (shippingOptionId) {
|
||||
await wp.data.dispatch('wc/store/cart').selectShippingRate(shippingOptionId);
|
||||
await shippingData.setSelectedRates(shippingOptionId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
actions.reject();
|
||||
}
|
||||
};
|
||||
|
||||
handleSubscriptionShippingAddressChange = async (data, actions) => {
|
||||
try {
|
||||
const address = paypalAddressToWc(convertKeysToSnakeCase(data.shippingAddress));
|
||||
|
||||
await wp.data.dispatch('wc/store/cart').updateCustomerData({
|
||||
shipping_address: address,
|
||||
});
|
||||
|
||||
await shippingData.setShippingAddress(address);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
actions.reject();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -302,7 +470,27 @@ const PayPalComponent = ({
|
|||
|
||||
const style = normalizeStyleForFundingSource(config.scriptData.button.style, fundingSource);
|
||||
|
||||
const PayPalButton = window.paypal.Buttons.driver("react", { React, ReactDOM });
|
||||
if (!paypalScriptLoaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const PayPalButton = paypal.Buttons.driver("react", { React, ReactDOM });
|
||||
|
||||
if(isPayPalSubscription(config.scriptData)) {
|
||||
return (
|
||||
<PayPalButton
|
||||
fundingSource={fundingSource}
|
||||
style={style}
|
||||
onClick={handleClick}
|
||||
onCancel={onClose}
|
||||
onError={onClose}
|
||||
createSubscription={createSubscription}
|
||||
onApprove={handleApproveSubscription}
|
||||
onShippingOptionsChange={handleSubscriptionShippingOptionsChange}
|
||||
onShippingAddressChange={handleSubscriptionShippingAddressChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PayPalButton
|
||||
|
@ -313,59 +501,110 @@ const PayPalComponent = ({
|
|||
onError={onClose}
|
||||
createOrder={createOrder}
|
||||
onApprove={handleApprove}
|
||||
onShippingChange={handleShippingChange}
|
||||
onShippingOptionsChange={handleShippingOptionsChange}
|
||||
onShippingAddressChange={handleShippingAddressChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const features = ['products'];
|
||||
let block_enabled = true;
|
||||
|
||||
if ((config.addPlaceOrderMethod || config.usePlaceOrder) && !config.scriptData.continuation) {
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <div dangerouslySetInnerHTML={{__html: config.description}}/>,
|
||||
edit: <div dangerouslySetInnerHTML={{__html: config.description}}/>,
|
||||
placeOrderButtonLabel: config.placeOrderButtonText,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: () => config.enabled,
|
||||
supports: {
|
||||
features: features,
|
||||
},
|
||||
});
|
||||
if(cartHasSubscriptionProducts(config.scriptData)) {
|
||||
// Don't show buttons on block cart page if using vault v2 and user is not logged in
|
||||
if (
|
||||
! config.scriptData.user.is_logged
|
||||
&& config.scriptData.context === "cart-block"
|
||||
&& ! isPayPalSubscription(config.scriptData) // using vaulting
|
||||
&& ! config.scriptData?.save_payment_methods?.id_token // not vault v3
|
||||
) {
|
||||
block_enabled = false;
|
||||
}
|
||||
|
||||
// Don't render if vaulting disabled and is in vault subscription mode
|
||||
if(
|
||||
! isPayPalSubscription(config.scriptData)
|
||||
&& ! config.scriptData.can_save_vault_token
|
||||
) {
|
||||
block_enabled = false;
|
||||
}
|
||||
|
||||
// Don't render buttons if in subscription mode and product not associated with a PayPal subscription
|
||||
if(
|
||||
isPayPalSubscription(config.scriptData)
|
||||
&& !config.scriptData.subscription_product_allowed
|
||||
) {
|
||||
block_enabled = false;
|
||||
}
|
||||
|
||||
features.push('subscriptions');
|
||||
}
|
||||
|
||||
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);
|
||||
if (block_enabled) {
|
||||
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>;
|
||||
}
|
||||
|
||||
for (const fundingSource of ['paypal', ...config.enabledFundingSources]) {
|
||||
registerExpressPaymentMethod({
|
||||
name: `${config.id}-${fundingSource}`,
|
||||
paymentMethodId: config.id,
|
||||
registerPaymentMethod({
|
||||
name: config.id,
|
||||
label: <div dangerouslySetInnerHTML={{__html: config.title}}/>,
|
||||
content: <PayPalComponent isEditing={false} fundingSource={fundingSource}/>,
|
||||
edit: <PayPalComponent isEditing={true} fundingSource={fundingSource}/>,
|
||||
content: descriptionElement,
|
||||
edit: descriptionElement,
|
||||
placeOrderButtonLabel: config.placeOrderButtonText,
|
||||
ariaLabel: config.title,
|
||||
canMakePayment: async () => {
|
||||
await paypalScriptPromise;
|
||||
|
||||
return paypal.Buttons({fundingSource}).isEligible();
|
||||
canMakePayment: () => {
|
||||
return 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: () => {
|
||||
return true;
|
||||
},
|
||||
supports: {
|
||||
features: [...features, 'ppcp_continuation'],
|
||||
},
|
||||
});
|
||||
} else if (!config.usePlaceOrder) {
|
||||
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 () => {
|
||||
if (!paypalScriptPromise) {
|
||||
paypalScriptPromise = loadPaypalScriptPromise(config.scriptData);
|
||||
paypalScriptPromise.then(() => {
|
||||
const messagesBootstrap = new BlockCheckoutMessagesBootstrap(config.scriptData);
|
||||
messagesBootstrap.init();
|
||||
});
|
||||
}
|
||||
await paypalScriptPromise;
|
||||
|
||||
return paypal.Buttons({fundingSource}).isEligible();
|
||||
},
|
||||
supports: {
|
||||
features: features,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Blocks;
|
|||
|
||||
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
|
||||
return array(
|
||||
'blocks.url' => static function ( ContainerInterface $container ): string {
|
||||
|
@ -28,7 +29,9 @@ return array(
|
|||
return new PayPalPaymentMethod(
|
||||
$container->get( 'blocks.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'button.smart-button' ),
|
||||
function () use ( $container ): SmartButtonInterface {
|
||||
return $container->get( 'button.smart-button' );
|
||||
},
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.settings.status' ),
|
||||
$container->get( 'wcgateway.paypal-gateway' ),
|
||||
|
@ -38,6 +41,7 @@ return array(
|
|||
$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' )
|
||||
);
|
||||
},
|
||||
|
|
|
@ -88,6 +88,28 @@ class BlocksModule implements ModuleInterface {
|
|||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
// 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' );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
/**
|
||||
* The smart button script loading handler.
|
||||
*
|
||||
* @var SmartButtonInterface
|
||||
* @var SmartButtonInterface|callable
|
||||
*/
|
||||
private $smart_button;
|
||||
|
||||
|
@ -108,6 +108,13 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -118,24 +125,25 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
/**
|
||||
* Assets constructor.
|
||||
*
|
||||
* @param string $module_url The url of this module.
|
||||
* @param string $version The assets version.
|
||||
* @param SmartButtonInterface $smart_button The smart button script loading handler.
|
||||
* @param Settings $plugin_settings The settings.
|
||||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param PayPalGateway $gateway The WC gateway.
|
||||
* @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 array $all_funding_sources All existing funding sources for PayPal buttons.
|
||||
* @param string $module_url The url of this module.
|
||||
* @param string $version The assets version.
|
||||
* @param SmartButtonInterface|callable $smart_button The smart button script loading handler.
|
||||
* @param Settings $plugin_settings The settings.
|
||||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param PayPalGateway $gateway The WC gateway.
|
||||
* @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,
|
||||
string $version,
|
||||
SmartButtonInterface $smart_button,
|
||||
$smart_button,
|
||||
Settings $plugin_settings,
|
||||
SettingsStatus $settings_status,
|
||||
PayPalGateway $gateway,
|
||||
|
@ -145,22 +153,24 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
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;
|
||||
$this->version = $version;
|
||||
$this->smart_button = $smart_button;
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->gateway = $gateway;
|
||||
$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->all_funding_sources = $all_funding_sources;
|
||||
$this->name = PayPalGateway::ID;
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->smart_button = $smart_button;
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->gateway = $gateway;
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,7 +209,7 @@ class PayPalPaymentMethod extends AbstractPaymentMethodType {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public function get_payment_method_data() {
|
||||
$script_data = $this->smart_button->script_data();
|
||||
$script_data = $this->smart_button()->script_data();
|
||||
|
||||
if ( isset( $script_data['continuation'] ) ) {
|
||||
$url = add_query_arg( array( CancelController::NONCE => wp_create_nonce( CancelController::NONCE ) ), wc_get_checkout_url() );
|
||||
|
@ -214,32 +224,64 @@ 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
|
||||
)
|
||||
);
|
||||
$funding_sources = array();
|
||||
if ( ! $this->is_editing() ) {
|
||||
$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,
|
||||
'description' => $this->gateway->description,
|
||||
'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,
|
||||
'enabledFundingSources' => $funding_sources,
|
||||
'ajax' => array(
|
||||
'id' => $this->gateway->id,
|
||||
'title' => $this->gateway->title,
|
||||
'description' => $this->gateway->description,
|
||||
'enabled' => $this->settings_status->is_smart_button_enabled_for_location( $script_data['context'] ?? 'checkout' ),
|
||||
'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 ),
|
||||
'nonce' => wp_create_nonce( UpdateShippingEndpoint::nonce() ),
|
||||
),
|
||||
),
|
||||
'scriptData' => $script_data,
|
||||
'scriptData' => $script_data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is the block editing mode.
|
||||
*/
|
||||
private function is_editing(): bool {
|
||||
if ( ! function_exists( 'get_current_screen' ) ) {
|
||||
return false;
|
||||
}
|
||||
$screen = get_current_screen();
|
||||
return $screen && $screen->is_block_editor();
|
||||
}
|
||||
|
||||
/**
|
||||
* The smart button.
|
||||
*
|
||||
* @return SmartButtonInterface
|
||||
*/
|
||||
private function smart_button(): SmartButtonInterface {
|
||||
if ( $this->smart_button instanceof SmartButtonInterface ) {
|
||||
return $this->smart_button;
|
||||
}
|
||||
|
||||
if ( is_callable( $this->smart_button ) ) {
|
||||
$this->smart_button = ( $this->smart_button )();
|
||||
}
|
||||
|
||||
return $this->smart_button;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -39,11 +40,6 @@ const bootstrap = () => {
|
|||
);
|
||||
const spinner = new Spinner();
|
||||
|
||||
let creditCardRenderer = new HostedFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||
if (typeof paypal.CardFields !== 'undefined') {
|
||||
creditCardRenderer = new CardFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||
}
|
||||
|
||||
const formSaver = new FormSaver(
|
||||
PayPalCommerceGateway.ajax.save_checkout_form.endpoint,
|
||||
PayPalCommerceGateway.ajax.save_checkout_form.nonce,
|
||||
|
@ -72,13 +68,7 @@ const bootstrap = () => {
|
|||
&& document.querySelector(PayPalCommerceGateway.messages.wrapper);
|
||||
}
|
||||
|
||||
const onSmartButtonClick = async (data, actions) => {
|
||||
window.ppcpFundingSource = data.fundingSource;
|
||||
const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input');
|
||||
requiredFields.each((i, input) => {
|
||||
jQuery(input).trigger('validate');
|
||||
});
|
||||
|
||||
const doBasicCheckoutValidation = () => {
|
||||
if (PayPalCommerceGateway.basic_checkout_validation_enabled) {
|
||||
// A quick fix to get the errors about empty form fields before attempting PayPal order,
|
||||
// it should solve #513 for most of the users, but it is not a proper solution.
|
||||
|
@ -116,9 +106,26 @@ const bootstrap = () => {
|
|||
errorHandler.message(PayPalCommerceGateway.labels.error.required.generic);
|
||||
}
|
||||
|
||||
return actions.reject();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const onCardFieldsBeforeSubmit = () => {
|
||||
return doBasicCheckoutValidation();
|
||||
};
|
||||
|
||||
const onSmartButtonClick = async (data, actions) => {
|
||||
window.ppcpFundingSource = data.fundingSource;
|
||||
const requiredFields = jQuery('form.woocommerce-checkout .validate-required:visible :input');
|
||||
requiredFields.each((i, input) => {
|
||||
jQuery(input).trigger('validate');
|
||||
});
|
||||
|
||||
if (!doBasicCheckoutValidation()) {
|
||||
return actions.reject();
|
||||
}
|
||||
|
||||
const form = document.querySelector(checkoutFormSelector);
|
||||
if (form) {
|
||||
|
@ -130,7 +137,12 @@ const bootstrap = () => {
|
|||
}
|
||||
|
||||
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
||||
if (isFreeTrial && data.fundingSource !== 'card') {
|
||||
if (
|
||||
isFreeTrial
|
||||
&& data.fundingSource !== 'card'
|
||||
&& ! PayPalCommerceGateway.subscription_plan_id
|
||||
&& ! PayPalCommerceGateway.vault_v3_enabled
|
||||
) {
|
||||
freeTrialHandler.handle();
|
||||
return actions.reject();
|
||||
}
|
||||
|
@ -145,8 +157,15 @@ const bootstrap = () => {
|
|||
};
|
||||
|
||||
const onSmartButtonsInit = () => {
|
||||
jQuery(document).trigger('ppcp-smart-buttons-init', this);
|
||||
buttonsSpinner.unblock();
|
||||
};
|
||||
|
||||
let creditCardRenderer = new HostedFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner);
|
||||
if (typeof paypal.CardFields !== 'undefined') {
|
||||
creditCardRenderer = new CardFieldsRenderer(PayPalCommerceGateway, errorHandler, spinner, onCardFieldsBeforeSubmit);
|
||||
}
|
||||
|
||||
const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit);
|
||||
const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages);
|
||||
|
||||
|
@ -217,6 +236,8 @@ const bootstrap = () => {
|
|||
messageRenderer,
|
||||
);
|
||||
messagesBootstrap.init();
|
||||
|
||||
apmButtonsInit(PayPalCommerceGateway);
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
|
|
|
@ -9,11 +9,11 @@ class CartActionHandler {
|
|||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
subscriptionsConfiguration(subscription_plan_id) {
|
||||
return {
|
||||
createSubscription: (data, actions) => {
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
'plan_id': subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
@ -11,11 +12,17 @@ class CheckoutActionHandler {
|
|||
this.spinner = spinner;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
subscriptionsConfiguration(subscription_plan_id) {
|
||||
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
|
||||
'plan_id': subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
|
@ -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();
|
||||
|
@ -134,6 +144,54 @@ class CheckoutActionHandler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
addPaymentMethodConfiguration() {
|
||||
return {
|
||||
createVaultSetupToken: async () => {
|
||||
const response = await fetch(this.config.ajax.create_setup_token.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_setup_token.nonce,
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json()
|
||||
if (result.data.id) {
|
||||
return result.data.id
|
||||
}
|
||||
|
||||
console.error(result)
|
||||
},
|
||||
onApprove: async ({vaultSetupToken}) => {
|
||||
const response = await fetch(this.config.ajax.create_payment_token_for_guest.endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce: this.config.ajax.create_payment_token_for_guest.nonce,
|
||||
vault_setup_token: vaultSetupToken,
|
||||
})
|
||||
})
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success === true) {
|
||||
document.querySelector('#place_order').click()
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(result)
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CheckoutActionHandler;
|
||||
|
|
|
@ -7,7 +7,6 @@ class ButtonModuleWatcher {
|
|||
}
|
||||
|
||||
watchContextBootstrap(callable) {
|
||||
console.log('ButtonModuleWatcher.js: watchContextBootstrap', this.contextBootstrapRegistry)
|
||||
this.contextBootstrapWatchers.push(callable);
|
||||
Object.values(this.contextBootstrapRegistry).forEach(callable);
|
||||
}
|
||||
|
|
|
@ -90,7 +90,12 @@ class CartBootstrap {
|
|||
PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
&& PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration());
|
||||
let subscription_plan_id = PayPalCommerceGateway.subscription_plan_id
|
||||
if(PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart !== '') {
|
||||
subscription_plan_id = PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart
|
||||
}
|
||||
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration(subscription_plan_id));
|
||||
|
||||
if(!PayPalCommerceGateway.subscription_product_allowed) {
|
||||
this.gateway.button.is_disabled = true;
|
||||
|
|
|
@ -106,7 +106,11 @@ class CheckoutBootstap {
|
|||
PayPalCommerceGateway.data_client_id.has_subscriptions
|
||||
&& PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration(), {}, actionHandler.configuration());
|
||||
let subscription_plan_id = PayPalCommerceGateway.subscription_plan_id
|
||||
if(PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart !== '') {
|
||||
subscription_plan_id = PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart
|
||||
}
|
||||
this.renderer.render(actionHandler.subscriptionsConfiguration(subscription_plan_id), {}, actionHandler.configuration());
|
||||
|
||||
if(!PayPalCommerceGateway.subscription_product_allowed) {
|
||||
this.gateway.button.is_disabled = true;
|
||||
|
@ -116,6 +120,14 @@ class CheckoutBootstap {
|
|||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
PayPalCommerceGateway.is_free_trial_cart
|
||||
&& PayPalCommerceGateway.vault_v3_enabled
|
||||
) {
|
||||
this.renderer.render(actionHandler.addPaymentMethodConfiguration(), {}, actionHandler.configuration());
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.render(actionHandler.configuration(), {}, actionHandler.configuration());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import {setVisible} from "../Helper/Hiding";
|
||||
import MessageRenderer from "../Renderer/MessageRenderer";
|
||||
|
||||
class MessagesBootstrap {
|
||||
constructor(gateway, messageRenderer) {
|
||||
this.gateway = gateway;
|
||||
this.renderer = messageRenderer;
|
||||
this.renderers = [];
|
||||
this.lastAmount = this.gateway.messages.amount;
|
||||
if (messageRenderer) {
|
||||
this.renderers.push(messageRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.gateway.messages?.block?.enabled) {
|
||||
this.discoverBlocks();
|
||||
}
|
||||
jQuery(document.body).on('ppcp_cart_rendered ppcp_checkout_rendered', () => {
|
||||
this.render();
|
||||
});
|
||||
|
@ -16,7 +23,7 @@ class MessagesBootstrap {
|
|||
|
||||
this.render();
|
||||
});
|
||||
jQuery(document.body).on('ppcp_cart_total_updated ppcp_checkout_total_updated ppcp_product_total_updated', (e, amount) => {
|
||||
jQuery(document.body).on('ppcp_cart_total_updated ppcp_checkout_total_updated ppcp_product_total_updated ppcp_block_cart_total_updated', (e, amount) => {
|
||||
if (this.lastAmount !== amount) {
|
||||
this.lastAmount = amount;
|
||||
|
||||
|
@ -27,28 +34,40 @@ class MessagesBootstrap {
|
|||
this.render();
|
||||
}
|
||||
|
||||
shouldShow() {
|
||||
discoverBlocks() {
|
||||
Array.from(document.querySelectorAll('.ppcp-paylater-message-block')).forEach(blockElement => {
|
||||
const config = {wrapper: '#' + blockElement.id};
|
||||
if (!blockElement.getAttribute('data-pp-placement')) {
|
||||
config.placement = this.gateway.messages.placement;
|
||||
}
|
||||
this.renderers.push(new MessageRenderer(config));
|
||||
});
|
||||
}
|
||||
|
||||
shouldShow(renderer) {
|
||||
if (this.gateway.messages.is_hidden === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const eventData = {result: true}
|
||||
jQuery(document.body).trigger('ppcp_should_show_messages', [eventData]);
|
||||
jQuery(document.body).trigger('ppcp_should_show_messages', [eventData, renderer.config.wrapper]);
|
||||
return eventData.result;
|
||||
}
|
||||
|
||||
shouldRender() {
|
||||
return this.shouldShow() && this.renderer.shouldRender();
|
||||
}
|
||||
|
||||
render() {
|
||||
setVisible(this.gateway.messages.wrapper, this.shouldShow());
|
||||
this.renderers.forEach(renderer => {
|
||||
const shouldShow = this.shouldShow(renderer);
|
||||
setVisible(renderer.config.wrapper, shouldShow);
|
||||
if (!shouldShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldRender()) {
|
||||
return;
|
||||
}
|
||||
if (!renderer.shouldRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderer.renderWithAmount(this.lastAmount);
|
||||
renderer.renderWithAmount(this.lastAmount);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
120
modules/ppcp-button/resources/js/modules/Helper/ApmButtons.js
Normal file
120
modules/ppcp-button/resources/js/modules/Helper/ApmButtons.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
|
||||
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();
|
||||
});
|
||||
|
||||
jQuery(document).on('ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled', (ev, data) => {
|
||||
this.refresh();
|
||||
setTimeout(this.refresh.bind(this), 200);
|
||||
});
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 minMargin = 11; // Minimum margin.
|
||||
const height = $el.height();
|
||||
const margin = Math.max(minMargin, Math.round(height * 0.3));
|
||||
$el.css('margin-top', `${margin}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;
|
|
@ -56,14 +56,22 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => {
|
|||
|
||||
// Build the PayPal script options.
|
||||
let scriptOptions = keysToCamelCase(config.url_params);
|
||||
scriptOptions = merge(scriptOptions, config.script_attributes);
|
||||
if (config.script_attributes) {
|
||||
scriptOptions = merge(scriptOptions, config.script_attributes);
|
||||
}
|
||||
|
||||
// Load PayPal script for special case with data-client-token
|
||||
if (config.data_client_id.set_attribute) {
|
||||
if (config.data_client_id?.set_attribute) {
|
||||
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)
|
||||
|
@ -81,3 +89,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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import {show} from "../Helper/Hiding";
|
||||
import {cardFieldStyles} from "../Helper/CardFieldsHelper";
|
||||
|
||||
class CardFieldsRenderer {
|
||||
|
||||
constructor(defaultConfig, errorHandler, spinner) {
|
||||
constructor(defaultConfig, errorHandler, spinner, onCardFieldsBeforeSubmit) {
|
||||
this.defaultConfig = defaultConfig;
|
||||
this.errorHandler = errorHandler;
|
||||
this.spinner = spinner;
|
||||
|
@ -10,6 +11,7 @@ class CardFieldsRenderer {
|
|||
this.formValid = false;
|
||||
this.emptyFields = new Set(['number', 'cvv', 'expirationDate']);
|
||||
this.currentHostedFieldsInstance = null;
|
||||
this.onCardFieldsBeforeSubmit = onCardFieldsBeforeSubmit;
|
||||
}
|
||||
|
||||
render(wrapper, contextConfig) {
|
||||
|
@ -53,29 +55,53 @@ class CardFieldsRenderer {
|
|||
if (cardField.isEligible()) {
|
||||
const nameField = document.getElementById('ppcp-credit-card-gateway-card-name');
|
||||
if (nameField) {
|
||||
let styles = this.cardFieldStyles(nameField);
|
||||
cardField.NameField({style: {'input': styles}}).render(nameField.parentNode);
|
||||
let styles = cardFieldStyles(nameField);
|
||||
let fieldOptions = {
|
||||
style: { 'input': styles }
|
||||
}
|
||||
if (nameField.getAttribute('placeholder')) {
|
||||
fieldOptions.placeholder = nameField.getAttribute('placeholder');
|
||||
}
|
||||
cardField.NameField(fieldOptions).render(nameField.parentNode);
|
||||
nameField.remove();
|
||||
}
|
||||
|
||||
const numberField = document.getElementById('ppcp-credit-card-gateway-card-number');
|
||||
if (numberField) {
|
||||
let styles = this.cardFieldStyles(numberField);
|
||||
cardField.NumberField({style: {'input': styles}}).render(numberField.parentNode);
|
||||
let styles = cardFieldStyles(numberField);
|
||||
let fieldOptions = {
|
||||
style: { 'input': styles }
|
||||
}
|
||||
if (numberField.getAttribute('placeholder')) {
|
||||
fieldOptions.placeholder = numberField.getAttribute('placeholder');
|
||||
}
|
||||
cardField.NumberField(fieldOptions).render(numberField.parentNode);
|
||||
numberField.remove();
|
||||
}
|
||||
|
||||
const expiryField = document.getElementById('ppcp-credit-card-gateway-card-expiry');
|
||||
if (expiryField) {
|
||||
let styles = this.cardFieldStyles(expiryField);
|
||||
cardField.ExpiryField({style: {'input': styles}}).render(expiryField.parentNode);
|
||||
let styles = cardFieldStyles(expiryField);
|
||||
let fieldOptions = {
|
||||
style: { 'input': styles }
|
||||
}
|
||||
if (expiryField.getAttribute('placeholder')) {
|
||||
fieldOptions.placeholder = expiryField.getAttribute('placeholder');
|
||||
}
|
||||
cardField.ExpiryField(fieldOptions).render(expiryField.parentNode);
|
||||
expiryField.remove();
|
||||
}
|
||||
|
||||
const cvvField = document.getElementById('ppcp-credit-card-gateway-card-cvc');
|
||||
if (cvvField) {
|
||||
let styles = this.cardFieldStyles(cvvField);
|
||||
cardField.CVVField({style: {'input': styles}}).render(cvvField.parentNode);
|
||||
let styles = cardFieldStyles(cvvField);
|
||||
let fieldOptions = {
|
||||
style: { 'input': styles }
|
||||
}
|
||||
if (cvvField.getAttribute('placeholder')) {
|
||||
fieldOptions.placeholder = cvvField.getAttribute('placeholder');
|
||||
}
|
||||
cardField.CVVField(fieldOptions).render(cvvField.parentNode);
|
||||
cvvField.remove();
|
||||
}
|
||||
|
||||
|
@ -86,70 +112,41 @@ class CardFieldsRenderer {
|
|||
|
||||
show(buttonSelector);
|
||||
|
||||
if(this.defaultConfig.cart_contains_subscription) {
|
||||
const saveToAccount = document.querySelector('#wc-ppcp-credit-card-gateway-new-payment-method');
|
||||
if(saveToAccount) {
|
||||
saveToAccount.checked = true;
|
||||
saveToAccount.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector(buttonSelector).addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
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') {
|
||||
document.querySelector('#place_order').click();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.onCardFieldsBeforeSubmit === 'function' && !this.onCardFieldsBeforeSubmit()) {
|
||||
this.spinner.unblock();
|
||||
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;
|
||||
|
|
|
@ -15,9 +15,13 @@ class MessageRenderer {
|
|||
|
||||
const options = {
|
||||
amount,
|
||||
placement: this.config.placement,
|
||||
style: this.config.style
|
||||
};
|
||||
if (this.config.placement) {
|
||||
options.placement = this.config.placement;
|
||||
}
|
||||
if (this.config.style) {
|
||||
options.style = this.config.style;
|
||||
}
|
||||
|
||||
// sometimes the element is destroyed while the options stay the same
|
||||
if (document.querySelector(this.config.wrapper).getAttribute('data-render-number') !== this.currentNumber.toString()) {
|
||||
|
|
|
@ -119,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' );
|
||||
|
@ -145,6 +145,8 @@ return array(
|
|||
$container->get( 'button.early-wc-checkout-validation-enabled' ),
|
||||
$container->get( 'button.pay-now-contexts' ),
|
||||
$container->get( 'wcgateway.funding-sources-without-redirect' ),
|
||||
$container->get( 'vaulting.vault-v3-enabled' ),
|
||||
$container->get( 'api.endpoint.payment-tokens' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
@ -294,8 +296,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(
|
||||
|
|
|
@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface;
|
|||
use WC_Order;
|
||||
use WC_Product;
|
||||
use WC_Product_Variation;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
|
||||
|
@ -32,9 +33,13 @@ use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest;
|
||||
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;
|
||||
|
@ -183,13 +188,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $funding_sources_without_redirect;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
|
@ -197,6 +195,27 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* Whether Vault v3 module is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $vault_v3_enabled;
|
||||
|
||||
/**
|
||||
* Payment tokens endpoint.
|
||||
*
|
||||
* @var PaymentTokensEndpoint
|
||||
*/
|
||||
private $payment_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* SmartButton constructor.
|
||||
*
|
||||
|
@ -219,6 +238,8 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param bool $early_validation_enabled Whether to execute WC validation of the checkout form.
|
||||
* @param array $pay_now_contexts The contexts that should have the Pay Now button.
|
||||
* @param string[] $funding_sources_without_redirect The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back.
|
||||
* @param bool $vault_v3_enabled Whether Vault v3 module is enabled.
|
||||
* @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -241,6 +262,8 @@ class SmartButton implements SmartButtonInterface {
|
|||
bool $early_validation_enabled,
|
||||
array $pay_now_contexts,
|
||||
array $funding_sources_without_redirect,
|
||||
bool $vault_v3_enabled,
|
||||
PaymentTokensEndpoint $payment_tokens_endpoint,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
|
@ -263,7 +286,9 @@ class SmartButton implements SmartButtonInterface {
|
|||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->funding_sources_without_redirect = $funding_sources_without_redirect;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->logger = $logger;
|
||||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,6 +297,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 +330,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>',
|
||||
|
@ -385,7 +418,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @return bool
|
||||
*/
|
||||
private function render_message_wrapper_registrar(): bool {
|
||||
if ( ! $this->settings_status->is_pay_later_messaging_enabled() ) {
|
||||
if ( ! $this->settings_status->is_pay_later_messaging_enabled() || ! $this->settings_status->has_pay_later_messaging_locations() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -597,7 +630,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
case 'product':
|
||||
return $smart_button_enabled_for_current_location || $smart_button_enabled_for_mini_cart;
|
||||
default:
|
||||
return $smart_button_enabled_for_mini_cart;
|
||||
return $smart_button_enabled_for_mini_cart || $this->is_block_editor();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -610,6 +643,10 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->settings_status->is_pay_later_messaging_enabled() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->messages_apply->for_country() || $this->is_free_trial_cart() ) {
|
||||
return false;
|
||||
}
|
||||
|
@ -618,6 +655,8 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
|
||||
$messaging_enabled_for_current_location = $this->settings_status->is_pay_later_messaging_enabled_for_location( $location );
|
||||
|
||||
$has_paylater_block = has_block( 'woocommerce-paypal-payments/paylater-messages' ) && PayLaterBlockModule::is_block_enabled( $this->settings_status );
|
||||
|
||||
switch ( $location ) {
|
||||
case 'checkout':
|
||||
case 'cart':
|
||||
|
@ -626,8 +665,13 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
case 'shop':
|
||||
case 'home':
|
||||
return $messaging_enabled_for_current_location;
|
||||
case 'block-editor':
|
||||
return true;
|
||||
case 'checkout-block':
|
||||
case 'cart-block':
|
||||
return $has_paylater_block || $this->is_block_editor();
|
||||
default:
|
||||
return false;
|
||||
return $has_paylater_block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,7 +682,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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -835,9 +879,10 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
}
|
||||
|
||||
$product = wc_get_product();
|
||||
$amount = ( is_a( $product, WC_Product::class ) ) ? wc_get_price_including_tax( $product ) : 0;
|
||||
|
||||
if ( is_checkout() || is_cart() ) {
|
||||
$amount = 0;
|
||||
if ( is_a( $product, WC_Product::class ) ) {
|
||||
$amount = wc_get_price_including_tax( $product );
|
||||
} elseif ( isset( WC()->cart ) ) {
|
||||
$amount = WC()->cart->get_total( 'raw' );
|
||||
}
|
||||
|
||||
|
@ -851,10 +896,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$text_color = $this->settings->has( "{$setting_name_prefix}_color" ) ? $this->settings->get( "{$setting_name_prefix}_color" ) : 'black';
|
||||
$style_color = $this->settings->has( "{$setting_name_prefix}_flex_color" ) ? $this->settings->get( "{$setting_name_prefix}_flex_color" ) : 'blue';
|
||||
$ratio = $this->settings->has( "{$setting_name_prefix}_flex_ratio" ) ? $this->settings->get( "{$setting_name_prefix}_flex_ratio" ) : '1x1';
|
||||
$text_size = $this->settings->has( "{$setting_name_prefix}_text_size" ) ? $this->settings->get( "{$setting_name_prefix}_text_size" ) : '12';
|
||||
|
||||
return array(
|
||||
'wrapper' => '#ppcp-messages',
|
||||
'is_hidden' => ! $this->is_pay_later_filter_enabled_for_location( $this->context() ),
|
||||
'block' => array(
|
||||
'enabled' => PayLaterBlockModule::is_block_enabled( $this->settings_status ),
|
||||
),
|
||||
'amount' => $amount,
|
||||
'placement' => $placement,
|
||||
'style' => array(
|
||||
|
@ -865,12 +914,12 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
),
|
||||
'text' => array(
|
||||
'color' => $text_color,
|
||||
'size' => $text_size,
|
||||
),
|
||||
'color' => $style_color,
|
||||
'ratio' => $ratio,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -925,7 +974,10 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* @return bool
|
||||
*/
|
||||
private function has_subscriptions(): bool {
|
||||
if ( ! $this->subscription_helper->accept_only_automatic_payment_gateways() ) {
|
||||
if (
|
||||
! $this->subscription_helper->accept_only_automatic_payment_gateways()
|
||||
&& $this->paypal_subscriptions_enabled() !== true
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if ( is_product() ) {
|
||||
|
@ -962,11 +1014,21 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
if ( $this->settings->has( '3d_secure_contingency' ) ) {
|
||||
$value = $this->settings->get( '3d_secure_contingency' );
|
||||
if ( $value ) {
|
||||
return $value;
|
||||
return $this->return_3ds_contingency( $value );
|
||||
}
|
||||
}
|
||||
|
||||
return 'SCA_WHEN_REQUIRED';
|
||||
return $this->return_3ds_contingency( 'SCA_WHEN_REQUIRED' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes and returns the 3D Secure contingency.
|
||||
*
|
||||
* @param string $contingency The ThreeD secure contingency.
|
||||
* @return string
|
||||
*/
|
||||
private function return_3ds_contingency( string $contingency ): string {
|
||||
return apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $contingency );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -997,44 +1059,59 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'redirect' => wc_get_checkout_url(),
|
||||
'context' => $this->context(),
|
||||
'ajax' => array(
|
||||
'simulate_cart' => array(
|
||||
'simulate_cart' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( SimulateCartEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( SimulateCartEndpoint::nonce() ),
|
||||
),
|
||||
'change_cart' => array(
|
||||
'change_cart' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ),
|
||||
),
|
||||
'create_order' => array(
|
||||
'create_order' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ),
|
||||
),
|
||||
'approve_order' => array(
|
||||
'approve_order' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ),
|
||||
),
|
||||
'approve_subscription' => array(
|
||||
'approve_subscription' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ApproveSubscriptionEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ApproveSubscriptionEndpoint::nonce() ),
|
||||
),
|
||||
'vault_paypal' => array(
|
||||
'vault_paypal' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ),
|
||||
),
|
||||
'save_checkout_form' => array(
|
||||
'save_checkout_form' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ),
|
||||
),
|
||||
'validate_checkout' => array(
|
||||
'validate_checkout' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ),
|
||||
),
|
||||
'cart_script_params' => array(
|
||||
'cart_script_params' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ),
|
||||
),
|
||||
'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() ),
|
||||
),
|
||||
'create_payment_token_for_guest' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentTokenForGuest::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( CreatePaymentTokenForGuest::nonce() ),
|
||||
),
|
||||
),
|
||||
'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(),
|
||||
'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(),
|
||||
'vault_v3_enabled' => $this->vault_v3_enabled,
|
||||
'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(),
|
||||
'variable_paypal_subscription_variation_from_cart' => $this->subscription_helper->paypal_subscription_variation_from_cart(),
|
||||
'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(),
|
||||
'locations_with_subscription_product' => $this->subscription_helper->locations_with_subscription_product(),
|
||||
'enforce_vault' => $this->has_subscriptions(),
|
||||
|
@ -1146,6 +1223,9 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled,
|
||||
'early_checkout_validation_enabled' => $this->early_validation_enabled,
|
||||
'funding_sources_without_redirect' => $this->funding_sources_without_redirect,
|
||||
'user' => array(
|
||||
'is_logged' => is_user_logged_in(),
|
||||
),
|
||||
);
|
||||
|
||||
if ( 'pay-now' === $this->context() ) {
|
||||
|
@ -1162,7 +1242,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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1228,6 +1309,16 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
'commit' => in_array( $context, $this->pay_now_contexts, true ) ? 'true' : 'false',
|
||||
'intent' => $intent,
|
||||
);
|
||||
|
||||
if (
|
||||
$this->settings->has( 'subscriptions_mode' )
|
||||
&& $this->settings->get( 'subscriptions_mode' ) === 'vaulting_api'
|
||||
&& apply_filters( 'woocommerce_paypal_payments_save_payment_methods_eligible', false )
|
||||
) {
|
||||
// Remove vault parameter to allow for Venmo with Save Payment Methods (Vault V3).
|
||||
unset( $params['vault'] );
|
||||
}
|
||||
|
||||
if (
|
||||
$this->environment->current_environment_is( Environment::SANDBOX )
|
||||
&& defined( 'WP_DEBUG' ) && \WP_DEBUG
|
||||
|
@ -1275,7 +1366,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$disable_funding,
|
||||
array_diff(
|
||||
array_keys( $this->all_funding_sources ),
|
||||
array( 'venmo', 'paylater' )
|
||||
array( 'venmo', 'paylater', 'paypal' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1296,6 +1387,20 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$disable_funding[] = 'paylater';
|
||||
}
|
||||
|
||||
$disable_funding = array_filter(
|
||||
$disable_funding,
|
||||
/**
|
||||
* Make sure paypal is not sent in disable funding.
|
||||
*
|
||||
* @param string $funding_source The funding_source.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $funding_source ) {
|
||||
return $funding_source !== 'paypal';
|
||||
}
|
||||
);
|
||||
|
||||
if ( count( $disable_funding ) > 0 ) {
|
||||
$params['disable-funding'] = implode( ',', array_unique( $disable_funding ) );
|
||||
}
|
||||
|
@ -1438,8 +1543,8 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
$context = 'general';
|
||||
}
|
||||
|
||||
return $this->get_style_value( "button_{$context}_${style}" )
|
||||
?? $this->get_style_value( "button_${style}" )
|
||||
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 ] ?? '' );
|
||||
}
|
||||
|
@ -1454,7 +1559,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* @return string|int
|
||||
*/
|
||||
private function style_for_apm( string $style, string $apm, $default = null ) {
|
||||
return $this->get_style_value( "${apm}_button_${style}" )
|
||||
return $this->get_style_value( "{$apm}_button_{$style}" )
|
||||
?? ( $default ? $this->normalize_style_value( $default ) : null )
|
||||
?? $this->style_for_context( $style, 'checkout' );
|
||||
}
|
||||
|
@ -1575,14 +1680,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* The filter returning the action name that will be used for rendering Pay Later messages.
|
||||
*/
|
||||
$hook = (string) apply_filters(
|
||||
"woocommerce_paypal_payments_${location_hook}_messages_renderer_hook",
|
||||
"woocommerce_paypal_payments_{$location_hook}_messages_renderer_hook",
|
||||
$default_hook
|
||||
);
|
||||
/**
|
||||
* The filter returning the action priority that will be used for rendering Pay Later messages.
|
||||
*/
|
||||
$priority = (int) apply_filters(
|
||||
"woocommerce_paypal_payments_${location_hook}_messages_renderer_priority",
|
||||
"woocommerce_paypal_payments_{$location_hook}_messages_renderer_priority",
|
||||
$default_priority
|
||||
);
|
||||
return array(
|
||||
|
@ -1606,14 +1711,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
* The filter returning the action name that will be used for rendering Pay Later messages.
|
||||
*/
|
||||
$block_name = (string) apply_filters(
|
||||
"woocommerce_paypal_payments_${location_hook}_messages_renderer_block",
|
||||
"woocommerce_paypal_payments_{$location_hook}_messages_renderer_block",
|
||||
$default_block
|
||||
);
|
||||
/**
|
||||
* The filter returning the action priority that will be used for rendering Pay Later messages.
|
||||
*/
|
||||
$priority = (int) apply_filters(
|
||||
"woocommerce_paypal_payments_${location_hook}_messages_renderer_block_priority",
|
||||
"woocommerce_paypal_payments_{$location_hook}_messages_renderer_block_priority",
|
||||
$default_priority
|
||||
);
|
||||
return array(
|
||||
|
@ -1837,8 +1942,18 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
*/
|
||||
private function get_vaulted_paypal_email(): string {
|
||||
try {
|
||||
$tokens = $this->get_payment_tokens();
|
||||
$customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
||||
if ( $customer_id ) {
|
||||
$customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id );
|
||||
foreach ( $customer_tokens as $token ) {
|
||||
$email_address = $token['payment_source']->properties()->email_address ?? '';
|
||||
if ( $email_address ) {
|
||||
return $email_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tokens = $this->get_payment_tokens();
|
||||
foreach ( $tokens as $token ) {
|
||||
if ( isset( $token->source()->paypal ) ) {
|
||||
return $token->source()->paypal->payer->email_address;
|
||||
|
@ -1847,6 +1962,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages
|
|||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Failed to get PayPal vaulted email. ' . $exception->getMessage() );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface;
|
|||
use Throwable;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
|
||||
/**
|
||||
* Class CartScriptParamsEndpoint.
|
||||
|
@ -25,7 +26,7 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
/**
|
||||
* The SmartButton.
|
||||
*
|
||||
* @var SmartButton
|
||||
* @var SmartButtonInterface
|
||||
*/
|
||||
private $smart_button;
|
||||
|
||||
|
@ -39,11 +40,11 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
/**
|
||||
* CartScriptParamsEndpoint constructor.
|
||||
*
|
||||
* @param SmartButton $smart_button he SmartButton.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param SmartButtonInterface $smart_button he SmartButton.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
SmartButton $smart_button,
|
||||
SmartButtonInterface $smart_button,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->smart_button = $smart_button;
|
||||
|
@ -66,6 +67,11 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
*/
|
||||
public function handle_request(): bool {
|
||||
try {
|
||||
if ( ! $this->smart_button instanceof SmartButton ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( is_callable( 'wc_maybe_define_constant' ) ) {
|
||||
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
|
||||
}
|
||||
|
@ -73,6 +79,10 @@ class CartScriptParamsEndpoint implements EndpointInterface {
|
|||
$include_shipping = (bool) wc_clean( wp_unslash( $_GET['shipping'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$script_data = $this->smart_button->script_data();
|
||||
if ( ! $script_data ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
$total = (float) WC()->cart->get_total( 'numeric' );
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -295,7 +294,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
if ( $this->early_validation_enabled
|
||||
&& $this->form
|
||||
&& 'checkout' === $data['context']
|
||||
&& in_array( $payment_method, array( PayPalGateway::ID, CardButtonGateway::ID ), true )
|
||||
&& in_array( $payment_method, array( PayPalGateway::ID, CardButtonGateway::ID, CreditCardGateway::ID ), true )
|
||||
) {
|
||||
$this->validate_form( $this->form );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -330,6 +329,21 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
|
||||
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
|
||||
|
||||
$payment_source = $order->payment_source();
|
||||
$payment_source_name = $payment_source ? $payment_source->name() : null;
|
||||
$payer = $order->payer();
|
||||
if (
|
||||
$payer
|
||||
&& $payment_source_name
|
||||
&& in_array( $payment_source_name, PayPalGateway::PAYMENT_SOURCES_WITH_PAYER_EMAIL, true )
|
||||
) {
|
||||
$payer_email = $payer->email_address();
|
||||
if ( $payer_email ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email );
|
||||
}
|
||||
}
|
||||
|
||||
$wc_order->save_meta_data();
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );
|
||||
|
@ -416,14 +430,17 @@ 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.
|
||||
*
|
||||
* @throws RuntimeException If create order request fails.
|
||||
* @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 +482,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,
|
||||
|
|
|
@ -13,6 +13,7 @@ use Exception;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButton;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\CartProductsHelper;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +26,7 @@ class SimulateCartEndpoint extends AbstractCartEndpoint {
|
|||
/**
|
||||
* The SmartButton.
|
||||
*
|
||||
* @var SmartButton
|
||||
* @var SmartButtonInterface
|
||||
*/
|
||||
private $smart_button;
|
||||
|
||||
|
@ -39,14 +40,14 @@ class SimulateCartEndpoint extends AbstractCartEndpoint {
|
|||
/**
|
||||
* ChangeCartEndpoint constructor.
|
||||
*
|
||||
* @param SmartButton $smart_button The SmartButton.
|
||||
* @param \WC_Cart $cart The current WC cart object.
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param CartProductsHelper $cart_products The cart products helper.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param SmartButtonInterface $smart_button The SmartButton.
|
||||
* @param \WC_Cart $cart The current WC cart object.
|
||||
* @param RequestData $request_data The request data helper.
|
||||
* @param CartProductsHelper $cart_products The cart products helper.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
SmartButton $smart_button,
|
||||
SmartButtonInterface $smart_button,
|
||||
\WC_Cart $cart,
|
||||
RequestData $request_data,
|
||||
CartProductsHelper $cart_products,
|
||||
|
@ -68,6 +69,11 @@ class SimulateCartEndpoint extends AbstractCartEndpoint {
|
|||
* @throws Exception On error.
|
||||
*/
|
||||
protected function handle_data(): bool {
|
||||
if ( ! $this->smart_button instanceof SmartButton ) {
|
||||
wp_send_json_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
$products = $this->products_from_request();
|
||||
|
||||
if ( ! $products ) {
|
||||
|
|
|
@ -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,14 @@ trait ContextTrait {
|
|||
return 'checkout';
|
||||
}
|
||||
|
||||
if ( $this->is_add_payment_method_page() ) {
|
||||
return 'add-payment-method';
|
||||
}
|
||||
|
||||
if ( $this->is_block_editor() ) {
|
||||
return 'block-editor';
|
||||
}
|
||||
|
||||
return 'mini-cart';
|
||||
}
|
||||
|
||||
|
@ -125,6 +166,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 +183,7 @@ trait ContextTrait {
|
|||
}
|
||||
|
||||
$source = $order->payment_source();
|
||||
if ( $source && $source->card() ) {
|
||||
if ( $source && $source->name() === 'card' ) {
|
||||
return false; // Ignore for DCC.
|
||||
}
|
||||
|
||||
|
@ -147,4 +193,46 @@ 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'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this user is changing the payment method for a subscription.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_subscription_change_payment_method_page(): bool {
|
||||
if ( isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return wcs_is_subscription( wc_clean( wp_unslash( $_GET['change_payment_method'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is the block editor page.
|
||||
*/
|
||||
protected function is_block_editor(): bool {
|
||||
if ( ! function_exists( 'get_current_screen' ) ) {
|
||||
return false;
|
||||
}
|
||||
$screen = get_current_screen();
|
||||
return $screen && $screen->is_block_editor();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,22 @@ class EarlyOrderHandler {
|
|||
$wc_order = wc_get_order( $order_id );
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
|
||||
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
|
||||
|
||||
$payment_source = $order->payment_source();
|
||||
$payment_source_name = $payment_source ? $payment_source->name() : null;
|
||||
$payer = $order->payer();
|
||||
if (
|
||||
$payer
|
||||
&& $payment_source_name
|
||||
&& in_array( $payment_source_name, PayPalGateway::PAYMENT_SOURCES_WITH_PAYER_EMAIL, true )
|
||||
&& $wc_order instanceof \WC_Order
|
||||
) {
|
||||
$payer_email = $payer->email_address();
|
||||
if ( $payer_email ) {
|
||||
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email );
|
||||
}
|
||||
}
|
||||
|
||||
$wc_order->save_meta_data();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 LoggerInterface $logger The logger.
|
||||
* @param CardAuthenticationResultFactory $card_authentication_result_factory Card authentication result factory.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( LoggerInterface $logger ) {
|
||||
$this->logger = $logger;
|
||||
public function __construct(
|
||||
CardAuthenticationResultFactory $card_authentication_result_factory,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->card_authentication_result_factory = $card_authentication_result_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,35 +57,58 @@ class ThreeDSecure {
|
|||
*
|
||||
* @link https://developer.paypal.com/docs/business/checkout/add-capabilities/3d-secure/#authenticationresult
|
||||
*
|
||||
* @param Order $order The order for which the decission is needed.
|
||||
* @param Order $order The order for which the decision is needed.
|
||||
*
|
||||
* @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() ) {
|
||||
return self::NO_DECISION;
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_three_d_secure_before_check', $order );
|
||||
|
||||
$payment_source = $order->payment_source();
|
||||
if ( ! $payment_source ) {
|
||||
return $this->return_decision( self::NO_DECISION, $order );
|
||||
}
|
||||
|
||||
$result = $order->payment_source()->card()->authentication_result();
|
||||
$this->logger->info( '3DS authentication result: ' . wc_print_r( $result->to_array(), true ) );
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) {
|
||||
return self::PROCCEED;
|
||||
if ( ! ( $payment_source->properties()->brand ?? '' ) ) {
|
||||
return $this->return_decision( self::NO_DECISION, $order );
|
||||
}
|
||||
if ( ! ( $payment_source->properties()->authentication_result ?? '' ) ) {
|
||||
return $this->return_decision( self::NO_DECISION, $order );
|
||||
}
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_UNKNOWN ) {
|
||||
return self::RETRY;
|
||||
$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 ) {
|
||||
return $this->return_decision( self::PROCCEED, $order );
|
||||
}
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_UNKNOWN ) {
|
||||
return $this->return_decision( self::RETRY, $order );
|
||||
}
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_NO ) {
|
||||
return $this->return_decision( $this->no_liability_shift( $result ), $order );
|
||||
}
|
||||
}
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_NO ) {
|
||||
return $this->no_liability_shift( $result );
|
||||
}
|
||||
return self::NO_DECISION;
|
||||
|
||||
return $this->return_decision( self::NO_DECISION, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes and returns a ThreeD secure decision.
|
||||
*
|
||||
* @param int $decision The ThreeD secure decision.
|
||||
* @param Order $order The PayPal Order object.
|
||||
* @return int
|
||||
*/
|
||||
public function return_decision( int $decision, Order $order ) {
|
||||
$decision = apply_filters( 'woocommerce_paypal_payments_three_d_secure_decision', $decision, $order );
|
||||
do_action( 'woocommerce_paypal_payments_three_d_secure_after_check', $order, $decision );
|
||||
return $decision;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,726 @@ return array(
|
|||
return apply_filters(
|
||||
'woocommerce_paypal_payments_card_fields_supported_country_currency_matrix',
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
|
@ -38,6 +758,30 @@ return array(
|
|||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -39,6 +39,12 @@ class CardFieldsModule implements ModuleInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
if ( ! $settings->has( 'dcc_enabled' ) || ! $settings->get( 'dcc_enabled' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
|
@ -80,6 +86,17 @@ class CardFieldsModule implements ModuleInterface {
|
|||
array_unshift( $default_fields, $new_field );
|
||||
}
|
||||
|
||||
if ( apply_filters( 'woocommerce_paypal_payments_card_fields_translate_card_number', true ) ) {
|
||||
if ( isset( $default_fields['card-number-field'] ) ) {
|
||||
// Replaces the default card number placeholder with a translatable one.
|
||||
$default_fields['card-number-field'] = str_replace(
|
||||
'•••• •••• •••• ••••',
|
||||
esc_attr__( 'Card number', 'woocommerce-paypal-payments' ),
|
||||
$default_fields['card-number-field']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $default_fields;
|
||||
},
|
||||
10,
|
||||
|
@ -89,20 +106,28 @@ 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 );
|
||||
|
||||
$three_d_secure_contingency =
|
||||
$settings->has( '3d_secure_contingency' )
|
||||
? apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $settings->get( '3d_secure_contingency' ) )
|
||||
: '';
|
||||
|
||||
if (
|
||||
$settings->has( '3d_secure_contingency' )
|
||||
&& (
|
||||
$settings->get( '3d_secure_contingency' ) === 'SCA_ALWAYS'
|
||||
|| $settings->get( '3d_secure_contingency' ) === 'SCA_WHEN_REQUIRED'
|
||||
)
|
||||
$three_d_secure_contingency === 'SCA_ALWAYS'
|
||||
|| $three_d_secure_contingency === 'SCA_WHEN_REQUIRED'
|
||||
) {
|
||||
$data['payment_source']['card'] = array(
|
||||
'attributes' => array(
|
||||
'verification' => array(
|
||||
'method' => $settings->get( '3d_secure_contingency' ),
|
||||
'method' => $three_d_secure_contingency,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -75,7 +75,7 @@ class PPECHelper {
|
|||
}
|
||||
|
||||
global $wpdb;
|
||||
if ( class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
if ( class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled() && isset( $wpdb->wc_orders ) ) {
|
||||
$result = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT 1 FROM {$wpdb->wc_orders} WHERE payment_method = %s",
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,7 +42,7 @@ return array(
|
|||
// Connection tab fields.
|
||||
$fields = $insert_after(
|
||||
$fields,
|
||||
'ppcp_dcc_status',
|
||||
'ppcp_reference_transactions_status',
|
||||
array(
|
||||
'googlepay_status' => array(
|
||||
'title' => __( 'Google Pay Payments', 'woocommerce-paypal-payments' ),
|
||||
|
|
|
@ -1,45 +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: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart {
|
||||
.ppcp-button-googlepay {
|
||||
margin: 0;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.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;
|
||||
|
|
|
@ -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,7 +44,7 @@ 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@ import GooglepayManager from "./GooglepayManager";
|
|||
}
|
||||
});
|
||||
|
||||
// Use set timeout as it's unnecessary to refresh upon Minicart initial render.
|
||||
setTimeout(() => {
|
||||
jQuery(document.body).on('wc_fragments_loaded wc_fragments_refreshed', () => {
|
||||
if (manager) {
|
||||
manager.reinit();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
|
|
|
@ -90,8 +90,9 @@ return array(
|
|||
return apply_filters(
|
||||
'woocommerce_paypal_payments_googlepay_supported_country_currency_matrix',
|
||||
array(
|
||||
'CA' => array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -100,16 +101,22 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -118,16 +125,334 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
|
@ -136,12 +461,377 @@ return array(
|
|||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper;
|
|||
|
||||
use Throwable;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatusCapability;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
@ -100,6 +101,11 @@ class ApmProductStatus {
|
|||
return false;
|
||||
}
|
||||
|
||||
$status_override = apply_filters( 'woocommerce_paypal_payments_google_pay_product_status', null );
|
||||
if ( null !== $status_override ) {
|
||||
return $status_override;
|
||||
}
|
||||
|
||||
// If status was already checked on this request return the same result.
|
||||
if ( null !== $this->current_status ) {
|
||||
return $this->current_status;
|
||||
|
@ -128,21 +134,32 @@ class ApmProductStatus {
|
|||
}
|
||||
|
||||
// Check the seller status for the intended capability.
|
||||
$has_capability = false;
|
||||
foreach ( $seller_status->products() as $product ) {
|
||||
if ( $product->name() !== 'PAYMENT_METHODS' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( in_array( self::CAPABILITY_NAME, $product->capabilities(), true ) ) {
|
||||
// Capability found, persist status and return true.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||
$this->settings->persist();
|
||||
|
||||
$this->current_status = true;
|
||||
return $this->current_status;
|
||||
$has_capability = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $seller_status->capabilities() as $capability ) {
|
||||
if ( $capability->name() === self::CAPABILITY_NAME && $capability->status() === SellerStatusCapability::STATUS_ACTIVE ) {
|
||||
$has_capability = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_capability ) {
|
||||
// Capability found, persist status and return true.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||
$this->settings->persist();
|
||||
|
||||
$this->current_status = true;
|
||||
return $this->current_status;
|
||||
}
|
||||
|
||||
// Capability not found, persist status and return false.
|
||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED );
|
||||
$this->settings->persist();
|
||||
|
|
|
@ -359,6 +359,20 @@ document.addEventListener(
|
|||
);
|
||||
}
|
||||
|
||||
const referenceTransactionsCheck = () => {
|
||||
if (
|
||||
typeof PayPalCommerceGatewaySettings !== 'undefined'
|
||||
&& PayPalCommerceGatewaySettings.reference_transaction_enabled !== '1'
|
||||
) {
|
||||
document.getElementById('ppcp-vault_enabled')?.setAttribute('disabled', 'disabled');
|
||||
|
||||
const description = document.getElementById('field-vault_enabled')?.getElementsByClassName('description')[0];
|
||||
if (description) {
|
||||
description.innerHTML = PayPalCommerceGatewaySettings.vaulting_must_enable_advanced_wallet_message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(() => {
|
||||
removeDisabledCardIcons('select[name="ppcp[disable_cards][]"]', 'select[name="ppcp[card_icons][]"]');
|
||||
|
||||
|
@ -408,6 +422,8 @@ document.addEventListener(
|
|||
);
|
||||
|
||||
togglePayLaterMessageFields();
|
||||
|
||||
referenceTransactionsCheck()
|
||||
})();
|
||||
}
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -100,7 +100,7 @@ return array(
|
|||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_supported_country_codes_for_second_version_of_tracking_api',
|
||||
array( 'US', 'AU', 'CA', 'FR', 'DE', 'IT', 'ES' )
|
||||
array( 'US', 'AU', 'CA', 'FR', 'DE', 'IT', 'ES', 'GB' )
|
||||
);
|
||||
},
|
||||
'order-tracking.should-use-second-version-of-api' => static function ( ContainerInterface $container ): bool {
|
||||
|
|
|
@ -14,6 +14,7 @@ use WC_Order_Item_Product;
|
|||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\ItemTrait;
|
||||
use WooCommerce\PayPalCommerce\OrderTracking\OrderTrackingModule;
|
||||
|
||||
/**
|
||||
|
@ -21,6 +22,8 @@ use WooCommerce\PayPalCommerce\OrderTracking\OrderTrackingModule;
|
|||
*/
|
||||
class Shipment implements ShipmentInterface {
|
||||
|
||||
use ItemTrait;
|
||||
|
||||
/**
|
||||
* The WC order ID.
|
||||
*
|
||||
|
@ -166,12 +169,12 @@ class Shipment implements ShipmentInterface {
|
|||
$image = wp_get_attachment_image_src( (int) $product->get_image_id(), 'full' );
|
||||
|
||||
$ppcp_order_item = new Item(
|
||||
mb_substr( $item->get_name(), 0, 127 ),
|
||||
$this->prepare_item_string( $item->get_name() ),
|
||||
new Money( $price_without_tax_rounded, $currency ),
|
||||
$quantity,
|
||||
$this->prepare_description( $product->get_description() ),
|
||||
$this->prepare_item_string( $product->get_description() ),
|
||||
null,
|
||||
$product->get_sku(),
|
||||
$this->prepare_sku( $product->get_sku() ),
|
||||
$product->is_virtual() ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS,
|
||||
$product->get_permalink(),
|
||||
$image[0] ?? ''
|
||||
|
@ -239,17 +242,6 @@ class Shipment implements ShipmentInterface {
|
|||
return $shipment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanups the description and prepares it for sending to PayPal.
|
||||
*
|
||||
* @param string $description Item description.
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare_description( string $description ): string {
|
||||
$description = strip_shortcodes( wp_strip_all_tags( $description ) );
|
||||
return substr( $description, 0, 127 ) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the shipment line items info.
|
||||
*
|
||||
|
|
14
modules/ppcp-paylater-block/.babelrc
Normal file
14
modules/ppcp-paylater-block/.babelrc
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react"
|
||||
]
|
||||
]
|
||||
}
|
3
modules/ppcp-paylater-block/.gitignore
vendored
Normal file
3
modules/ppcp-paylater-block/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
assets/js
|
||||
assets/css
|
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