mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-31 06:52:50 +08:00
Resolve merge conflict
This commit is contained in:
commit
2317c90db4
77 changed files with 6249 additions and 123 deletions
|
@ -81,5 +81,12 @@ return function ( string $root_dir ): iterable {
|
|||
$modules[] = ( require "$modules_dir/ppcp-paylater-configurator/module.php" )();
|
||||
}
|
||||
|
||||
if ( apply_filters(
|
||||
'woocommerce.feature-flags.woocommerce_paypal_payments.axo_enabled',
|
||||
getenv( 'PCP_AXO_ENABLED' ) === '1'
|
||||
) ) {
|
||||
$modules[] = ( require "$modules_dir/ppcp-axo/module.php" )();
|
||||
}
|
||||
|
||||
return $modules;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
|
@ -1633,4 +1634,11 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken {
|
||||
return new SdkClientToken(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
109
modules/ppcp-api-client/src/Authentication/SdkClientToken.php
Normal file
109
modules/ppcp-api-client/src/Authentication/SdkClientToken.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?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 SdkClientToken
|
||||
*/
|
||||
class SdkClientToken {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* SdkClientToken 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 `sdk_client_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 sdk_client_token( string $target_customer_id = '' ): string {
|
||||
$bearer = $this->bearer->bearer();
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' );
|
||||
|
||||
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=*.' . $domain;
|
||||
|
||||
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->access_token;
|
||||
}
|
||||
}
|
|
@ -115,7 +115,7 @@ class PaymentMethodTokensEndpoint {
|
|||
* @throws RuntimeException When something when wrong with the request.
|
||||
* @throws PayPalApiException When something when wrong setting up the token.
|
||||
*/
|
||||
public function payment_tokens( PaymentSource $payment_source ): stdClass {
|
||||
public function create_payment_token( PaymentSource $payment_source ): stdClass {
|
||||
$data = array(
|
||||
'payment_source' => array(
|
||||
$payment_source->name() => $payment_source->properties(),
|
||||
|
|
|
@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Exception;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Class PayPalApiException
|
||||
*/
|
||||
|
@ -17,7 +19,7 @@ class PayPalApiException extends RuntimeException {
|
|||
/**
|
||||
* The JSON response object of PayPal.
|
||||
*
|
||||
* @var \stdClass
|
||||
* @var stdClass
|
||||
*/
|
||||
private $response;
|
||||
|
||||
|
@ -31,10 +33,10 @@ class PayPalApiException extends RuntimeException {
|
|||
/**
|
||||
* PayPalApiException constructor.
|
||||
*
|
||||
* @param \stdClass|null $response The JSON object.
|
||||
* @param stdClass|null $response The JSON object.
|
||||
* @param int $status_code The HTTP status code.
|
||||
*/
|
||||
public function __construct( \stdClass $response = null, int $status_code = 0 ) {
|
||||
public function __construct( stdClass $response = null, int $status_code = 0 ) {
|
||||
if ( is_null( $response ) ) {
|
||||
$response = new \stdClass();
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ class PayPalApiException extends RuntimeException {
|
|||
*/
|
||||
$this->response = $response;
|
||||
$this->status_code = $status_code;
|
||||
$message = $response->message;
|
||||
$message = $this->get_customer_friendly_message( $response );
|
||||
if ( $response->name ) {
|
||||
$message = '[' . $response->name . '] ' . $message;
|
||||
}
|
||||
|
@ -141,4 +143,40 @@ class PayPalApiException extends RuntimeException {
|
|||
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a friendly message if the error detail is known.
|
||||
*
|
||||
* @param stdClass $json The response.
|
||||
* @return string
|
||||
*/
|
||||
public function get_customer_friendly_message( stdClass $json ): string {
|
||||
if ( empty( $json->details ) ) {
|
||||
return $json->message;
|
||||
}
|
||||
$improved_keys_messages = array(
|
||||
'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ),
|
||||
'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ),
|
||||
'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ),
|
||||
'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ),
|
||||
'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ),
|
||||
'AGREEMENT_ALREADY_CANCELLED' => __( 'The requested agreement is already canceled. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
$improved_errors = array_filter(
|
||||
array_keys( $improved_keys_messages ),
|
||||
function ( $key ) use ( $json ): bool {
|
||||
foreach ( $json->details as $detail ) {
|
||||
if ( isset( $detail->issue ) && $detail->issue === $key ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
if ( $improved_errors ) {
|
||||
$improved_errors = array_values( $improved_errors );
|
||||
return $improved_keys_messages[ $improved_errors[0] ];
|
||||
}
|
||||
return $json->message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
BIN
modules/ppcp-applepay/assets/images/applepay.png
Normal file
BIN
modules/ppcp-applepay/assets/images/applepay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -68,6 +68,8 @@ return array(
|
|||
$device_eligibility_notes = __( 'The Apple Pay button will be visible both in previews and below the PayPal buttons in the shop.', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
$module_url = $container->get( 'applepay.url' );
|
||||
|
||||
// Connection tab fields.
|
||||
$fields = $insert_after(
|
||||
$fields,
|
||||
|
@ -91,10 +93,15 @@ return array(
|
|||
$connection_link = '<a href="' . $connection_url . '" style="pointer-events: auto">';
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'allow_card_button_gateway',
|
||||
'digital_wallet_heading',
|
||||
array(
|
||||
'applepay_button_enabled' => array(
|
||||
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Apple Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'type' => 'checkbox',
|
||||
'class' => array( 'ppcp-grayed-out-text' ),
|
||||
'input_class' => array( 'ppcp-disabled-checkbox' ),
|
||||
|
@ -109,7 +116,7 @@ return array(
|
|||
. '</p>',
|
||||
'default' => 'yes',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
'custom_attributes' => array(
|
||||
'data-ppcp-display' => wp_json_encode(
|
||||
|
@ -122,6 +129,7 @@ return array(
|
|||
)
|
||||
),
|
||||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
@ -129,10 +137,25 @@ return array(
|
|||
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'allow_card_button_gateway',
|
||||
'digital_wallet_heading',
|
||||
array(
|
||||
'spacer' => array(
|
||||
'title' => '',
|
||||
'type' => 'ppcp-text',
|
||||
'text' => '',
|
||||
'class' => array(),
|
||||
'classes' => array( 'ppcp-active-spacer' ),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_enabled' => array(
|
||||
'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/applepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Apple Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' )
|
||||
. '<p class="description">'
|
||||
|
@ -145,7 +168,7 @@ return array(
|
|||
. '</p>',
|
||||
'default' => 'yes',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
'custom_attributes' => array(
|
||||
'data-ppcp-display' => wp_json_encode(
|
||||
|
@ -160,10 +183,12 @@ return array(
|
|||
->action_visible( 'applepay_button_type' )
|
||||
->action_visible( 'applepay_button_language' )
|
||||
->action_visible( 'applepay_checkout_data_mode' )
|
||||
->action_class( 'applepay_button_enabled', 'active' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
'applepay_button_domain_registration' => array(
|
||||
'title' => __( 'Domain Registration', 'woocommerce-paypal-payments' ),
|
||||
|
@ -183,7 +208,7 @@ return array(
|
|||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_domain_validation' => array(
|
||||
|
@ -206,7 +231,7 @@ return array(
|
|||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_device_eligibility' => array(
|
||||
|
@ -224,7 +249,7 @@ return array(
|
|||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_type' => array(
|
||||
|
@ -241,7 +266,7 @@ return array(
|
|||
'default' => 'pay',
|
||||
'options' => PropertiesDictionary::button_types(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_color' => array(
|
||||
|
@ -259,7 +284,7 @@ return array(
|
|||
'default' => 'black',
|
||||
'options' => PropertiesDictionary::button_colors(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_button_language' => array(
|
||||
|
@ -276,7 +301,7 @@ return array(
|
|||
'default' => 'en',
|
||||
'options' => PropertiesDictionary::button_languages(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'applepay_checkout_data_mode' => array(
|
||||
|
@ -290,7 +315,7 @@ return array(
|
|||
'default' => PropertiesDictionary::BILLING_DATA_MODE_DEFAULT,
|
||||
'options' => PropertiesDictionary::billing_data_modes(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -39,7 +39,7 @@ class ApplepayButton {
|
|||
|
||||
this.log = function() {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
console.log('[ApplePayButton]', ...arguments);
|
||||
//console.log('[ApplePayButton]', ...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
modules/ppcp-axo/.babelrc
Normal file
14
modules/ppcp-axo/.babelrc
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.25.0"
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/preset-react"
|
||||
]
|
||||
]
|
||||
}
|
3
modules/ppcp-axo/.gitignore
vendored
Normal file
3
modules/ppcp-axo/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
assets/js
|
||||
assets/css
|
BIN
modules/ppcp-axo/assets/images/fastlane.png
Normal file
BIN
modules/ppcp-axo/assets/images/fastlane.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
17
modules/ppcp-axo/composer.json
Normal file
17
modules/ppcp-axo/composer.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "woocommerce/ppcp-axo",
|
||||
"type": "dhii-mod",
|
||||
"description": "Axo module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WooCommerce\\PayPalCommerce\\Axo\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
336
modules/ppcp-axo/extensions.php
Normal file
336
modules/ppcp-axo/extensions.php
Normal file
|
@ -0,0 +1,336 @@
|
|||
<?php
|
||||
/**
|
||||
* The Axo module extensions.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager;
|
||||
|
||||
return array(
|
||||
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
|
||||
$insert_after = function( array $array, string $key, array $new ): array {
|
||||
$keys = array_keys( $array );
|
||||
$index = array_search( $key, $keys, true );
|
||||
$pos = false === $index ? count( $array ) : $index + 1;
|
||||
|
||||
return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
|
||||
};
|
||||
|
||||
$display_manager = $container->get( 'wcgateway.display-manager' );
|
||||
assert( $display_manager instanceof DisplayManager );
|
||||
|
||||
$module_url = $container->get( 'axo.url' );
|
||||
|
||||
// Standard Payments tab fields.
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'vault_enabled_dcc',
|
||||
array(
|
||||
'axo_heading' => array(
|
||||
'heading' => __( 'Fastlane', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'Offer an accelerated checkout experience that recognizes guest shoppers and autofills their details so they can pay in seconds.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
rel="noreferrer noopener"
|
||||
href="https://woo.com/document/woocommerce-paypal-payments/#vaulting-a-card"
|
||||
>',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_enabled' => array(
|
||||
'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/fastlane.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Fastlane', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable Fastlane by PayPal', 'woocommerce-paypal-payments' )
|
||||
. '<p class="description">'
|
||||
. __( 'Help accelerate checkout for guests with PayPal\'s autofill solution.', 'woocommerce-paypal-payments' )
|
||||
. '</p>',
|
||||
'default' => 'yes',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
'custom_attributes' => array(
|
||||
'data-ppcp-display' => wp_json_encode(
|
||||
array(
|
||||
$display_manager
|
||||
->rule()
|
||||
->condition_element( 'axo_enabled', '1' )
|
||||
->action_visible( 'axo_gateway_title' )
|
||||
->action_visible( 'axo_privacy' )
|
||||
->action_visible( 'axo_style_heading' )
|
||||
->action_class( 'axo_enabled', 'active' )
|
||||
->to_array(),
|
||||
$display_manager
|
||||
->rule()
|
||||
->condition_element( 'axo_enabled', '1' )
|
||||
->condition_js_variable( 'ppcpAxoShowStyles', true )
|
||||
->action_visible( 'axo_style_root_heading' )
|
||||
->action_visible( 'axo_style_root_bg_color' )
|
||||
->action_visible( 'axo_style_root_error_color' )
|
||||
->action_visible( 'axo_style_root_font_family' )
|
||||
->action_visible( 'axo_style_root_font_size_base' )
|
||||
->action_visible( 'axo_style_root_padding' )
|
||||
->action_visible( 'axo_style_root_primary_color' )
|
||||
->action_visible( 'axo_style_input_heading' )
|
||||
->action_visible( 'axo_style_input_bg_color' )
|
||||
->action_visible( 'axo_style_input_border_radius' )
|
||||
->action_visible( 'axo_style_input_border_color' )
|
||||
->action_visible( 'axo_style_input_border_width' )
|
||||
->action_visible( 'axo_style_input_text_color_base' )
|
||||
->action_visible( 'axo_style_input_focus_border_color' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
'axo_gateway_title' => array(
|
||||
'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'desc_tip' => true,
|
||||
'description' => __(
|
||||
'This controls the title of the Fastlane gateway the user sees on checkout.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'default' => __(
|
||||
'Fastlane Debit & Credit Cards',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_privacy' => array(
|
||||
'title' => __( 'Privacy', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'select',
|
||||
'desc_tip' => true,
|
||||
'description' => __(
|
||||
'This setting will control whether Fastlane branding is shown by email field.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => 'yes',
|
||||
'options' => PropertiesDictionary::privacy_options(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'axo_style_heading' => array(
|
||||
'heading' => __( 'Advanced Style Settings (optional)', 'woocommerce-paypal-payments' ),
|
||||
'heading_html' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__(
|
||||
'Advanced Style Settings (optional) %1$sSee more%2$s %3$sSee less%4$s',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a href="javascript:void(0)" id="ppcp-axo-style-more" onclick="jQuery(this).hide(); jQuery(\'#ppcp-axo-style-less\').show(); document.ppcpAxoShowStyles = true; jQuery(document).trigger(\'ppcp-display-change\');" style="font-weight: normal;">',
|
||||
'</a>',
|
||||
'<a href="javascript:void(0)" id="ppcp-axo-style-less" onclick="jQuery(this).hide(); jQuery(\'#ppcp-axo-style-more\').show(); document.ppcpAxoShowStyles = false; jQuery(document).trigger(\'ppcp-display-change\');" style="font-weight: normal; display: none;">',
|
||||
'</a>'
|
||||
),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'Leave the default styling, or customize how Fastlane looks on your website. %1$sSee PayPal\'s developer docs%2$s for info',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a href="https://www.paypal.com/us/fastlane" target="_blank">',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
|
||||
'axo_style_root_heading' => array(
|
||||
'heading' => __( 'Root Settings', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => __(
|
||||
'These apply to the overall Fastlane checkout module.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'requirements' => array( 'dcc' ),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_bg_color' => array(
|
||||
'title' => __( 'Background Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_error_color' => array(
|
||||
'title' => __( 'Error Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_font_family' => array(
|
||||
'title' => __( 'Font Family', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_font_size_base' => array(
|
||||
'title' => __( 'Font Size Base', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_padding' => array(
|
||||
'title' => __( 'Padding', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_root_primary_color' => array(
|
||||
'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_heading' => array(
|
||||
'heading' => __( 'Input Settings', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => __(
|
||||
'These apply to the customer input fields on your Fastlane module.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'requirements' => array( 'dcc' ),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_bg_color' => array(
|
||||
'title' => __( 'Background Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_border_radius' => array(
|
||||
'title' => __( 'Border Radius', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_border_color' => array(
|
||||
'title' => __( 'Border Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_border_width' => array(
|
||||
'title' => __( 'Border Width', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_text_color_base' => array(
|
||||
'title' => __( 'Text Color Base', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'axo_style_input_focus_border_color' => array(
|
||||
'title' => __( 'Focus Border Color', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
);
|
16
modules/ppcp-axo/module.php
Normal file
16
modules/ppcp-axo/module.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* The Axo module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return new AxoModule();
|
||||
};
|
34
modules/ppcp-axo/package.json
Normal file
34
modules/ppcp-axo/package.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "ppcp-axo",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"browserslist": [
|
||||
"> 0.5%",
|
||||
"Safari >= 8",
|
||||
"Chrome >= 41",
|
||||
"Firefox >= 43",
|
||||
"Edge >= 14"
|
||||
],
|
||||
"dependencies": {
|
||||
"@paypal/paypal-js": "^6.0.0",
|
||||
"core-js": "^3.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19",
|
||||
"@babel/preset-env": "^7.19",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@woocommerce/dependency-extraction-webpack-plugin": "^2.2.0",
|
||||
"babel-loader": "^8.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"webpack": "^5.76",
|
||||
"webpack-cli": "^4.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
40
modules/ppcp-axo/resources/css/styles.scss
Normal file
40
modules/ppcp-axo/resources/css/styles.scss
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
.ppcp-axo-card-icons {
|
||||
padding: 4px 0 16px 25px;
|
||||
|
||||
.ppcp-card-icon {
|
||||
float: left !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-watermark-container {
|
||||
max-width: 200px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.ppcp-axo-payment-container {
|
||||
padding: 1rem 0;
|
||||
background-color: #ffffff;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-axo-email-widget {
|
||||
border: 1px solid #cccccc;
|
||||
background-color: #eeeeee;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.ppcp-axo-field-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ppcp-axo-customer-details {
|
||||
margin-bottom: 40px;
|
||||
}
|
733
modules/ppcp-axo/resources/js/AxoManager.js
Normal file
733
modules/ppcp-axo/resources/js/AxoManager.js
Normal file
|
@ -0,0 +1,733 @@
|
|||
import Fastlane from "./Connection/Fastlane";
|
||||
import {log} from "./Helper/Debug";
|
||||
import DomElementCollection from "./Components/DomElementCollection";
|
||||
import ShippingView from "./Views/ShippingView";
|
||||
import BillingView from "./Views/BillingView";
|
||||
import CardView from "./Views/CardView";
|
||||
import PayPalInsights from "./Insights/PayPalInsights";
|
||||
import {disable,enable} from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler";
|
||||
|
||||
class AxoManager {
|
||||
|
||||
constructor(axoConfig, ppcpConfig) {
|
||||
this.axoConfig = axoConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
|
||||
this.initialized = false;
|
||||
this.fastlane = new Fastlane();
|
||||
this.$ = jQuery;
|
||||
|
||||
this.hideGatewaySelection = false;
|
||||
|
||||
this.status = {
|
||||
active: false,
|
||||
validEmail: false,
|
||||
hasProfile: false,
|
||||
useEmailWidget: this.useEmailWidget()
|
||||
};
|
||||
|
||||
this.data = {
|
||||
email: null,
|
||||
billing: null,
|
||||
shipping: null,
|
||||
card: null,
|
||||
};
|
||||
|
||||
this.el = new DomElementCollection();
|
||||
|
||||
this.styles = {
|
||||
root: {
|
||||
backgroundColorPrimary: '#ffffff'
|
||||
}
|
||||
};
|
||||
|
||||
this.locale = 'en_us';
|
||||
|
||||
this.registerEventHandlers();
|
||||
|
||||
this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el);
|
||||
this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el);
|
||||
this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this);
|
||||
|
||||
document.axoDebugSetStatus = (key, value) => {
|
||||
this.setStatus(key, value);
|
||||
}
|
||||
|
||||
document.axoDebugObject = () => {
|
||||
console.log(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (
|
||||
this.axoConfig?.insights?.enabled
|
||||
&& this.axoConfig?.insights?.client_id
|
||||
&& this.axoConfig?.insights?.session_id
|
||||
) {
|
||||
PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true });
|
||||
PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id);
|
||||
PayPalInsights.trackJsLoad();
|
||||
|
||||
if (document.querySelector('.woocommerce-checkout')) {
|
||||
PayPalInsights.trackBeginCheckout({
|
||||
amount: this.axoConfig?.insights?.amount,
|
||||
page_type: 'checkout',
|
||||
user_data: {
|
||||
country: 'US',
|
||||
is_store_member: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerGatewayChange();
|
||||
}
|
||||
|
||||
registerEventHandlers() {
|
||||
|
||||
this.$(document).on('change', 'input[name=payment_method]', (ev) => {
|
||||
const map = {
|
||||
'ppcp-axo-gateway': 'card',
|
||||
'ppcp-gateway': 'paypal',
|
||||
}
|
||||
|
||||
PayPalInsights.trackSelectPaymentMethod({
|
||||
payment_method_selected: map[ev.target.value] || 'other',
|
||||
page_type: 'checkout'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Listen to Gateway Radio button changes.
|
||||
this.el.gatewayRadioButton.on('change', (ev) => {
|
||||
if (ev.target.checked) {
|
||||
this.activateAxo();
|
||||
} else {
|
||||
this.deactivateAxo();
|
||||
}
|
||||
});
|
||||
|
||||
this.$(document).on('updated_checkout payment_method_selected', () => {
|
||||
this.triggerGatewayChange();
|
||||
});
|
||||
|
||||
// On checkout form submitted.
|
||||
this.el.submitButton.on('click', () => {
|
||||
this.onClickSubmitButton();
|
||||
return false;
|
||||
})
|
||||
|
||||
// Click change shipping address link.
|
||||
this.el.changeShippingAddressLink.on('click', async () => {
|
||||
if (this.status.hasProfile) {
|
||||
const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector();
|
||||
|
||||
if (selectionChanged) {
|
||||
this.setShipping(selectedAddress);
|
||||
this.shippingView.refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Click change billing address link.
|
||||
this.el.changeBillingAddressLink.on('click', async () => {
|
||||
if (this.status.hasProfile) {
|
||||
this.el.changeCardLink.trigger('click');
|
||||
}
|
||||
});
|
||||
|
||||
// Click change card link.
|
||||
this.el.changeCardLink.on('click', async () => {
|
||||
const response = await this.fastlane.profile.showCardSelector();
|
||||
|
||||
if (response.selectionChanged) {
|
||||
this.setCard(response.selectedCard);
|
||||
this.setBilling({
|
||||
address: response.selectedCard.paymentSource.card.billingAddress
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Cancel "continuation" mode.
|
||||
this.el.showGatewaySelectionLink.on('click', async () => {
|
||||
this.hideGatewaySelection = false;
|
||||
this.$('.wc_payment_methods label').show();
|
||||
this.cardView.refresh();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
rerender() {
|
||||
/**
|
||||
* active | 0 1 1 1
|
||||
* validEmail | * 0 1 1
|
||||
* hasProfile | * * 0 1
|
||||
* --------------------------------
|
||||
* defaultSubmitButton | 1 0 0 0
|
||||
* defaultEmailField | 1 0 0 0
|
||||
* defaultFormFields | 1 0 1 0
|
||||
* extraFormFields | 0 0 0 1
|
||||
* axoEmailField | 0 1 0 0
|
||||
* axoProfileViews | 0 0 0 1
|
||||
* axoPaymentContainer | 0 0 1 1
|
||||
* axoSubmitButton | 0 0 1 1
|
||||
*/
|
||||
const scenario = this.identifyScenario(
|
||||
this.status.active,
|
||||
this.status.validEmail,
|
||||
this.status.hasProfile
|
||||
);
|
||||
|
||||
log('Scenario', scenario);
|
||||
|
||||
// Reset some elements to a default status.
|
||||
this.el.watermarkContainer.hide();
|
||||
|
||||
if (scenario.defaultSubmitButton) {
|
||||
this.el.defaultSubmitButton.show();
|
||||
} else {
|
||||
this.el.defaultSubmitButton.hide();
|
||||
}
|
||||
|
||||
if (scenario.defaultEmailField) {
|
||||
this.el.fieldBillingEmail.show();
|
||||
} else {
|
||||
this.el.fieldBillingEmail.hide();
|
||||
}
|
||||
|
||||
if (scenario.defaultFormFields) {
|
||||
this.el.customerDetails.show();
|
||||
} else {
|
||||
this.el.customerDetails.hide();
|
||||
}
|
||||
|
||||
if (scenario.extraFormFields) {
|
||||
this.el.customerDetails.show();
|
||||
// Hiding of unwanted will be handled by the axoProfileViews handler.
|
||||
}
|
||||
|
||||
if (scenario.axoEmailField) {
|
||||
this.showAxoEmailField();
|
||||
this.el.watermarkContainer.show();
|
||||
|
||||
// Move watermark to after email.
|
||||
this.$(this.el.fieldBillingEmail.selector).append(
|
||||
this.$(this.el.watermarkContainer.selector)
|
||||
);
|
||||
|
||||
} else {
|
||||
this.el.emailWidgetContainer.hide();
|
||||
if (!scenario.defaultEmailField) {
|
||||
this.el.fieldBillingEmail.hide();
|
||||
}
|
||||
}
|
||||
|
||||
if (scenario.axoProfileViews) {
|
||||
this.shippingView.activate();
|
||||
this.billingView.activate();
|
||||
this.cardView.activate();
|
||||
|
||||
// Move watermark to after shipping.
|
||||
this.$(this.el.shippingAddressContainer.selector).after(
|
||||
this.$(this.el.watermarkContainer.selector)
|
||||
);
|
||||
|
||||
this.el.watermarkContainer.show();
|
||||
|
||||
} else {
|
||||
this.shippingView.deactivate();
|
||||
this.billingView.deactivate();
|
||||
this.cardView.deactivate();
|
||||
}
|
||||
|
||||
if (scenario.axoPaymentContainer) {
|
||||
this.el.paymentContainer.show();
|
||||
} else {
|
||||
this.el.paymentContainer.hide();
|
||||
}
|
||||
|
||||
if (scenario.axoSubmitButton) {
|
||||
this.el.submitButtonContainer.show();
|
||||
} else {
|
||||
this.el.submitButtonContainer.hide();
|
||||
}
|
||||
|
||||
this.ensureBillingFieldsConsistency();
|
||||
this.ensureShippingFieldsConsistency();
|
||||
}
|
||||
|
||||
identifyScenario(active, validEmail, hasProfile) {
|
||||
let response = {
|
||||
defaultSubmitButton: false,
|
||||
defaultEmailField: false,
|
||||
defaultFormFields: false,
|
||||
extraFormFields: false,
|
||||
axoEmailField: false,
|
||||
axoProfileViews: false,
|
||||
axoPaymentContainer: false,
|
||||
axoSubmitButton: false,
|
||||
}
|
||||
|
||||
if (active && validEmail && hasProfile) {
|
||||
response.extraFormFields = true;
|
||||
response.axoProfileViews = true;
|
||||
response.axoPaymentContainer = true;
|
||||
response.axoSubmitButton = true;
|
||||
return response;
|
||||
}
|
||||
if (active && validEmail && !hasProfile) {
|
||||
response.defaultFormFields = true;
|
||||
response.axoEmailField = true;
|
||||
response.axoPaymentContainer = true;
|
||||
response.axoSubmitButton = true;
|
||||
return response;
|
||||
}
|
||||
if (active && !validEmail) {
|
||||
response.axoEmailField = true;
|
||||
return response;
|
||||
}
|
||||
if (!active) {
|
||||
response.defaultSubmitButton = true;
|
||||
response.defaultEmailField = true;
|
||||
response.defaultFormFields = true;
|
||||
return response;
|
||||
}
|
||||
throw new Error('Invalid scenario.');
|
||||
}
|
||||
|
||||
ensureBillingFieldsConsistency() {
|
||||
const $billingFields = this.$('.woocommerce-billing-fields .form-row:visible');
|
||||
const $billingHeaders = this.$('.woocommerce-billing-fields h3');
|
||||
if (this.billingView.isActive()) {
|
||||
if ($billingFields.length) {
|
||||
$billingHeaders.show();
|
||||
} else {
|
||||
$billingHeaders.hide();
|
||||
}
|
||||
} else {
|
||||
$billingHeaders.show();
|
||||
}
|
||||
}
|
||||
|
||||
ensureShippingFieldsConsistency() {
|
||||
const $shippingFields = this.$('.woocommerce-shipping-fields .form-row:visible');
|
||||
const $shippingHeaders = this.$('.woocommerce-shipping-fields h3');
|
||||
if (this.shippingView.isActive()) {
|
||||
if ($shippingFields.length) {
|
||||
$shippingHeaders.show();
|
||||
} else {
|
||||
$shippingHeaders.hide();
|
||||
}
|
||||
} else {
|
||||
$shippingHeaders.show();
|
||||
}
|
||||
}
|
||||
|
||||
showAxoEmailField() {
|
||||
if (this.status.useEmailWidget) {
|
||||
this.el.emailWidgetContainer.show();
|
||||
this.el.fieldBillingEmail.hide();
|
||||
} else {
|
||||
this.el.emailWidgetContainer.hide();
|
||||
this.el.fieldBillingEmail.show();
|
||||
}
|
||||
}
|
||||
|
||||
setStatus(key, value) {
|
||||
this.status[key] = value;
|
||||
|
||||
log('Status updated', JSON.parse(JSON.stringify(this.status)));
|
||||
|
||||
this.rerender();
|
||||
}
|
||||
|
||||
activateAxo() {
|
||||
this.initPlacements();
|
||||
this.initFastlane();
|
||||
this.setStatus('active', true);
|
||||
|
||||
const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
|
||||
if (emailInput && this.lastEmailCheckedIdentity !== emailInput.value) {
|
||||
this.onChangeEmail();
|
||||
}
|
||||
}
|
||||
|
||||
deactivateAxo() {
|
||||
this.setStatus('active', false);
|
||||
}
|
||||
|
||||
initPlacements() {
|
||||
const wrapper = this.el.axoCustomerDetails;
|
||||
|
||||
// Customer details container.
|
||||
if (!document.querySelector(wrapper.selector)) {
|
||||
document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', `
|
||||
<div id="${wrapper.id}" class="${wrapper.className}"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
const wrapperElement = document.querySelector(wrapper.selector);
|
||||
|
||||
// Billing view container.
|
||||
const bc = this.el.billingAddressContainer;
|
||||
if (!document.querySelector(bc.selector)) {
|
||||
wrapperElement.insertAdjacentHTML('beforeend', `
|
||||
<div id="${bc.id}" class="${bc.className}"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
// Shipping view container.
|
||||
const sc = this.el.shippingAddressContainer;
|
||||
if (!document.querySelector(sc.selector)) {
|
||||
wrapperElement.insertAdjacentHTML('beforeend', `
|
||||
<div id="${sc.id}" class="${sc.className}"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
// Watermark container
|
||||
const wc = this.el.watermarkContainer;
|
||||
if (!document.querySelector(wc.selector)) {
|
||||
this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
|
||||
this.emailInput.insertAdjacentHTML('afterend', `
|
||||
<div class="${wc.className}" id="${wc.id}"></div>
|
||||
`);
|
||||
}
|
||||
|
||||
// Payment container
|
||||
const pc = this.el.paymentContainer;
|
||||
if (!document.querySelector(pc.selector)) {
|
||||
const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway');
|
||||
gatewayPaymentContainer.insertAdjacentHTML('beforeend', `
|
||||
<div id="${pc.id}" class="${pc.className} hidden">
|
||||
<div id="${pc.id}-form" class="${pc.className}-form"></div>
|
||||
<div id="${pc.id}-details" class="${pc.className}-details"></div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
if (this.useEmailWidget()) {
|
||||
|
||||
// Display email widget.
|
||||
const ec = this.el.emailWidgetContainer;
|
||||
if (!document.querySelector(ec.selector)) {
|
||||
wrapperElement.insertAdjacentHTML('afterbegin', `
|
||||
<div id="${ec.id}" class="${ec.className}">
|
||||
--- EMAIL WIDGET PLACEHOLDER ---
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Move email to the AXO container.
|
||||
let emailRow = document.querySelector(this.el.fieldBillingEmail.selector);
|
||||
wrapperElement.prepend(emailRow);
|
||||
emailRow.querySelector('input').focus();
|
||||
}
|
||||
}
|
||||
|
||||
async initFastlane() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
this.initialized = true;
|
||||
|
||||
await this.connect();
|
||||
this.renderWatermark();
|
||||
this.watchEmail();
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (this.axoConfig.environment.is_sandbox) {
|
||||
window.localStorage.setItem('axoEnv', 'sandbox');
|
||||
}
|
||||
|
||||
await this.fastlane.connect({
|
||||
locale: this.locale,
|
||||
styles: this.styles
|
||||
});
|
||||
|
||||
this.fastlane.setLocale('en_us');
|
||||
}
|
||||
|
||||
triggerGatewayChange() {
|
||||
this.el.gatewayRadioButton.trigger('change');
|
||||
}
|
||||
|
||||
async renderWatermark() {
|
||||
(await this.fastlane.FastlaneWatermarkComponent({
|
||||
includeAdditionalInfo: true
|
||||
})).render(this.el.watermarkContainer.selector);
|
||||
}
|
||||
|
||||
watchEmail() {
|
||||
|
||||
if (this.useEmailWidget()) {
|
||||
|
||||
// TODO
|
||||
|
||||
} else {
|
||||
|
||||
this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input');
|
||||
this.emailInput.addEventListener('change', async ()=> {
|
||||
this.onChangeEmail();
|
||||
});
|
||||
|
||||
if (this.emailInput.value) {
|
||||
this.onChangeEmail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onChangeEmail () {
|
||||
this.clearData();
|
||||
|
||||
if (!this.status.active) {
|
||||
log('Email checking skipped, AXO not active.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.emailInput) {
|
||||
log('Email field not initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
log('Email changed: ' + (this.emailInput ? this.emailInput.value : '<empty>'));
|
||||
|
||||
this.$(this.el.paymentContainer.selector + '-detail').html('');
|
||||
this.$(this.el.paymentContainer.selector + '-form').html('');
|
||||
|
||||
this.setStatus('validEmail', false);
|
||||
this.setStatus('hasProfile', false);
|
||||
|
||||
this.hideGatewaySelection = false;
|
||||
|
||||
this.lastEmailCheckedIdentity = this.emailInput.value;
|
||||
|
||||
if (!this.emailInput.value || !this.emailInput.checkValidity()) {
|
||||
log('The email address is not valid.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.email = this.emailInput.value;
|
||||
this.billingView.setData(this.data);
|
||||
|
||||
if (!this.fastlane.identity) {
|
||||
log('Not initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
PayPalInsights.trackSubmitCheckoutEmail({
|
||||
page_type: 'checkout'
|
||||
});
|
||||
|
||||
const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value);
|
||||
|
||||
if (lookupResponse.customerContextId) {
|
||||
// Email is associated with a Connect profile or a PayPal member.
|
||||
// Authenticate the customer to get access to their profile.
|
||||
log('Email is associated with a Connect profile or a PayPal member');
|
||||
|
||||
const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId);
|
||||
|
||||
log('AuthResponse', authResponse);
|
||||
|
||||
if (authResponse.authenticationState === 'succeeded') {
|
||||
log(JSON.stringify(authResponse));
|
||||
|
||||
// Add addresses
|
||||
this.setShipping(authResponse.profileData.shippingAddress);
|
||||
this.setBilling({
|
||||
address: authResponse.profileData.card.paymentSource.card.billingAddress
|
||||
});
|
||||
this.setCard(authResponse.profileData.card);
|
||||
|
||||
this.setStatus('validEmail', true);
|
||||
this.setStatus('hasProfile', true);
|
||||
|
||||
this.hideGatewaySelection = true;
|
||||
this.$('.wc_payment_methods label').hide();
|
||||
|
||||
this.rerender();
|
||||
|
||||
} else {
|
||||
// authentication failed or canceled by the customer
|
||||
log("Authentication Failed")
|
||||
}
|
||||
|
||||
} else {
|
||||
// No profile found with this email address.
|
||||
// This is a guest customer.
|
||||
log('No profile found with this email address.');
|
||||
|
||||
this.setStatus('validEmail', true);
|
||||
this.setStatus('hasProfile', false);
|
||||
|
||||
this.cardComponent = (await this.fastlane.FastlaneCardComponent(
|
||||
this.cardComponentData()
|
||||
)).render(this.el.paymentContainer.selector + '-form');
|
||||
}
|
||||
}
|
||||
|
||||
clearData() {
|
||||
this.data = {
|
||||
email: null,
|
||||
billing: null,
|
||||
shipping: null,
|
||||
card: null,
|
||||
};
|
||||
}
|
||||
|
||||
setShipping(shipping) {
|
||||
this.data.shipping = shipping;
|
||||
this.shippingView.setData(this.data);
|
||||
}
|
||||
|
||||
setBilling(billing) {
|
||||
this.data.billing = billing;
|
||||
this.billingView.setData(this.data);
|
||||
}
|
||||
|
||||
setCard(card) {
|
||||
this.data.card = card;
|
||||
this.cardView.setData(this.data);
|
||||
}
|
||||
|
||||
onClickSubmitButton() {
|
||||
// TODO: validate data.
|
||||
|
||||
if (this.data.card) { // Ryan flow
|
||||
log('Ryan flow.');
|
||||
|
||||
this.$('#ship-to-different-address-checkbox').prop('checked', 'checked');
|
||||
|
||||
let data = {};
|
||||
this.billingView.toSubmitData(data);
|
||||
this.shippingView.toSubmitData(data);
|
||||
this.cardView.toSubmitData(data);
|
||||
|
||||
this.submit(this.data.card.id, data);
|
||||
|
||||
} else { // Gary flow
|
||||
log('Gary flow.');
|
||||
|
||||
try {
|
||||
this.cardComponent.getPaymentToken(
|
||||
this.tokenizeData()
|
||||
).then((response) => {
|
||||
this.submit(response.id);
|
||||
});
|
||||
} catch (e) {
|
||||
log('Error tokenizing.');
|
||||
alert('Error tokenizing data.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cardComponentData() {
|
||||
return {
|
||||
fields: {
|
||||
cardholderName: {} // optionally pass this to show the card holder name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokenizeData() {
|
||||
return {
|
||||
name: {
|
||||
fullName: this.billingView.fullName()
|
||||
},
|
||||
billingAddress: {
|
||||
addressLine1: this.billingView.inputValue('street1'),
|
||||
addressLine2: this.billingView.inputValue('street2'),
|
||||
adminArea1: this.billingView.inputValue('city'),
|
||||
adminArea2: this.billingView.inputValue('stateCode'),
|
||||
postalCode: this.billingView.inputValue('postCode'),
|
||||
countryCode: this.billingView.inputValue('countryCode'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submit(nonce, data) {
|
||||
// Send the nonce and previously captured device data to server to complete checkout
|
||||
if (!this.el.axoNonceInput.get()) {
|
||||
this.$('form.woocommerce-checkout').append(`<input type="hidden" id="${this.el.axoNonceInput.id}" name="axo_nonce" value="" />`);
|
||||
}
|
||||
|
||||
this.el.axoNonceInput.get().value = nonce;
|
||||
|
||||
PayPalInsights.trackEndCheckout({
|
||||
amount: this.axoConfig?.insights?.amount,
|
||||
page_type: 'checkout',
|
||||
payment_method_selected: 'card',
|
||||
user_data: {
|
||||
country: 'US',
|
||||
is_store_member: false,
|
||||
}
|
||||
});
|
||||
|
||||
if (data) {
|
||||
|
||||
// Ryan flow.
|
||||
const form = document.querySelector('form.woocommerce-checkout');
|
||||
const formData = new FormData(form);
|
||||
|
||||
this.showLoading();
|
||||
|
||||
// Fill in form data.
|
||||
Object.keys(data).forEach((key) => {
|
||||
formData.set(key, data[key]);
|
||||
});
|
||||
|
||||
fetch(wc_checkout_params.checkout_url, { // TODO: maybe create a new endpoint to process_payment.
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(responseData => {
|
||||
if (responseData.result === 'failure') {
|
||||
if (responseData.messages) {
|
||||
const $notices = this.$('.woocommerce-notices-wrapper').eq(0);
|
||||
$notices.html(responseData.messages);
|
||||
this.$('html, body').animate({
|
||||
scrollTop: $notices.offset().top
|
||||
}, 500);
|
||||
}
|
||||
console.error('Failure:', responseData);
|
||||
this.hideLoading();
|
||||
return;
|
||||
}
|
||||
if (responseData.redirect) {
|
||||
window.location.href = responseData.redirect;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
this.hideLoading();
|
||||
});
|
||||
|
||||
} else {
|
||||
// Gary flow.
|
||||
this.el.defaultSubmitButton.click();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const submitContainerSelector = '.woocommerce-checkout-payment';
|
||||
jQuery('form.woocommerce-checkout').append('<div class="blockUI blockOverlay" style="z-index: 1000; border: medium; margin: 0px; padding: 0px; width: 100%; height: 100%; top: 0px; left: 0px; background: rgb(255, 255, 255); opacity: 0.6; cursor: default; position: absolute;"></div>');
|
||||
disable(submitContainerSelector);
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const submitContainerSelector = '.woocommerce-checkout-payment';
|
||||
jQuery('form.woocommerce-checkout .blockOverlay').remove();
|
||||
enable(submitContainerSelector);
|
||||
}
|
||||
|
||||
useEmailWidget() {
|
||||
return this.axoConfig?.widgets?.email === 'use_widget';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AxoManager;
|
41
modules/ppcp-axo/resources/js/Components/DomElement.js
Normal file
41
modules/ppcp-axo/resources/js/Components/DomElement.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import {setVisible} from "../../../../ppcp-button/resources/js/modules/Helper/Hiding";
|
||||
|
||||
class DomElement {
|
||||
|
||||
constructor(config) {
|
||||
this.$ = jQuery;
|
||||
this.config = config;
|
||||
this.selector = this.config.selector;
|
||||
this.id = this.config.id || null;
|
||||
this.className = this.config.className || null;
|
||||
this.attributes = this.config.attributes || null;
|
||||
this.anchorSelector = this.config.anchorSelector || null;
|
||||
}
|
||||
|
||||
trigger(action) {
|
||||
this.$(this.selector).trigger(action);
|
||||
}
|
||||
|
||||
on(action, callable) {
|
||||
this.$(document).on(action, this.selector, callable);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$(this.selector).hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.$(this.selector).show();
|
||||
}
|
||||
|
||||
click() {
|
||||
this.get().click();
|
||||
}
|
||||
|
||||
get() {
|
||||
return document.querySelector(this.selector);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DomElement;
|
|
@ -0,0 +1,95 @@
|
|||
import DomElement from "./DomElement";
|
||||
|
||||
class DomElementCollection {
|
||||
|
||||
constructor() {
|
||||
this.gatewayRadioButton = new DomElement({
|
||||
selector: '#payment_method_ppcp-axo-gateway',
|
||||
});
|
||||
|
||||
this.defaultSubmitButton = new DomElement({
|
||||
selector: '#place_order',
|
||||
});
|
||||
|
||||
this.paymentContainer = new DomElement({
|
||||
id: 'ppcp-axo-payment-container',
|
||||
selector: '#ppcp-axo-payment-container',
|
||||
className: 'ppcp-axo-payment-container'
|
||||
});
|
||||
|
||||
this.watermarkContainer = new DomElement({
|
||||
id: 'ppcp-axo-watermark-container',
|
||||
selector: '#ppcp-axo-watermark-container',
|
||||
className: 'ppcp-axo-watermark-container'
|
||||
});
|
||||
|
||||
this.customerDetails = new DomElement({
|
||||
selector: '#customer_details > *:not(#ppcp-axo-customer-details)'
|
||||
});
|
||||
|
||||
this.axoCustomerDetails = new DomElement({
|
||||
id: 'ppcp-axo-customer-details',
|
||||
selector: '#ppcp-axo-customer-details',
|
||||
className: 'ppcp-axo-customer-details',
|
||||
anchorSelector: '#customer_details'
|
||||
});
|
||||
|
||||
this.emailWidgetContainer = new DomElement({
|
||||
id: 'ppcp-axo-email-widget',
|
||||
selector: '#ppcp-axo-email-widget',
|
||||
className: 'ppcp-axo-email-widget'
|
||||
});
|
||||
|
||||
this.shippingAddressContainer = new DomElement({
|
||||
id: 'ppcp-axo-shipping-address-container',
|
||||
selector: '#ppcp-axo-shipping-address-container',
|
||||
className: 'ppcp-axo-shipping-address-container'
|
||||
});
|
||||
|
||||
this.billingAddressContainer = new DomElement({
|
||||
id: 'ppcp-axo-billing-address-container',
|
||||
selector: '#ppcp-axo-billing-address-container',
|
||||
className: 'ppcp-axo-billing-address-container'
|
||||
});
|
||||
|
||||
this.fieldBillingEmail = new DomElement({
|
||||
selector: '#billing_email_field'
|
||||
});
|
||||
|
||||
this.submitButtonContainer = new DomElement({
|
||||
selector: '#ppcp-axo-submit-button-container',
|
||||
});
|
||||
|
||||
this.submitButton = new DomElement({
|
||||
selector: '#ppcp-axo-submit-button-container button'
|
||||
});
|
||||
|
||||
this.changeShippingAddressLink = new DomElement({
|
||||
selector: '*[data-ppcp-axo-change-shipping-address]',
|
||||
attributes: 'data-ppcp-axo-change-shipping-address',
|
||||
});
|
||||
|
||||
this.changeBillingAddressLink = new DomElement({
|
||||
selector: '*[data-ppcp-axo-change-billing-address]',
|
||||
attributes: 'data-ppcp-axo-change-billing-address',
|
||||
});
|
||||
|
||||
this.changeCardLink = new DomElement({
|
||||
selector: '*[data-ppcp-axo-change-card]',
|
||||
attributes: 'data-ppcp-axo-change-card',
|
||||
});
|
||||
|
||||
this.showGatewaySelectionLink = new DomElement({
|
||||
selector: '*[data-ppcp-axo-show-gateway-selection]',
|
||||
attributes: 'data-ppcp-axo-show-gateway-selection',
|
||||
});
|
||||
|
||||
this.axoNonceInput = new DomElement({
|
||||
id: 'ppcp-axo-nonce',
|
||||
selector: '#ppcp-axo-nonce',
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default DomElementCollection;
|
153
modules/ppcp-axo/resources/js/Components/FormFieldGroup.js
Normal file
153
modules/ppcp-axo/resources/js/Components/FormFieldGroup.js
Normal file
|
@ -0,0 +1,153 @@
|
|||
|
||||
class FormFieldGroup {
|
||||
|
||||
constructor(config) {
|
||||
this.data = {};
|
||||
|
||||
this.baseSelector = config.baseSelector;
|
||||
this.contentSelector = config.contentSelector;
|
||||
this.fields = config.fields || {};
|
||||
this.template = config.template;
|
||||
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
this.data = data;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
dataValue(fieldKey) {
|
||||
if (!fieldKey || !this.fields[fieldKey]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (typeof this.fields[fieldKey].valueCallback === 'function') {
|
||||
return this.fields[fieldKey].valueCallback(this.data);
|
||||
}
|
||||
|
||||
const path = this.fields[fieldKey].valuePath;
|
||||
|
||||
if (!path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const value = path.split('.').reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this.data);
|
||||
return value ? value : '';
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.active = true;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.active = false;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.active ? this.deactivate() : this.activate();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
let content = document.querySelector(this.contentSelector);
|
||||
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.innerHTML = '';
|
||||
|
||||
if (!this.active) {
|
||||
this.hideField(this.contentSelector);
|
||||
} else {
|
||||
this.showField(this.contentSelector);
|
||||
}
|
||||
|
||||
Object.keys(this.fields).forEach((key) => {
|
||||
const field = this.fields[key];
|
||||
|
||||
if (this.active && !field.showInput) {
|
||||
this.hideField(field.selector);
|
||||
} else {
|
||||
this.showField(field.selector);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof this.template === 'function') {
|
||||
content.innerHTML = this.template({
|
||||
value: (fieldKey) => {
|
||||
return this.dataValue(fieldKey);
|
||||
},
|
||||
isEmpty: () => {
|
||||
let isEmpty = true;
|
||||
Object.keys(this.fields).forEach((fieldKey) => {
|
||||
if (this.dataValue(fieldKey)) {
|
||||
isEmpty = false;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return isEmpty;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
showField(selector) {
|
||||
const field = document.querySelector(this.baseSelector + ' ' + selector);
|
||||
if (field) {
|
||||
field.classList.remove('ppcp-axo-field-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
hideField(selector) {
|
||||
const field = document.querySelector(this.baseSelector + ' ' + selector);
|
||||
if (field) {
|
||||
field.classList.add('ppcp-axo-field-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
inputElement(name) {
|
||||
const baseSelector = this.fields[name].selector;
|
||||
|
||||
const select = document.querySelector(baseSelector + ' select');
|
||||
if (select) {
|
||||
return select;
|
||||
}
|
||||
|
||||
const input = document.querySelector(baseSelector + ' input');
|
||||
if (input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
inputValue(name) {
|
||||
const el = this.inputElement(name);
|
||||
return el ? el.value : '';
|
||||
}
|
||||
|
||||
toSubmitData(data) {
|
||||
Object.keys(this.fields).forEach((fieldKey) => {
|
||||
const field = this.fields[fieldKey];
|
||||
|
||||
if (!field.valuePath || !field.selector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const inputElement = this.inputElement(fieldKey);
|
||||
|
||||
if (!inputElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
data[inputElement.name] = this.dataValue(fieldKey);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FormFieldGroup;
|
41
modules/ppcp-axo/resources/js/Connection/Fastlane.js
Normal file
41
modules/ppcp-axo/resources/js/Connection/Fastlane.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
class Fastlane {
|
||||
|
||||
construct() {
|
||||
this.connection = null;
|
||||
this.identity = null;
|
||||
this.profile = null;
|
||||
this.FastlaneCardComponent = null;
|
||||
this.FastlanePaymentComponent = null;
|
||||
this.FastlaneWatermarkComponent = null;
|
||||
}
|
||||
|
||||
connect(config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.paypal.Fastlane(config)
|
||||
.then((result) => {
|
||||
this.init(result);
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
init(connection) {
|
||||
this.connection = connection;
|
||||
this.identity = this.connection.identity;
|
||||
this.profile = this.connection.profile;
|
||||
this.FastlaneCardComponent = this.connection.FastlaneCardComponent;
|
||||
this.FastlanePaymentComponent = this.connection.FastlanePaymentComponent;
|
||||
this.FastlaneWatermarkComponent = this.connection.FastlaneWatermarkComponent
|
||||
}
|
||||
|
||||
setLocale(locale) {
|
||||
this.connection.setLocale(locale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Fastlane;
|
4
modules/ppcp-axo/resources/js/Helper/Debug.js
Normal file
4
modules/ppcp-axo/resources/js/Helper/Debug.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
export function log(...args) {
|
||||
//console.log('[AXO] ', ...args);
|
||||
}
|
58
modules/ppcp-axo/resources/js/Insights/PayPalInsights.js
Normal file
58
modules/ppcp-axo/resources/js/Insights/PayPalInsights.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
|
||||
class PayPalInsights {
|
||||
|
||||
constructor() {
|
||||
window.paypalInsightDataLayer = window.paypalInsightDataLayer || [];
|
||||
document.paypalInsight = () => {
|
||||
paypalInsightDataLayer.push(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {PayPalInsights}
|
||||
*/
|
||||
static init() {
|
||||
if (!PayPalInsights.instance) {
|
||||
PayPalInsights.instance = new PayPalInsights();
|
||||
}
|
||||
return PayPalInsights.instance;
|
||||
}
|
||||
|
||||
static track(eventName, data) {
|
||||
PayPalInsights.init();
|
||||
paypalInsight('event', eventName, data);
|
||||
}
|
||||
|
||||
static config (clientId, data) {
|
||||
PayPalInsights.init();
|
||||
paypalInsight('config', clientId, data);
|
||||
}
|
||||
|
||||
static setSessionId (sessionId) {
|
||||
PayPalInsights.init();
|
||||
paypalInsight('set', { session_id: sessionId });
|
||||
}
|
||||
|
||||
static trackJsLoad () {
|
||||
PayPalInsights.track('js_load', { timestamp: Date.now() });
|
||||
}
|
||||
|
||||
static trackBeginCheckout (data) {
|
||||
PayPalInsights.track('begin_checkout', data);
|
||||
}
|
||||
|
||||
static trackSubmitCheckoutEmail (data) {
|
||||
PayPalInsights.track('submit_checkout_email', data);
|
||||
}
|
||||
|
||||
static trackSelectPaymentMethod (data) {
|
||||
PayPalInsights.track('select_payment_method', data);
|
||||
}
|
||||
|
||||
static trackEndCheckout (data) {
|
||||
PayPalInsights.track('end_checkout', data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default PayPalInsights;
|
126
modules/ppcp-axo/resources/js/Views/BillingView.js
Normal file
126
modules/ppcp-axo/resources/js/Views/BillingView.js
Normal file
|
@ -0,0 +1,126 @@
|
|||
import FormFieldGroup from "../Components/FormFieldGroup";
|
||||
|
||||
class BillingView {
|
||||
|
||||
constructor(selector, elements) {
|
||||
this.el = elements;
|
||||
|
||||
this.group = new FormFieldGroup({
|
||||
baseSelector: '.woocommerce-checkout',
|
||||
contentSelector: selector,
|
||||
template: (data) => {
|
||||
const valueOfSelect = (selectSelector, key) => {
|
||||
if (!key) {
|
||||
return '';
|
||||
}
|
||||
const selectElement = document.querySelector(selectSelector);
|
||||
|
||||
if (!selectElement) {
|
||||
return key;
|
||||
}
|
||||
|
||||
const option = selectElement.querySelector(`option[value="${key}"]`);
|
||||
return option ? option.textContent : key;
|
||||
}
|
||||
|
||||
if (data.isEmpty()) {
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3>Billing <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
|
||||
<div>Please fill in your billing details.</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3>Billing <a href="javascript:void(0)" ${this.el.changeBillingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
|
||||
<div>${data.value('email')}</div>
|
||||
<div>${data.value('company')}</div>
|
||||
<div>${data.value('firstName')} ${data.value('lastName')}</div>
|
||||
<div>${data.value('street1')}</div>
|
||||
<div>${data.value('street2')}</div>
|
||||
<div>${data.value('postCode')} ${data.value('city')}</div>
|
||||
<div>${valueOfSelect('#billing_state', data.value('stateCode'))}</div>
|
||||
<div>${valueOfSelect('#billing_country', data.value('countryCode'))}</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
fields: {
|
||||
email: {
|
||||
'valuePath': 'email',
|
||||
},
|
||||
firstName: {
|
||||
'selector': '#billing_first_name_field',
|
||||
'valuePath': null
|
||||
},
|
||||
lastName: {
|
||||
'selector': '#billing_last_name_field',
|
||||
'valuePath': null
|
||||
},
|
||||
street1: {
|
||||
'selector': '#billing_address_1_field',
|
||||
'valuePath': 'billing.address.addressLine1',
|
||||
},
|
||||
street2: {
|
||||
'selector': '#billing_address_2_field',
|
||||
'valuePath': null
|
||||
},
|
||||
postCode: {
|
||||
'selector': '#billing_postcode_field',
|
||||
'valuePath': 'billing.address.postalCode',
|
||||
},
|
||||
city: {
|
||||
'selector': '#billing_city_field',
|
||||
'valuePath': 'billing.address.adminArea2',
|
||||
},
|
||||
stateCode: {
|
||||
'selector': '#billing_state_field',
|
||||
'valuePath': 'billing.address.adminArea1',
|
||||
},
|
||||
countryCode: {
|
||||
'selector': '#billing_country_field',
|
||||
'valuePath': 'billing.address.countryCode',
|
||||
},
|
||||
company: {
|
||||
'selector': '#billing_company_field',
|
||||
'valuePath': null,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.group.active;
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.group.activate();
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.group.deactivate();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.group.refresh();
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
this.group.setData(data);
|
||||
}
|
||||
|
||||
inputValue(name) {
|
||||
return this.group.inputValue(name);
|
||||
}
|
||||
|
||||
fullName() {
|
||||
return `${this.inputValue('firstName')} ${this.inputValue('lastName')}`.trim();
|
||||
}
|
||||
|
||||
toSubmitData(data) {
|
||||
return this.group.toSubmitData(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default BillingView;
|
114
modules/ppcp-axo/resources/js/Views/CardView.js
Normal file
114
modules/ppcp-axo/resources/js/Views/CardView.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
import FormFieldGroup from "../Components/FormFieldGroup";
|
||||
|
||||
class CardView {
|
||||
|
||||
constructor(selector, elements, manager) {
|
||||
this.el = elements;
|
||||
this.manager = manager;
|
||||
|
||||
this.group = new FormFieldGroup({
|
||||
baseSelector: '.ppcp-axo-payment-container',
|
||||
contentSelector: selector,
|
||||
template: (data) => {
|
||||
const selectOtherPaymentMethod = () => {
|
||||
if (!this.manager.hideGatewaySelection) {
|
||||
return '';
|
||||
}
|
||||
return `<p style="margin-top: 40px; text-align: center;"><a href="javascript:void(0)" ${this.el.showGatewaySelectionLink.attributes}>Select other payment method</a></p>`;
|
||||
};
|
||||
|
||||
if (data.isEmpty()) {
|
||||
return `
|
||||
<div style="margin-bottom: 20px; text-align: center;">
|
||||
<div style="border:2px solid #cccccc; border-radius: 10px; padding: 26px 20px; margin-bottom: 20px; background-color:#f6f6f6">
|
||||
<div>Please fill in your card details.</div>
|
||||
</div>
|
||||
<h4><a href="javascript:void(0)" ${this.el.changeCardLink.attributes}>Add card details</a></h4>
|
||||
${selectOtherPaymentMethod()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const expiry = data.value('expiry').split('-');
|
||||
|
||||
const cardIcons = {
|
||||
'VISA': 'visa-dark.svg',
|
||||
'MASTER_CARD': 'mastercard-dark.svg',
|
||||
'AMEX': 'amex.svg',
|
||||
'DISCOVER': 'discover.svg',
|
||||
};
|
||||
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3>Card Details <a href="javascript:void(0)" ${this.el.changeCardLink.attributes} style="margin-left: 20px;">Edit</a></h3>
|
||||
<div style="border:2px solid #cccccc; border-radius: 10px; padding: 16px 20px; background-color:#f6f6f6">
|
||||
<div style="float: right;">
|
||||
<img
|
||||
class="ppcp-card-icon"
|
||||
title="${data.value('brand')}"
|
||||
src="/wp-content/plugins/woocommerce-paypal-payments/modules/ppcp-wc-gateway/assets/images/${cardIcons[data.value('brand')]}"
|
||||
alt="${data.value('brand')}"
|
||||
>
|
||||
</div>
|
||||
<div style="font-family: monospace; font-size: 1rem; margin-top: 10px;">${data.value('lastDigits') ? '**** **** **** ' + data.value('lastDigits'): ''}</div>
|
||||
<div>${expiry[1]}/${expiry[0]}</div>
|
||||
<div style="text-transform: uppercase">${data.value('name')}</div>
|
||||
</div>
|
||||
${selectOtherPaymentMethod()}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
fields: {
|
||||
brand: {
|
||||
'valuePath': 'card.paymentSource.card.brand',
|
||||
},
|
||||
expiry: {
|
||||
'valuePath': 'card.paymentSource.card.expiry',
|
||||
},
|
||||
lastDigits: {
|
||||
'valuePath': 'card.paymentSource.card.lastDigits',
|
||||
},
|
||||
name: {
|
||||
'valuePath': 'card.paymentSource.card.name',
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.group.activate();
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.group.deactivate();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.group.refresh();
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
this.group.setData(data);
|
||||
}
|
||||
|
||||
toSubmitData(data) {
|
||||
const name = this.group.dataValue('name');
|
||||
const { firstName, lastName } = this.splitName(name);
|
||||
|
||||
data['billing_first_name'] = firstName;
|
||||
data['billing_last_name'] = lastName ? lastName : firstName;
|
||||
|
||||
return this.group.toSubmitData(data);
|
||||
}
|
||||
|
||||
splitName(fullName) {
|
||||
let nameParts = fullName.trim().split(' ');
|
||||
let firstName = nameParts[0];
|
||||
let lastName = nameParts.length > 1 ? nameParts[nameParts.length - 1] : '';
|
||||
|
||||
return { firstName, lastName };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CardView;
|
134
modules/ppcp-axo/resources/js/Views/ShippingView.js
Normal file
134
modules/ppcp-axo/resources/js/Views/ShippingView.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
import FormFieldGroup from "../Components/FormFieldGroup";
|
||||
|
||||
class ShippingView {
|
||||
|
||||
constructor(selector, elements) {
|
||||
this.el = elements;
|
||||
|
||||
this.group = new FormFieldGroup({
|
||||
baseSelector: '.woocommerce-checkout',
|
||||
contentSelector: selector,
|
||||
template: (data) => {
|
||||
const valueOfSelect = (selectSelector, key) => {
|
||||
if (!key) {
|
||||
return '';
|
||||
}
|
||||
const selectElement = document.querySelector(selectSelector);
|
||||
|
||||
if (!selectElement) {
|
||||
return key;
|
||||
}
|
||||
|
||||
const option = selectElement.querySelector(`option[value="${key}"]`);
|
||||
return option ? option.textContent : key;
|
||||
}
|
||||
|
||||
if (data.isEmpty()) {
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3>Shipping <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
|
||||
<div>Please fill in your shipping details.</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h3>Shipping <a href="javascript:void(0)" ${this.el.changeShippingAddressLink.attributes} style="margin-left: 20px;">Edit</a></h3>
|
||||
<div>${data.value('company')}</div>
|
||||
<div>${data.value('firstName')} ${data.value('lastName')}</div>
|
||||
<div>${data.value('street1')}</div>
|
||||
<div>${data.value('street2')}</div>
|
||||
<div>${data.value('postCode')} ${data.value('city')}</div>
|
||||
<div>${valueOfSelect('#shipping_state', data.value('stateCode'))}</div>
|
||||
<div>${valueOfSelect('#shipping_country', data.value('countryCode'))}</div>
|
||||
<div>${data.value('phone')}</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
fields: {
|
||||
firstName: {
|
||||
'key': 'firstName',
|
||||
'selector': '#shipping_first_name_field',
|
||||
'valuePath': 'shipping.name.firstName',
|
||||
},
|
||||
lastName: {
|
||||
'selector': '#shipping_last_name_field',
|
||||
'valuePath': 'shipping.name.lastName',
|
||||
},
|
||||
street1: {
|
||||
'selector': '#shipping_address_1_field',
|
||||
'valuePath': 'shipping.address.addressLine1',
|
||||
},
|
||||
street2: {
|
||||
'selector': '#shipping_address_2_field',
|
||||
'valuePath': null
|
||||
},
|
||||
postCode: {
|
||||
'selector': '#shipping_postcode_field',
|
||||
'valuePath': 'shipping.address.postalCode',
|
||||
},
|
||||
city: {
|
||||
'selector': '#shipping_city_field',
|
||||
'valuePath': 'shipping.address.adminArea2',
|
||||
},
|
||||
stateCode: {
|
||||
'selector': '#shipping_state_field',
|
||||
'valuePath': 'shipping.address.adminArea1',
|
||||
},
|
||||
countryCode: {
|
||||
'selector': '#shipping_country_field',
|
||||
'valuePath': 'shipping.address.countryCode',
|
||||
},
|
||||
company: {
|
||||
'selector': '#shipping_company_field',
|
||||
'valuePath': null,
|
||||
},
|
||||
shipDifferentAddress: {
|
||||
'selector': '#ship-to-different-address',
|
||||
'valuePath': null,
|
||||
},
|
||||
phone: {
|
||||
//'selector': '#billing_phone_field', // There is no shipping phone field.
|
||||
'valueCallback': function (data) {
|
||||
let phone = '';
|
||||
const cc = data?.shipping?.phoneNumber?.countryCode;
|
||||
const number = data?.shipping?.phoneNumber?.nationalNumber;
|
||||
|
||||
if (cc) {
|
||||
phone = `+${cc} `;
|
||||
}
|
||||
phone += number;
|
||||
return phone;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.group.active;
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.group.activate();
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.group.deactivate();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.group.refresh();
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
this.group.setData(data);
|
||||
}
|
||||
|
||||
toSubmitData(data) {
|
||||
return this.group.toSubmitData(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ShippingView;
|
33
modules/ppcp-axo/resources/js/boot.js
Normal file
33
modules/ppcp-axo/resources/js/boot.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import AxoManager from "./AxoManager";
|
||||
import {loadPaypalScript} from "../../../ppcp-button/resources/js/modules/Helper/ScriptLoading";
|
||||
|
||||
(function ({
|
||||
axoConfig,
|
||||
ppcpConfig,
|
||||
jQuery
|
||||
}) {
|
||||
|
||||
const bootstrap = () => {
|
||||
new AxoManager(axoConfig, ppcpConfig);
|
||||
}
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
if (!typeof (PayPalCommerceGateway)) {
|
||||
console.error('AXO could not be configured.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load PayPal
|
||||
loadPaypalScript(ppcpConfig, () => {
|
||||
bootstrap();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
})({
|
||||
axoConfig: window.wc_ppcp_axo,
|
||||
ppcpConfig: window.PayPalCommerceGateway,
|
||||
jQuery: window.jQuery
|
||||
});
|
117
modules/ppcp-axo/services.php
Normal file
117
modules/ppcp-axo/services.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
/**
|
||||
* The Axo module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
return array(
|
||||
|
||||
// If AXO can be configured.
|
||||
'axo.eligible' => static function ( ContainerInterface $container ): bool {
|
||||
$apm_applies = $container->get( 'axo.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency() && $apm_applies->for_settings();
|
||||
},
|
||||
|
||||
'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
return new ApmApplies(
|
||||
$container->get( 'axo.supported-country-currency-matrix' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
|
||||
// If AXO is configured and onboarded.
|
||||
'axo.available' => static function ( ContainerInterface $container ): bool {
|
||||
return true;
|
||||
},
|
||||
|
||||
'axo.url' => static function ( ContainerInterface $container ): string {
|
||||
$path = realpath( __FILE__ );
|
||||
if ( false === $path ) {
|
||||
return '';
|
||||
}
|
||||
return plugins_url(
|
||||
'/modules/ppcp-axo/',
|
||||
dirname( $path, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
|
||||
'axo.manager' => static function ( ContainerInterface $container ): AxoManager {
|
||||
return new AxoManager(
|
||||
$container->get( 'axo.url' ),
|
||||
$container->get( 'ppcp.asset-version' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'onboarding.environment' ),
|
||||
$container->get( 'wcgateway.settings.status' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway {
|
||||
return new AxoGateway(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.url' ),
|
||||
$container->get( 'wcgateway.order-processor' ),
|
||||
$container->get( 'axo.card_icons' ),
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.factory.purchase-unit' ),
|
||||
$container->get( 'api.factory.shipping-preference' ),
|
||||
$container->get( 'wcgateway.transaction-url-provider' ),
|
||||
$container->get( 'onboarding.environment' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'axo.card_icons' => static function ( ContainerInterface $container ): array {
|
||||
return array(
|
||||
array(
|
||||
'title' => 'Visa',
|
||||
'file' => 'visa-dark.svg',
|
||||
),
|
||||
array(
|
||||
'title' => 'MasterCard',
|
||||
'file' => 'mastercard-dark.svg',
|
||||
),
|
||||
array(
|
||||
'title' => 'American Express',
|
||||
'file' => 'amex.svg',
|
||||
),
|
||||
array(
|
||||
'title' => 'Discover',
|
||||
'file' => 'discover.svg',
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for AXO.
|
||||
*/
|
||||
'axo.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for AXO.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_axo_supported_country_currency_matrix',
|
||||
array(
|
||||
'US' => array(
|
||||
'USD',
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
);
|
210
modules/ppcp-axo/src/Assets/AxoManager.php
Normal file
210
modules/ppcp-axo/src/Assets/AxoManager.php
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
/**
|
||||
* The AXO AxoManager
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Assets
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo\Assets;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class AxoManager.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
*/
|
||||
class AxoManager {
|
||||
|
||||
/**
|
||||
* The URL to the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The assets version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* The environment object.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
private $environment;
|
||||
|
||||
/**
|
||||
* The Settings status helper.
|
||||
*
|
||||
* @var SettingsStatus
|
||||
*/
|
||||
private $settings_status;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
private $session_handler;
|
||||
|
||||
/**
|
||||
* AxoManager constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param string $version The assets version.
|
||||
* @param SessionHandler $session_handler The Session handler.
|
||||
* @param Settings $settings The Settings.
|
||||
* @param Environment $environment The environment object.
|
||||
* @param SettingsStatus $settings_status The Settings status helper.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url,
|
||||
string $version,
|
||||
SessionHandler $session_handler,
|
||||
Settings $settings,
|
||||
Environment $environment,
|
||||
SettingsStatus $settings_status,
|
||||
string $currency,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->environment = $environment;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues scripts/styles.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue() {
|
||||
|
||||
// Register styles.
|
||||
wp_register_style(
|
||||
'wc-ppcp-axo',
|
||||
untrailingslashit( $this->module_url ) . '/assets/css/styles.css',
|
||||
array(),
|
||||
$this->version
|
||||
);
|
||||
wp_enqueue_style( 'wc-ppcp-axo' );
|
||||
|
||||
// Register scripts.
|
||||
wp_register_script(
|
||||
'wc-ppcp-axo',
|
||||
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
|
||||
array(),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
wp_enqueue_script( 'wc-ppcp-axo' );
|
||||
|
||||
wp_localize_script(
|
||||
'wc-ppcp-axo',
|
||||
'wc_ppcp_axo',
|
||||
$this->script_data()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration for AXO.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function script_data() {
|
||||
return array(
|
||||
'environment' => array(
|
||||
'is_sandbox' => $this->environment->current_environment() === 'sandbox',
|
||||
),
|
||||
'widgets' => array(
|
||||
'email' => 'render',
|
||||
),
|
||||
'insights' => array(
|
||||
'enabled' => true,
|
||||
'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ),
|
||||
'session_id' =>
|
||||
substr(
|
||||
method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '',
|
||||
0,
|
||||
16
|
||||
),
|
||||
'amount' => array(
|
||||
'currency_code' => get_woocommerce_currency(),
|
||||
'value' => WC()->cart->get_total( 'numeric' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action name that PayPal AXO button will use for rendering on the checkout page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function checkout_button_renderer_hook(): string {
|
||||
/**
|
||||
* The filter returning the action name that PayPal AXO button will use for rendering on the checkout page.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_paypal_payments_checkout_axo_renderer_hook', 'woocommerce_review_order_after_submit' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the HTML for the AXO submit button.
|
||||
*/
|
||||
public function render_checkout_button(): void {
|
||||
$id = 'ppcp-axo-submit-button-container';
|
||||
|
||||
/**
|
||||
* The WC filter returning the WC order button text.
|
||||
* phpcs:disable WordPress.WP.I18n.TextDomainMismatch
|
||||
*/
|
||||
$label = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) );
|
||||
|
||||
printf(
|
||||
'<div id="%1$s" style="display: none;">
|
||||
<button type="button" class="button alt ppcp-axo-order-button">%2$s</button>
|
||||
</div>',
|
||||
esc_attr( $id ),
|
||||
esc_html( $label )
|
||||
);
|
||||
}
|
||||
|
||||
}
|
224
modules/ppcp-axo/src/AxoModule.php
Normal file
224
modules/ppcp-axo/src/AxoModule.php
Normal file
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
/**
|
||||
* The Axo module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Class AxoModule
|
||||
*/
|
||||
class AxoModule implements ModuleInterface {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
$module = $this;
|
||||
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $methods ) use ( $c ): array {
|
||||
if ( ! is_array( $methods ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$gateway = $c->get( 'axo.gateway' );
|
||||
|
||||
// Check if the module is applicable, correct country, currency, ... etc.
|
||||
if ( ! $c->get( 'axo.eligible' ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
// Add the gateway in admin area.
|
||||
if ( is_admin() ) {
|
||||
$methods[] = $gateway;
|
||||
return $methods;
|
||||
}
|
||||
|
||||
if ( is_user_logged_in() ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$methods[] = $gateway;
|
||||
return $methods;
|
||||
},
|
||||
1,
|
||||
9
|
||||
);
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
static function () use ( $c, $module ) {
|
||||
|
||||
// Check if the module is applicable, correct country, currency, ... etc.
|
||||
if ( ! $c->get( 'axo.eligible' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$manager = $c->get( 'axo.manager' );
|
||||
assert( $manager instanceof AxoManager );
|
||||
|
||||
// Enqueue frontend scripts.
|
||||
add_action(
|
||||
'wp_enqueue_scripts',
|
||||
static function () use ( $c, $manager ) {
|
||||
$smart_button = $c->get( 'button.smart-button' );
|
||||
assert( $smart_button instanceof SmartButtonInterface );
|
||||
|
||||
if ( $smart_button->should_load_ppcp_script() ) {
|
||||
$manager->enqueue();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Render submit button.
|
||||
add_action(
|
||||
$manager->checkout_button_renderer_hook(),
|
||||
static function () use ( $c, $manager ) {
|
||||
$manager->render_checkout_button();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_sdk_components_hook',
|
||||
function( $components ) {
|
||||
$components[] = 'fastlane';
|
||||
return $components;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_head',
|
||||
function () {
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
|
||||
echo '<script async src="https://www.paypalobjects.com/insights/v1/paypal-insights.sandbox.min.js"></script>';
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_localized_script_data',
|
||||
function( array $localized_script_data ) use ( $c, $module ) {
|
||||
$api = $c->get( 'api.sdk-client-token' );
|
||||
assert( $api instanceof SdkClientToken );
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
return $module->add_sdk_client_token_to_script_data( $api, $logger, $localized_script_data );
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'ppcp_onboarding_dcc_table_rows',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ( $rows, $renderer ): array {
|
||||
if ( ! is_array( $rows ) ) {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
if ( $renderer instanceof OnboardingOptionsRenderer ) {
|
||||
$rows[] = $renderer->render_table_row(
|
||||
__( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ),
|
||||
__( 'Yes', 'woocommerce-paypal-payments' ),
|
||||
__( 'Help accelerate guest checkout with PayPal\'s autofill solution.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
return $rows;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
},
|
||||
1
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds id token to localized script data.
|
||||
*
|
||||
* @param SdkClientToken $api User id token api.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param array $localized_script_data The localized script data.
|
||||
* @return array
|
||||
*/
|
||||
private function add_sdk_client_token_to_script_data(
|
||||
SdkClientToken $api,
|
||||
LoggerInterface $logger,
|
||||
array $localized_script_data
|
||||
): array {
|
||||
try {
|
||||
$target_customer_id = '';
|
||||
if ( is_user_logged_in() ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true );
|
||||
if ( ! $target_customer_id ) {
|
||||
$target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true );
|
||||
}
|
||||
}
|
||||
|
||||
$sdk_client_token = $api->sdk_client_token( $target_customer_id );
|
||||
$localized_script_data['axo'] = array(
|
||||
'sdk_client_token' => $sdk_client_token,
|
||||
);
|
||||
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger->error( $error );
|
||||
}
|
||||
|
||||
return $localized_script_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
}
|
||||
}
|
324
modules/ppcp-axo/src/Gateway/AxoGateway.php
Normal file
324
modules/ppcp-axo/src/Gateway/AxoGateway.php
Normal file
|
@ -0,0 +1,324 @@
|
|||
<?php
|
||||
/**
|
||||
* The AXO Gateway
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo\Gateway;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WC_Payment_Gateway;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
|
||||
/**
|
||||
* Class AXOGateway.
|
||||
*/
|
||||
class AxoGateway extends WC_Payment_Gateway {
|
||||
use OrderMetaTrait;
|
||||
|
||||
const ID = 'ppcp-axo-gateway';
|
||||
|
||||
/**
|
||||
* The settings.
|
||||
*
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $ppcp_settings;
|
||||
|
||||
/**
|
||||
* The WcGateway module URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $wcgateway_module_url;
|
||||
|
||||
/**
|
||||
* The processor for orders.
|
||||
*
|
||||
* @var OrderProcessor
|
||||
*/
|
||||
protected $order_processor;
|
||||
|
||||
/**
|
||||
* The card icons.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $card_icons;
|
||||
|
||||
/**
|
||||
* The order endpoint.
|
||||
*
|
||||
* @var OrderEndpoint
|
||||
*/
|
||||
protected $order_endpoint;
|
||||
|
||||
/**
|
||||
* The purchase unit factory.
|
||||
*
|
||||
* @var PurchaseUnitFactory
|
||||
*/
|
||||
protected $purchase_unit_factory;
|
||||
|
||||
/**
|
||||
* The shipping preference factory.
|
||||
*
|
||||
* @var ShippingPreferenceFactory
|
||||
*/
|
||||
protected $shipping_preference_factory;
|
||||
|
||||
/**
|
||||
* The transaction url provider.
|
||||
*
|
||||
* @var TransactionUrlProvider
|
||||
*/
|
||||
protected $transaction_url_provider;
|
||||
|
||||
/**
|
||||
* The environment.
|
||||
*
|
||||
* @var Environment
|
||||
*/
|
||||
protected $environment;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* AXOGateway constructor.
|
||||
*
|
||||
* @param ContainerInterface $ppcp_settings The settings.
|
||||
* @param string $wcgateway_module_url The WcGateway module URL.
|
||||
* @param OrderProcessor $order_processor The Order processor.
|
||||
* @param array $card_icons The card icons.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
|
||||
* @param ShippingPreferenceFactory $shipping_preference_factory The shipping preference factory.
|
||||
* @param TransactionUrlProvider $transaction_url_provider The transaction url provider.
|
||||
* @param Environment $environment The environment.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
ContainerInterface $ppcp_settings,
|
||||
string $wcgateway_module_url,
|
||||
OrderProcessor $order_processor,
|
||||
array $card_icons,
|
||||
OrderEndpoint $order_endpoint,
|
||||
PurchaseUnitFactory $purchase_unit_factory,
|
||||
ShippingPreferenceFactory $shipping_preference_factory,
|
||||
TransactionUrlProvider $transaction_url_provider,
|
||||
Environment $environment,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
|
||||
$this->ppcp_settings = $ppcp_settings;
|
||||
$this->wcgateway_module_url = $wcgateway_module_url;
|
||||
$this->order_processor = $order_processor;
|
||||
$this->card_icons = $card_icons;
|
||||
|
||||
$this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' );
|
||||
$this->method_description = __( 'Accept credit cards with Fastlane’s latest solution.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$is_axo_enabled = $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' );
|
||||
$this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' );
|
||||
|
||||
$this->title = $this->ppcp_settings->has( 'axo_gateway_title' )
|
||||
? $this->ppcp_settings->get( 'axo_gateway_title' )
|
||||
: $this->get_option( 'title', $this->method_title );
|
||||
|
||||
$this->description = $this->get_option( 'description', '' );
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_payment_gateways_' . $this->id,
|
||||
array(
|
||||
$this,
|
||||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->purchase_unit_factory = $purchase_unit_factory;
|
||||
$this->shipping_preference_factory = $shipping_preference_factory;
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->transaction_url_provider = $transaction_url_provider;
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the form fields.
|
||||
*/
|
||||
public function init_form_fields() {
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'AXO', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'no',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enable/Disable AXO payment gateway.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the order.
|
||||
*
|
||||
* @param int $order_id The WC order ID.
|
||||
* @return array
|
||||
*/
|
||||
public function process_payment( $order_id ) {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
|
||||
$payment_method_title = __( 'Credit Card (via Fastlane by PayPal)', 'woocommerce-paypal-payments' );
|
||||
|
||||
$wc_order->set_payment_method_title( $payment_method_title );
|
||||
$wc_order->save();
|
||||
|
||||
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) );
|
||||
|
||||
try {
|
||||
$shipping_preference = $this->shipping_preference_factory->from_state(
|
||||
$purchase_unit,
|
||||
'checkout'
|
||||
);
|
||||
|
||||
$payment_source_properties = new \stdClass();
|
||||
$payment_source_properties->single_use_token = $nonce;
|
||||
|
||||
$payment_source = new PaymentSource(
|
||||
'card',
|
||||
$payment_source_properties
|
||||
);
|
||||
|
||||
$order = $this->order_endpoint->create(
|
||||
array( $purchase_unit ),
|
||||
$shipping_preference,
|
||||
null,
|
||||
null,
|
||||
'',
|
||||
ApplicationContext::USER_ACTION_CONTINUE,
|
||||
'',
|
||||
array(),
|
||||
$payment_source
|
||||
);
|
||||
|
||||
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
|
||||
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$this->logger->error( $error );
|
||||
wc_add_notice( $error, 'error' );
|
||||
|
||||
$wc_order->update_status(
|
||||
'failed',
|
||||
$error
|
||||
);
|
||||
|
||||
return array(
|
||||
'result' => 'failure',
|
||||
'redirect' => wc_get_checkout_url(),
|
||||
);
|
||||
}
|
||||
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
$result = array(
|
||||
'result' => 'success',
|
||||
'redirect' => $this->get_return_url( $wc_order ),
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icons of the gateway.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_icon() {
|
||||
$icon = parent::get_icon();
|
||||
|
||||
if ( empty( $this->card_icons ) ) {
|
||||
return $icon;
|
||||
}
|
||||
|
||||
$images = array();
|
||||
foreach ( $this->card_icons as $card ) {
|
||||
$images[] = '<img
|
||||
class="ppcp-card-icon"
|
||||
title="' . $card['title'] . '"
|
||||
src="' . esc_url( $this->wcgateway_module_url ) . 'assets/images/' . $card['file'] . '"
|
||||
> ';
|
||||
}
|
||||
|
||||
return '<div class="ppcp-axo-card-icons">' . implode( '', $images ) . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction url for this gateway and given order.
|
||||
*
|
||||
* @param WC_Order $order WC order to get transaction url by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_transaction_url( $order ): string {
|
||||
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
|
||||
|
||||
return parent::get_transaction_url( $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gateway's title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_title() {
|
||||
if ( is_admin() ) {
|
||||
// $theorder and other things for retrieving the order or post info are not available
|
||||
// in the constructor, so must do it here.
|
||||
global $theorder;
|
||||
if ( $theorder instanceof WC_Order ) {
|
||||
if ( $theorder->get_payment_method() === self::ID ) {
|
||||
$payment_method_title = $theorder->get_payment_method_title();
|
||||
if ( $payment_method_title ) {
|
||||
$this->title = $payment_method_title;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::get_title();
|
||||
}
|
||||
|
||||
}
|
82
modules/ppcp-axo/src/Helper/ApmApplies.php
Normal file
82
modules/ppcp-axo/src/Helper/ApmApplies.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
/**
|
||||
* ApmApplies helper.
|
||||
* Checks if AXO is available for a given country and currency.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo\Helper
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo\Helper;
|
||||
|
||||
/**
|
||||
* Class ApmApplies
|
||||
*/
|
||||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for AXO.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* 2-letter country code of the shop.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for AXO.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether AXO can be used in the current country and the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the settings are compatible with AXO.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_settings(): bool {
|
||||
if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
29
modules/ppcp-axo/src/Helper/PropertiesDictionary.php
Normal file
29
modules/ppcp-axo/src/Helper/PropertiesDictionary.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Properties of the AXO module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Axo\Helper
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Axo\Helper;
|
||||
|
||||
/**
|
||||
* Class PropertiesDictionary
|
||||
*/
|
||||
class PropertiesDictionary {
|
||||
|
||||
/**
|
||||
* Returns the list of possible privacy options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function privacy_options(): array {
|
||||
return array(
|
||||
'yes' => __( 'Yes (Recommended)', 'woocommerce-paypal-payments' ),
|
||||
'no' => __( 'No', 'woocommerce-paypal-payments' ),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
39
modules/ppcp-axo/webpack.config.js
Normal file
39
modules/ppcp-axo/webpack.config.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
const path = require('path');
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' );
|
||||
|
||||
module.exports = {
|
||||
devtool: isProduction ? 'source-map' : 'eval-source-map',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
plugins: [ new DependencyExtractionWebpackPlugin() ],
|
||||
entry: {
|
||||
'boot': path.resolve('./resources/js/boot.js'),
|
||||
'styles': path.resolve('./resources/css/styles.scss')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'css/[name].css',
|
||||
}
|
||||
},
|
||||
{loader:'sass-loader'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
2213
modules/ppcp-axo/yarn.lock
Normal file
2213
modules/ppcp-axo/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -121,8 +121,13 @@ 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': config.scriptData.subscription_plan_id
|
||||
'plan_id': planId
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -137,7 +137,12 @@ const bootstrap = () => {
|
|||
}
|
||||
|
||||
const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart;
|
||||
if (isFreeTrial && data.fundingSource !== 'card' && ! PayPalCommerceGateway.subscription_plan_id) {
|
||||
if (
|
||||
isFreeTrial
|
||||
&& data.fundingSource !== 'card'
|
||||
&& ! PayPalCommerceGateway.subscription_plan_id
|
||||
&& ! PayPalCommerceGateway.vault_v3_enabled
|
||||
) {
|
||||
freeTrialHandler.handle();
|
||||
return actions.reject();
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -12,7 +12,7 @@ class CheckoutActionHandler {
|
|||
this.spinner = spinner;
|
||||
}
|
||||
|
||||
subscriptionsConfiguration() {
|
||||
subscriptionsConfiguration(subscription_plan_id) {
|
||||
return {
|
||||
createSubscription: async (data, actions) => {
|
||||
try {
|
||||
|
@ -22,7 +22,7 @@ class CheckoutActionHandler {
|
|||
}
|
||||
|
||||
return actions.subscription.create({
|
||||
'plan_id': this.config.subscription_plan_id
|
||||
'plan_id': subscription_plan_id
|
||||
});
|
||||
},
|
||||
onApprove: (data, actions) => {
|
||||
|
@ -144,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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,13 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => {
|
|||
scriptOptions = merge(scriptOptions, config.script_attributes);
|
||||
}
|
||||
|
||||
// Axo SDK options
|
||||
const sdkClientToken = config?.axo?.sdk_client_token;
|
||||
if(sdkClientToken) {
|
||||
scriptOptions['data-sdk-client-token'] = sdkClientToken;
|
||||
scriptOptions['data-client-metadata-id'] = 'ppcp-cm-id';
|
||||
}
|
||||
|
||||
// Load PayPal script for special case with data-client-token
|
||||
if (config.data_client_id?.set_attribute) {
|
||||
dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback, errorCallback);
|
||||
|
|
|
@ -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' )
|
||||
);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
@ -34,6 +35,9 @@ use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
|||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule;
|
||||
use WooCommerce\PayPalCommerce\PayLaterWCBlocks\PayLaterWCBlocksModule;
|
||||
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\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
|
@ -185,13 +189,6 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
private $funding_sources_without_redirect;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Session handler.
|
||||
*
|
||||
|
@ -199,6 +196,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.
|
||||
*
|
||||
|
@ -221,6 +239,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(
|
||||
|
@ -243,6 +263,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
|
||||
) {
|
||||
|
||||
|
@ -265,7 +287,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1097,10 +1121,24 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
'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(),
|
||||
|
@ -1931,8 +1969,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;
|
||||
|
@ -1941,6 +1989,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
} catch ( Exception $exception ) {
|
||||
$this->logger->error( 'Failed to get PayPal vaulted email. ' . $exception->getMessage() );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
BIN
modules/ppcp-googlepay/assets/images/googlepay.png
Normal file
BIN
modules/ppcp-googlepay/assets/images/googlepay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -39,6 +39,8 @@ return array(
|
|||
$display_manager = $container->get( 'wcgateway.display-manager' );
|
||||
assert( $display_manager instanceof DisplayManager );
|
||||
|
||||
$module_url = $container->get( 'googlepay.url' );
|
||||
|
||||
// Connection tab fields.
|
||||
$fields = $insert_after(
|
||||
$fields,
|
||||
|
@ -62,10 +64,15 @@ return array(
|
|||
$connection_link = '<a href="' . $connection_url . '" style="pointer-events: auto">';
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'allow_card_button_gateway',
|
||||
'digital_wallet_heading',
|
||||
array(
|
||||
'googlepay_button_enabled' => array(
|
||||
'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/googlepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Google Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'type' => 'checkbox',
|
||||
'class' => array( 'ppcp-grayed-out-text' ),
|
||||
'input_class' => array( 'ppcp-disabled-checkbox' ),
|
||||
|
@ -80,7 +87,7 @@ return array(
|
|||
. '</p>',
|
||||
'default' => 'yes',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
'custom_attributes' => array(
|
||||
'data-ppcp-display' => wp_json_encode(
|
||||
|
@ -93,6 +100,7 @@ return array(
|
|||
)
|
||||
),
|
||||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
@ -101,10 +109,15 @@ return array(
|
|||
// Standard Payments tab fields.
|
||||
return $insert_after(
|
||||
$fields,
|
||||
'allow_card_button_gateway',
|
||||
'digital_wallet_heading',
|
||||
array(
|
||||
'googlepay_button_enabled' => array(
|
||||
'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/googlepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Google Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Enable Google Pay button', 'woocommerce-paypal-payments' )
|
||||
. '<p class="description">'
|
||||
|
@ -117,7 +130,7 @@ return array(
|
|||
. '</p>',
|
||||
'default' => 'yes',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
'custom_attributes' => array(
|
||||
'data-ppcp-display' => wp_json_encode(
|
||||
|
@ -129,10 +142,12 @@ return array(
|
|||
->action_visible( 'googlepay_button_color' )
|
||||
->action_visible( 'googlepay_button_language' )
|
||||
->action_visible( 'googlepay_button_shipping_enabled' )
|
||||
->action_class( 'googlepay_button_enabled', 'active' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
),
|
||||
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
|
||||
),
|
||||
'googlepay_button_type' => array(
|
||||
'title' => __( 'Button Label', 'woocommerce-paypal-payments' ),
|
||||
|
@ -148,7 +163,7 @@ return array(
|
|||
'default' => 'pay',
|
||||
'options' => PropertiesDictionary::button_types(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'googlepay_button_color' => array(
|
||||
|
@ -166,7 +181,7 @@ return array(
|
|||
'default' => 'black',
|
||||
'options' => PropertiesDictionary::button_colors(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'googlepay_button_language' => array(
|
||||
|
@ -183,7 +198,7 @@ return array(
|
|||
'default' => 'en',
|
||||
'options' => PropertiesDictionary::button_languages(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
'googlepay_button_shipping_enabled' => array(
|
||||
|
@ -198,7 +213,7 @@ return array(
|
|||
'label' => __( 'Enable Google Pay shipping callback', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'no',
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'paypal',
|
||||
'gateway' => 'dcc',
|
||||
'requirements' => array(),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -28,7 +28,7 @@ class GooglepayButton {
|
|||
|
||||
this.log = function() {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
console.log('[GooglePayButton]', ...arguments);
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,6 +136,9 @@ class OnboardingOptionsRenderer {
|
|||
__( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
);
|
||||
|
||||
$basic_table_rows = apply_filters( 'ppcp_onboarding_basic_table_rows', $basic_table_rows );
|
||||
|
||||
$items[] = '
|
||||
<li>
|
||||
<label>
|
||||
|
@ -191,6 +194,9 @@ class OnboardingOptionsRenderer {
|
|||
__( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
);
|
||||
|
||||
$dcc_table_rows = apply_filters( 'ppcp_onboarding_dcc_table_rows', $dcc_table_rows, $this );
|
||||
|
||||
$items[] = '
|
||||
<li>
|
||||
<label>
|
||||
|
@ -224,7 +230,7 @@ class OnboardingOptionsRenderer {
|
|||
* @param string $note The additional description text, such as about conditions.
|
||||
* @return string
|
||||
*/
|
||||
private function render_table_row( string $header, string $value, string $tooltip = '', string $note = '' ): string {
|
||||
public function render_table_row( string $header, string $value, string $tooltip = '', string $note = '' ): string {
|
||||
$value_html = $value;
|
||||
if ( $note ) {
|
||||
$value_html .= '<br/><span class="ppcp-muted-text">' . $note . '</span>';
|
||||
|
|
|
@ -12,7 +12,7 @@ import ErrorHandler from "../../../ppcp-button/resources/js/modules/ErrorHandler
|
|||
import {cardFieldStyles} from "../../../ppcp-button/resources/js/modules/Helper/CardFieldsHelper";
|
||||
|
||||
const errorHandler = new ErrorHandler(
|
||||
PayPalCommerceGateway.labels.error.generic,
|
||||
ppcp_add_payment_method.labels.error.generic,
|
||||
document.querySelector('.woocommerce-notices-wrapper')
|
||||
);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\SavePaymentMethods;
|
|||
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Helper\SavePaymentMethodsApplies;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
||||
|
@ -811,4 +812,10 @@ return array(
|
|||
$container->get( 'vaulting.wc-payment-tokens' )
|
||||
);
|
||||
},
|
||||
'save-payment-methods.endpoint.create-payment-token-for-guest' => static function ( ContainerInterface $container ): CreatePaymentTokenForGuest {
|
||||
return new CreatePaymentTokenForGuest(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'api.endpoint.payment-method-tokens' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -94,7 +94,7 @@ class CreatePaymentToken implements EndpointInterface {
|
|||
)
|
||||
);
|
||||
|
||||
$result = $this->payment_method_tokens_endpoint->payment_tokens( $payment_source );
|
||||
$result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source );
|
||||
|
||||
if ( is_user_logged_in() && isset( $result->customer->id ) ) {
|
||||
$current_user_id = get_current_user_id();
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* Create payment token for guest user.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\EndpointInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
|
||||
/**
|
||||
* Class UpdateCustomerId
|
||||
*/
|
||||
class CreatePaymentTokenForGuest implements EndpointInterface {
|
||||
|
||||
const ENDPOINT = 'ppc-update-customer-id';
|
||||
|
||||
/**
|
||||
* The request data.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* The payment method tokens endpoint.
|
||||
*
|
||||
* @var PaymentMethodTokensEndpoint
|
||||
*/
|
||||
private $payment_method_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* CreatePaymentToken constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param PaymentMethodTokensEndpoint $payment_method_tokens_endpoint The payment method tokens endpoint.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestData $request_data,
|
||||
PaymentMethodTokensEndpoint $payment_method_tokens_endpoint
|
||||
) {
|
||||
$this->request_data = $request_data;
|
||||
$this->payment_method_tokens_endpoint = $payment_method_tokens_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception On Error.
|
||||
*/
|
||||
public function handle_request(): bool {
|
||||
$data = $this->request_data->read_request( $this->nonce() );
|
||||
|
||||
/**
|
||||
* Suppress ArgumentTypeCoercion
|
||||
*
|
||||
* @psalm-suppress ArgumentTypeCoercion
|
||||
*/
|
||||
$payment_source = new PaymentSource(
|
||||
'token',
|
||||
(object) array(
|
||||
'id' => $data['vault_setup_token'],
|
||||
'type' => 'SETUP_TOKEN',
|
||||
)
|
||||
);
|
||||
|
||||
$result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source );
|
||||
WC()->session->set( 'ppcp_guest_payment_for_free_trial', $result );
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest;
|
||||
use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
|
@ -316,6 +317,14 @@ class SavePaymentMethodsModule implements ModuleInterface {
|
|||
'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ),
|
||||
),
|
||||
),
|
||||
'labels' => array(
|
||||
'error' => array(
|
||||
'generic' => __(
|
||||
'Something went wrong. Please try again or choose another payment source.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
} catch ( RuntimeException $exception ) {
|
||||
|
@ -363,6 +372,16 @@ class SavePaymentMethodsModule implements ModuleInterface {
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . CreatePaymentTokenForGuest::ENDPOINT,
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'save-payment-methods.endpoint.create-payment-token-for-guest' );
|
||||
assert( $endpoint instanceof CreatePaymentTokenForGuest );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_before_delete_payment_token',
|
||||
function( string $token_id ) use ( $c ) {
|
||||
|
|
|
@ -64,4 +64,7 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'vaulting.vault-v3-enabled' => static function( ContainerInterface $container ): bool {
|
||||
return $container->has( 'save-payment-methods.eligible' ) && $container->get( 'save-payment-methods.eligible' );
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
@use "../../../ppcp-button/resources/css/mixins/apm-button" as apm-button;
|
||||
|
||||
$border-color: #c3c3c3;
|
||||
$background-ident-color: #fbfbfb;
|
||||
|
||||
.ppcp-field-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -11,9 +14,10 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ppcp-field-indent {
|
||||
th {
|
||||
padding-left: 20px;
|
||||
.ppcp-active-spacer {
|
||||
th, td {
|
||||
padding: 0;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,3 +43,51 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-align-label-center {
|
||||
th {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-valign-label-middle {
|
||||
th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// Box indented fields.
|
||||
@media screen and (min-width: 800px) {
|
||||
.ppcp-settings-field {
|
||||
border-left: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
|
||||
&.active {
|
||||
background-color: $background-ident-color;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
th {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.ppcp-field-indent {
|
||||
background-color: $background-ident-color;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
th, &.ppcp-settings-field-heading td {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
|
||||
& + .ppcp-field-indent {
|
||||
th, td {
|
||||
border-top: 1px solid $background-ident-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import ElementAction from "./action/ElementAction";
|
||||
import VisibilityAction from "./action/VisibilityAction";
|
||||
import AttributeAction from "./action/AttributeAction";
|
||||
|
||||
class ActionFactory {
|
||||
static make(actionConfig) {
|
||||
switch (actionConfig.type) {
|
||||
case 'element':
|
||||
return new ElementAction(actionConfig);
|
||||
case 'visibility':
|
||||
return new VisibilityAction(actionConfig);
|
||||
case 'attribute':
|
||||
return new AttributeAction(actionConfig);
|
||||
}
|
||||
|
||||
throw new Error('[ActionFactory] Unknown action: ' + actionConfig.type);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ElementCondition from "./condition/ElementCondition";
|
||||
import BoolCondition from "./condition/BoolCondition";
|
||||
import JsVariableCondition from "./condition/JsVariableCondition";
|
||||
|
||||
class ConditionFactory {
|
||||
static make(conditionConfig, triggerUpdate) {
|
||||
|
@ -8,6 +9,8 @@ class ConditionFactory {
|
|||
return new ElementCondition(conditionConfig, triggerUpdate);
|
||||
case 'bool':
|
||||
return new BoolCondition(conditionConfig, triggerUpdate);
|
||||
case 'js_variable':
|
||||
return new JsVariableCondition(conditionConfig, triggerUpdate);
|
||||
}
|
||||
|
||||
throw new Error('[ConditionFactory] Unknown condition: ' + conditionConfig.type);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import BaseAction from "./BaseAction";
|
||||
|
||||
class AttributeAction extends BaseAction {
|
||||
|
||||
run(status) {
|
||||
|
||||
if (status) {
|
||||
jQuery(this.config.selector).addClass(this.config.html_class);
|
||||
} else {
|
||||
jQuery(this.config.selector).removeClass(this.config.html_class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AttributeAction;
|
|
@ -1,6 +1,6 @@
|
|||
import BaseAction from "./BaseAction";
|
||||
|
||||
class ElementAction extends BaseAction {
|
||||
class VisibilityAction extends BaseAction {
|
||||
|
||||
run(status) {
|
||||
|
||||
|
@ -32,4 +32,4 @@ class ElementAction extends BaseAction {
|
|||
|
||||
}
|
||||
|
||||
export default ElementAction;
|
||||
export default VisibilityAction;
|
|
@ -0,0 +1,24 @@
|
|||
import BaseCondition from "./BaseCondition";
|
||||
|
||||
class JsVariableCondition extends BaseCondition {
|
||||
|
||||
register() {
|
||||
jQuery(document).on('ppcp-display-change', () => {
|
||||
const status = this.check();
|
||||
if (status !== this.status) {
|
||||
this.status = status;
|
||||
this.triggerUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
this.status = this.check();
|
||||
}
|
||||
|
||||
check() {
|
||||
let value = document[this.config.variable];
|
||||
return this.config.value === value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default JsVariableCondition;
|
|
@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers;
|
||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
|
@ -103,7 +104,10 @@ return array(
|
|||
$api_shop_country,
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.factory.paypal-checkout-url' ),
|
||||
$container->get( 'wcgateway.place-order-button-text' )
|
||||
$container->get( 'wcgateway.place-order-button-text' ),
|
||||
$container->get( 'api.endpoint.payment-tokens' ),
|
||||
$container->get( 'vaulting.vault-v3-enabled' ),
|
||||
$container->get( 'vaulting.wc-payment-tokens' )
|
||||
);
|
||||
},
|
||||
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
|
||||
|
@ -806,25 +810,6 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'vault_enabled_dcc' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'3d_secure_heading' => array(
|
||||
'heading' => __( '3D Secure', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
|
@ -881,6 +866,52 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'saved_payments_heading' => array(
|
||||
'heading' => __( 'Saved Payments', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'PayPal can securely store your customers’ payment methods for
|
||||
%1$sfuture payments and subscriptions%2$s, simplifying the checkout
|
||||
process and enabling recurring transactions on your website.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
rel="noreferrer noopener"
|
||||
href="https://woo.com/document/woocommerce-paypal-payments/#vaulting-a-card"
|
||||
>',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'vault_enabled_dcc' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'paypal_saved_payments' => array(
|
||||
'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ),
|
||||
'description' => sprintf(
|
||||
|
@ -919,6 +950,32 @@ return array(
|
|||
'gateway' => 'paypal',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'digital_wallet_heading' => array(
|
||||
'heading' => __( 'Digital Wallet Services', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'PayPal supports digital wallet services like Apple Pay or Google Pay
|
||||
to give your buyers more options to pay without a PayPal account.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
rel="noreferrer noopener"
|
||||
href="https://woo.com/document/woocommerce-paypal-payments/#vaulting-a-card"
|
||||
>',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
);
|
||||
|
||||
if ( ! $subscription_helper->plugin_is_active() ) {
|
||||
|
@ -1506,6 +1563,7 @@ return array(
|
|||
PayUponInvoiceGateway::ID,
|
||||
CardButtonGateway::ID,
|
||||
OXXOGateway::ID,
|
||||
AxoGateway::ID,
|
||||
);
|
||||
},
|
||||
'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository {
|
||||
|
|
|
@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
|||
*/
|
||||
class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
||||
|
||||
use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait;
|
||||
use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait, FreeTrialHandlerTrait;
|
||||
|
||||
const ID = 'ppcp-credit-card-gateway';
|
||||
|
||||
|
@ -454,6 +455,17 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$card_payment_token_id = wc_clean( wp_unslash( $_POST['wc-ppcp-credit-card-gateway-payment-token'] ?? '' ) );
|
||||
|
||||
if ( $this->is_free_trial_order( $wc_order ) && $card_payment_token_id ) {
|
||||
$customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() );
|
||||
foreach ( $customer_tokens as $token ) {
|
||||
if ( $token['payment_source']->name() === 'card' ) {
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $card_payment_token_id ) {
|
||||
$customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() );
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@ use Psr\Log\LoggerInterface;
|
|||
use WC_Order;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
|
@ -179,6 +181,27 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
private $paypal_checkout_url_factory;
|
||||
|
||||
/**
|
||||
* Payment tokens endpoint.
|
||||
*
|
||||
* @var PaymentTokensEndpoint
|
||||
*/
|
||||
private $payment_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* Whether Vault v3 module is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $vault_v3_enabled;
|
||||
|
||||
/**
|
||||
* WooCommerce payment tokens.
|
||||
*
|
||||
* @var WooCommercePaymentTokens
|
||||
*/
|
||||
private $wc_payment_tokens;
|
||||
|
||||
/**
|
||||
* PayPalGateway constructor.
|
||||
*
|
||||
|
@ -199,6 +222,9 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID.
|
||||
* @param string $place_order_button_text The text for the standard "Place order" button.
|
||||
* @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint.
|
||||
* @param bool $vault_v3_enabled Whether Vault v3 module is enabled.
|
||||
* @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens.
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsRenderer $settings_renderer,
|
||||
|
@ -217,7 +243,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
string $api_shop_country,
|
||||
OrderEndpoint $order_endpoint,
|
||||
callable $paypal_checkout_url_factory,
|
||||
string $place_order_button_text
|
||||
string $place_order_button_text,
|
||||
PaymentTokensEndpoint $payment_tokens_endpoint,
|
||||
bool $vault_v3_enabled,
|
||||
WooCommercePaymentTokens $wc_payment_tokens
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
$this->settings_renderer = $settings_renderer;
|
||||
|
@ -237,6 +266,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$this->api_shop_country = $api_shop_country;
|
||||
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;
|
||||
$this->order_button_text = $place_order_button_text;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->wc_payment_tokens = $wc_payment_tokens;
|
||||
|
||||
if ( $this->onboarded ) {
|
||||
$this->supports = array( 'refunds', 'tokenization' );
|
||||
|
@ -265,6 +298,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions'
|
||||
);
|
||||
} elseif ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' ) {
|
||||
$this->supports[] = 'gateway_scheduled_payments';
|
||||
} elseif ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) {
|
||||
$this->supports[] = 'tokenization';
|
||||
}
|
||||
|
@ -299,8 +334,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -500,7 +533,49 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$wc_order->save();
|
||||
}
|
||||
|
||||
if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) {
|
||||
if (
|
||||
'card' !== $funding_source
|
||||
&& $this->is_free_trial_order( $wc_order )
|
||||
&& ! $this->subscription_helper->paypal_subscription_id()
|
||||
) {
|
||||
$ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial' ) ?? null;
|
||||
if ( $this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial ) {
|
||||
$customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? '';
|
||||
if ( $customer_id ) {
|
||||
update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id );
|
||||
}
|
||||
|
||||
if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal ) ) {
|
||||
$email = '';
|
||||
if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address ) ) {
|
||||
$email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address;
|
||||
}
|
||||
|
||||
$this->wc_payment_tokens->create_payment_token_paypal(
|
||||
$wc_order->get_customer_id(),
|
||||
$ppcp_guest_payment_for_free_trial->id,
|
||||
$email
|
||||
);
|
||||
}
|
||||
|
||||
WC()->session->set( 'ppcp_guest_payment_for_free_trial', null );
|
||||
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
|
||||
$customer_id = get_user_meta( $wc_order->get_customer_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 ) {
|
||||
$payment_source_name = $token['payment_source']->name() ?? '';
|
||||
if ( $payment_source_name === 'paypal' || $payment_source_name === 'venmo' ) {
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user_id = (int) $wc_order->get_customer_id();
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $user_id );
|
||||
if ( ! array_filter(
|
||||
|
@ -513,7 +588,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ class DisplayRule {
|
|||
|
||||
const CONDITION_TYPE_ELEMENT = 'element';
|
||||
const CONDITION_TYPE_BOOL = 'bool';
|
||||
const CONDITION_TYPE_JS_VARIABLE = 'js_variable';
|
||||
|
||||
const CONDITION_OPERATION_EQUALS = 'equals';
|
||||
const CONDITION_OPERATION_NOT_EQUALS = 'not_equals';
|
||||
|
@ -26,10 +27,12 @@ class DisplayRule {
|
|||
const CONDITION_OPERATION_EMPTY = 'empty';
|
||||
const CONDITION_OPERATION_NOT_EMPTY = 'not_empty';
|
||||
|
||||
const ACTION_TYPE_ELEMENT = 'element';
|
||||
const ACTION_TYPE_VISIBILITY = 'visibility';
|
||||
const ACTION_TYPE_ATTRIBUTE = 'attribute';
|
||||
|
||||
const ACTION_VISIBLE = 'visible';
|
||||
const ACTION_ENABLE = 'enable';
|
||||
const ACTION_CLASS = 'class';
|
||||
|
||||
/**
|
||||
* The element selector.
|
||||
|
@ -132,6 +135,24 @@ class DisplayRule {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition related to js variable check.
|
||||
*
|
||||
* @param string $variable_name The javascript variable name.
|
||||
* @param mixed $value The value to enable / disable the condition.
|
||||
* @return self
|
||||
*/
|
||||
public function condition_js_variable( string $variable_name, $value ): self {
|
||||
$this->add_condition(
|
||||
array(
|
||||
'type' => self::CONDITION_TYPE_JS_VARIABLE,
|
||||
'variable' => $variable_name,
|
||||
'value' => $value,
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to show/hide the element.
|
||||
*
|
||||
|
@ -140,7 +161,7 @@ class DisplayRule {
|
|||
public function action_visible( string $selector ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ELEMENT,
|
||||
'type' => self::ACTION_TYPE_VISIBILITY,
|
||||
'selector' => $selector,
|
||||
'action' => self::ACTION_VISIBLE,
|
||||
)
|
||||
|
@ -148,6 +169,24 @@ class DisplayRule {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to add/remove a html class.
|
||||
*
|
||||
* @param string $selector The condition selector.
|
||||
* @param string $class The class.
|
||||
*/
|
||||
public function action_class( string $selector, string $class ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ATTRIBUTE,
|
||||
'selector' => $selector,
|
||||
'html_class' => $class,
|
||||
'action' => self::ACTION_CLASS,
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to enable/disable the element.
|
||||
*
|
||||
|
@ -156,7 +195,7 @@ class DisplayRule {
|
|||
public function action_enable( string $selector ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ELEMENT,
|
||||
'type' => self::ACTION_TYPE_VISIBILITY,
|
||||
'selector' => $selector,
|
||||
'action' => self::ACTION_ENABLE,
|
||||
)
|
||||
|
|
|
@ -140,7 +140,13 @@ trait CreditCardOrderInfoHandlingTrait {
|
|||
);
|
||||
$wc_order->add_order_note( $cvv_response_order_note );
|
||||
|
||||
$meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand ) );
|
||||
$meta_details = array_merge(
|
||||
$fraud_responses,
|
||||
array(
|
||||
'card_brand' => $card_brand,
|
||||
'card_last_digits' => $card_last_digits,
|
||||
)
|
||||
);
|
||||
$wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $meta_details );
|
||||
$wc_order->save_meta_data();
|
||||
|
||||
|
|
|
@ -270,6 +270,40 @@ class OrderProcessor {
|
|||
do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a given WooCommerce order and captured/authorizes the connected PayPal orders.
|
||||
*
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @param Order $order The PayPal order.
|
||||
*
|
||||
* @throws Exception If processing fails.
|
||||
*/
|
||||
public function process_captured_and_authorized( WC_Order $wc_order, Order $order ): void {
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
if ( $order->intent() === 'AUTHORIZE' ) {
|
||||
$wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' );
|
||||
|
||||
if ( $this->subscription_helper->has_subscription( $wc_order->get_id() ) ) {
|
||||
$wc_order->update_meta_data( '_ppcp_captured_vault_webhook', 'false' );
|
||||
}
|
||||
}
|
||||
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
|
||||
if ( $transaction_id ) {
|
||||
$this->update_transaction_id( $transaction_id, $wc_order );
|
||||
}
|
||||
|
||||
$this->handle_new_order_status( $order, $wc_order );
|
||||
|
||||
if ( $this->capture_authorized_downloads( $order ) ) {
|
||||
$this->authorized_payments_processor->capture_authorized_payment( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PayPal order for the given WC order.
|
||||
*
|
||||
|
|
|
@ -436,7 +436,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'desc_tip' => true,
|
||||
'label' => $container->get( 'wcgateway.settings.fraudnet-label' ),
|
||||
'description' => __( 'FraudNet is a JavaScript library developed by PayPal and embedded into a merchant’s web page to collect browser-based data to help reduce fraud.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'default' => true,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
|
@ -522,8 +522,8 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'options' => array(
|
||||
PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ),
|
||||
PurchaseUnitSanitizer::MODE_EXTRA_LINE => __( 'Add another line item', 'woocommerce-paypal-payments' ),
|
||||
PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_START,
|
||||
|
@ -538,6 +538,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
->rule()
|
||||
->condition_element( 'subtotal_mismatch_behavior', PurchaseUnitSanitizer::MODE_EXTRA_LINE )
|
||||
->action_visible( 'subtotal_mismatch_line_name' )
|
||||
->action_class( 'subtotal_mismatch_behavior', 'active' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
|
@ -548,6 +549,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'The name of the extra line that will be sent to PayPal to correct the subtotal mismatch.', 'woocommerce-paypal-payments' ),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'maxlength' => 22,
|
||||
'default' => '',
|
||||
'screens' => array(
|
||||
|
|
|
@ -91,7 +91,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'title' => __( 'Customize Smart Buttons Per Location', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Customize smart button style per location', 'woocommerce-paypal-payments' ),
|
||||
'default' => true,
|
||||
'default' => false,
|
||||
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
|
|
|
@ -142,7 +142,7 @@ class Settings implements ContainerInterface {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'smart_button_locations' => $this->default_button_locations,
|
||||
'smart_button_enable_styling_per_location' => true,
|
||||
'smart_button_enable_styling_per_location' => false,
|
||||
'pay_later_messaging_enabled' => true,
|
||||
'pay_later_button_enabled' => true,
|
||||
'pay_later_button_locations' => $this->default_pay_later_button_locations,
|
||||
|
|
|
@ -262,7 +262,7 @@ class SettingsRenderer {
|
|||
$html = sprintf(
|
||||
'<h3 class="wc-settings-sub-title %s">%s</h3>',
|
||||
esc_attr( implode( ' ', $config['class'] ) ),
|
||||
esc_html( $config['heading'] )
|
||||
isset( $config['heading_html'] ) ? $config['heading_html'] : esc_html( $config['heading'] )
|
||||
);
|
||||
|
||||
return $html;
|
||||
|
@ -388,7 +388,12 @@ $data_rows_html
|
|||
<th scope="row">
|
||||
<label
|
||||
for="<?php echo esc_attr( $id ); ?>"
|
||||
><?php echo esc_html( $config['title'] ); ?></label>
|
||||
>
|
||||
<?php
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo isset( $config['title_html'] ) ? $config['title_html'] : esc_html( $config['title'] );
|
||||
?>
|
||||
</label>
|
||||
<?php if ( isset( $config['desc_tip'] ) && $config['desc_tip'] ) : ?>
|
||||
<span
|
||||
class="woocommerce-help-tip"
|
||||
|
|
|
@ -315,4 +315,29 @@ class SubscriptionHelper {
|
|||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variation subscription plan id from the cart.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function paypal_subscription_variation_from_cart(): string {
|
||||
$cart = WC()->cart ?? null;
|
||||
if ( ! $cart || $cart->is_empty() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$items = $cart->get_cart_contents();
|
||||
foreach ( $items as $item ) {
|
||||
$variation_id = $item['variation_id'] ?? 0;
|
||||
if ( $variation_id ) {
|
||||
$variation_product = wc_get_product( $variation_id ) ?? '';
|
||||
if ( $variation_product && $variation_product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
return $variation_product->get_meta( 'ppcp_subscription_plan' )['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install",
|
||||
"install:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn install",
|
||||
"install:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn install",
|
||||
"install:modules:ppcp-axo": "cd modules/ppcp-axo && yarn install",
|
||||
"install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install",
|
||||
"install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install",
|
||||
"install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install",
|
||||
|
@ -33,6 +34,7 @@
|
|||
"build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build",
|
||||
"build:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run build",
|
||||
"build:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run build",
|
||||
"build:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run build",
|
||||
"build:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run build",
|
||||
"build:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run build",
|
||||
"build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build",
|
||||
|
@ -50,6 +52,7 @@
|
|||
"watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch",
|
||||
"watch:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run watch",
|
||||
"watch:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run watch",
|
||||
"watch:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run watch",
|
||||
"watch:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run watch",
|
||||
"watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch",
|
||||
"watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch",
|
||||
|
|
20
tests/PHPUnit/ApiClient/Exception/PayPalApiExceptionTest.php
Normal file
20
tests/PHPUnit/ApiClient/Exception/PayPalApiExceptionTest.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Exception;
|
||||
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
|
||||
class PayPalApiExceptionTest extends TestCase
|
||||
{
|
||||
public function testFriendlyMessage()
|
||||
{
|
||||
$testee = new PayPalApiException();
|
||||
|
||||
$response = json_decode('{"details":[{"issue":"PAYMENT_DENIED"}]}');
|
||||
|
||||
$this->assertEquals(
|
||||
'PayPal rejected the payment. Please reach out to the PayPal support for more information.',
|
||||
$testee->get_customer_friendly_message($response)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,11 +6,13 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway;
|
|||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
|
@ -44,6 +46,9 @@ class WcGatewayTest extends TestCase
|
|||
private $logger;
|
||||
private $apiShopCountry;
|
||||
private $orderEndpoint;
|
||||
private $paymentTokensEndpoint;
|
||||
private $vaultV3Enabled;
|
||||
private $wcPaymentTokens;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
|
@ -88,6 +93,10 @@ class WcGatewayTest extends TestCase
|
|||
|
||||
$this->logger->shouldReceive('info');
|
||||
$this->logger->shouldReceive('error');
|
||||
|
||||
$this->paymentTokensEndpoint = Mockery::mock(PaymentTokensEndpoint::class);
|
||||
$this->vaultV3Enabled = true;
|
||||
$this->wcPaymentTokens = Mockery::mock(WooCommercePaymentTokens::class);
|
||||
}
|
||||
|
||||
private function createGateway()
|
||||
|
@ -111,7 +120,10 @@ class WcGatewayTest extends TestCase
|
|||
function ($id) {
|
||||
return 'checkoutnow=' . $id;
|
||||
},
|
||||
'Pay via PayPal'
|
||||
'Pay via PayPal',
|
||||
$this->paymentTokensEndpoint,
|
||||
$this->vaultV3Enabled,
|
||||
$this->wcPaymentTokens
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class ApplicationContextRepositoryTest extends TestCase
|
|||
->andReturn($value);
|
||||
}
|
||||
|
||||
expect('network_home_url')
|
||||
expect('home_url')
|
||||
->andReturn('https://example.com/');
|
||||
expect('wc_get_checkout_url')
|
||||
->andReturn('https://example.com/checkout/');
|
||||
|
|
|
@ -37,6 +37,53 @@ test('Save during purchase', async ({page}) => {
|
|||
await expectOrderReceivedPage(page);
|
||||
});
|
||||
|
||||
test('PayPal add payment method', async ({page}) => {
|
||||
await loginAsCustomer(page);
|
||||
await page.goto('/my-account/add-payment-method');
|
||||
|
||||
const popup = await openPaypalPopup(page);
|
||||
await loginIntoPaypal(popup);
|
||||
popup.locator('#consentButton').click();
|
||||
|
||||
await page.waitForURL('/my-account/payment-methods');
|
||||
});
|
||||
|
||||
test('ACDC add payment method', async ({page}) => {
|
||||
await loginAsCustomer(page);
|
||||
await page.goto('/my-account/add-payment-method');
|
||||
|
||||
await page.click("text=Debit & Credit Cards");
|
||||
|
||||
const creditCardNumber = await page.frameLocator('[title="paypal_card_number_field"]').locator('.card-field-number');
|
||||
await creditCardNumber.fill('4005519200000004');
|
||||
|
||||
const expirationDate = await page.frameLocator('[title="paypal_card_expiry_field"]').locator('.card-field-expiry');
|
||||
await expirationDate.fill('01/25');
|
||||
|
||||
const cvv = await page.frameLocator('[title="paypal_card_cvv_field"]').locator('.card-field-cvv');
|
||||
await cvv.fill('123');
|
||||
|
||||
await page.waitForURL('/my-account/payment-methods');
|
||||
});
|
||||
|
||||
test('PayPal logged-in user free trial subscription without payment token', async ({page}) => {
|
||||
await loginAsCustomer(page);
|
||||
|
||||
await page.goto('/shop');
|
||||
await page.click("text=Sign up now");
|
||||
await page.goto('/classic-checkout');
|
||||
|
||||
const popup = await openPaypalPopup(page);
|
||||
await loginIntoPaypal(popup);
|
||||
popup.locator('#consentButton').click();
|
||||
|
||||
await page.click("text=Proceed to PayPal");
|
||||
|
||||
const title = await page.locator('.entry-title');
|
||||
await expect(title).toHaveText('Order received');
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue