Axo gateway skeleton.

This commit is contained in:
Pedro Silva 2024-02-08 14:37:56 +00:00
parent fc7b8022e8
commit b4b5863cb4
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
14 changed files with 2872 additions and 9 deletions

View file

@ -71,5 +71,12 @@ return function ( string $root_dir ): iterable {
$modules[] = ( require "$modules_dir/ppcp-paylater-block/module.php" )(); $modules[] = ( require "$modules_dir/ppcp-paylater-block/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; return $modules;
}; };

View file

@ -118,7 +118,7 @@ class PayPalBearer implements Bearer {
private function newBearer(): Token { private function newBearer(): Token {
$key = $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) ? $this->settings->get( 'client_id' ) : $this->key; $key = $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) ? $this->settings->get( 'client_id' ) : $this->key;
$secret = $this->settings->has( 'client_secret' ) && $this->settings->get( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : $this->secret; $secret = $this->settings->has( 'client_secret' ) && $this->settings->get( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : $this->secret;
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials'; $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&intent=sdk_init';
$args = array( $args = array(
'method' => 'POST', 'method' => 'POST',

View file

@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\PPCP;
use WP_Error; use WP_Error;
/** /**
@ -71,20 +72,27 @@ class UserIdToken {
public function id_token( string $target_customer_id = '' ): string { public function id_token( string $target_customer_id = '' ): string {
$bearer = $this->bearer->bearer(); $bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token&intent=sdk_init';
if ( $target_customer_id ) { if ( $target_customer_id ) {
$url = add_query_arg( // $url = add_query_arg(
array( // array(
'target_customer_id' => $target_customer_id, // 'target_customer_id' => $target_customer_id,
), // ),
$url // $url
); // );
} }
// TODO fix this to use Bearer instead of Basic auth:
$settings = PPCP::container()->get( 'wcgateway.settings' );
$key = $settings->has( 'client_id' ) && $settings->get( 'client_id' ) ? $settings->get( 'client_id' ) : null;
$secret = $settings->has( 'client_secret' ) && $settings->get( 'client_secret' ) ? $settings->get( 'client_secret' ) : null;
$args = array( $args = array(
'method' => 'POST', 'method' => 'POST',
'headers' => array( 'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(), // 'Authorization' => 'Bearer ' . $bearer->token(),
'Authorization' => 'Basic ' . base64_encode( "$key:$secret" ),
'Content-Type' => 'application/x-www-form-urlencoded', 'Content-Type' => 'application/x-www-form-urlencoded',
), ),
); );

View file

@ -9,6 +9,118 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo; 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;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array( 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 );
// Standard Payments tab fields.
return $insert_after(
$fields,
'vault_enabled',
array(
'axo_enabled' => array(
'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
'label' => __( 'Enable Fastlane Checkout', 'woocommerce-paypal-payments' )
. '<p class="description">'
. sprintf(
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
__( 'Buyers can use %1$sFastlane%2$s to make payments.', 'woocommerce-paypal-payments' ),
'<a href="https://www.paypal.com" target="_blank">',
'</a>'
)
. '</p>',
'default' => 'yes',
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'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_address_widget' )
->action_visible( 'axo_payment_widget' )
->to_array(),
)
),
),
),
'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' => 'paypal',
),
'axo_address_widget' => array(
'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,
'description' => __(
'This controls if the Hosted Address Widget should be used.',
'woocommerce-paypal-payments'
),
'classes' => array( 'ppcp-field-indent' ),
'class' => array(),
'input_class' => array( 'wc-enhanced-select' ),
'default' => 'pay',
'options' => PropertiesDictionary::address_widget_options(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
'axo_payment_widget' => array(
'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ),
'type' => 'select',
'desc_tip' => true,
'description' => __(
'This controls if the Hosted Payment Widget should be used.',
'woocommerce-paypal-payments'
),
'label' => '',
'input_class' => array( 'wc-enhanced-select' ),
'classes' => array( 'ppcp-field-indent' ),
'class' => array(),
'default' => 'black',
'options' => PropertiesDictionary::payment_widget_options(),
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
),
)
);
},
); );

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,207 @@
let initialized = false;
function log(message) {
console.log('Log: ', message);
// let div = document.createElement('div');
// div.style.cssText = 'margin-bottom: 4px;';
// div.textContent = message;
// document.getElementById('log-container').appendChild(div);
}
const init = () => {
if (initialized) {
return;
}
initialized = true;
(async function () {
// sets the SDK to run in sandbox mode (Remove for production)
window.localStorage.setItem('axoEnv', 'sandbox');
//specify global styles here
const styles = {
root: {
backgroundColorPrimary: "#ffffff"
}
}
const locale = 'en_us';
// instantiates the Connect module
const connect = await window.paypal.Connect({
locale,
styles
});
console.log('connect', connect);
// Specifying the locale if necessary
connect.setLocale('en_us');
const {
identity,
profile,
ConnectCardComponent,
ConnectWatermarkComponent
} = connect;
console.log(
'[identity, profile, ConnectCardComponent]',
identity,
profile,
ConnectCardComponent
);
//--------
const emailInput = document.querySelector('#billing_email_field').querySelector('input');
jQuery(emailInput).after(`
<div id="watermark-container" style="max-width: 200px; margin-top: 10px;"></div>
`);
jQuery('.payment_method_ppcp-axo-gateway').append(`
<div id="payment-container"></div>
`);
const connectWatermarkComponent = ConnectWatermarkComponent({
includeAdditionalInfo: true
}).render('#watermark-container');
//--------
const emailUpdated = async () => {
log('Email changed: ' + emailInput.value);
if (!emailInput.checkValidity()) {
log('The email address is not valid.');
return;
}
const { customerContextId } = await identity.lookupCustomerByEmail(emailInput.value);
if (customerContextId) {
// Email is associated with a Connect profile or aPayPal member
// Authenticate the customer to get access to their profile
log('Email is associated with a Connect profile or a PayPal member');
} else {
// No profile found with this email address.
// This is a guest customer.
log('No profile found with this email address.');
const fields = {
phoneNumber: {
prefill: "1234567890"
},
cardholderName: {} // optionally pass this to show the card holder name
};
jQuery("#payment-container").css({
'padding': '1rem 0',
'background-color': '#ffffff',
});
const connectCardComponent = await connect
.ConnectCardComponent({fields})
.render("#payment-container");
const submitButton = document.getElementById('submit-button');
// event listener when the customer clicks to place the order
submitButton.addEventListener("click", async ()=> {
const { nonce } = await connectCardComponent.tokenize({
name: {
fullName: "John Doe"
},
billingAddress: {
addressLine1: "2211 North 1st St",
adminArea1: "San Jose",
adminArea2: "CA",
postalCode: "95131",
countryCode: "US"
}
});
// Send the nonce and previously captured device data to server to complete checkout
log('nonce: ' + nonce);
// fetch('submit.php', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({
// nonce: nonce
// }),
// })
//
// .then(response => {
// if (!response.ok) {
// throw new Error('Network response was not ok');
// }
// return response.json();
// })
// .then(data => {
// console.log('Submit response', data);
// log(JSON.stringify(data));
// })
// .catch(error => {
// console.error('There has been a problem with your fetch operation:', error);
// });
});
}
}
emailInput.addEventListener("change", async ()=> {
emailUpdated();
});
if (emailInput.value) {
emailUpdated();
}
})();
}
const bootstrap = () => {
jQuery(document).on('change', '#payment_method_ppcp-axo-gateway', (ev) => {
if(ev.target.checked) {
let emailRow = document.querySelector('#billing_email_field');
jQuery(emailRow.parentNode).prepend(emailRow);
emailRow.querySelector('input').focus();
init();
} else {
console.log('Checkbox is not checked.');
// Additional actions when the checkbox is unchecked
}
});
let gatewaysRenderedInterval = setInterval(() => {
console.log('not rendered');
if (document.querySelector('.wc_payment_methods')) {
console.log('YES rendered');
clearInterval(gatewaysRenderedInterval);
jQuery('#payment_method_ppcp-axo-gateway').trigger('change');
}
}, 100);
}
document.addEventListener(
'DOMContentLoaded',
() => {
if (!typeof (PayPalCommerceGateway)) {
console.error('PayPal button could not be configured.');
return;
}
bootstrap();
},
);

View file

@ -9,6 +9,19 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo; namespace WooCommerce\PayPalCommerce\Axo;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array( return array(
'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'
);
},
); );

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo; namespace WooCommerce\PayPalCommerce\Axo;
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
@ -33,6 +34,74 @@ class AxoModule implements ModuleInterface {
*/ */
public function run( ContainerInterface $c ): void { public function run( ContainerInterface $c ): void {
add_filter(
'woocommerce_payment_gateways',
function ( $methods ): array {
$methods[] = new AxoGateway();
return $methods;
},
1,
9
);
/**
* Param types removed to avoid third-party issues.
*
* @psalm-suppress MissingClosureParamType
*/
add_filter(
'woocommerce_paypal_payments_sdk_components_hook',
function( $components ) {
$components[] = 'connect';
return $components;
}
);
add_action(
'init',
static function () use ( $c ) {
// Enqueue frontend scripts.
add_action(
'wp_enqueue_scripts',
static function () use ( $c ) {
$module_url = $c->get( 'axo.url' );
$version = '1';
// Register styles.
wp_register_style(
'wc-ppcp-axo',
untrailingslashit( $module_url ) . '/assets/css/styles.css',
array(),
$version
);
wp_enqueue_style( 'wc-ppcp-axo' );
// Register scripts.
wp_register_script(
'wc-ppcp-axo',
untrailingslashit( $module_url ) . '/assets/js/boot.js',
array(),
$version,
true
);
wp_enqueue_script( 'wc-ppcp-axo' );
wp_localize_script(
'wc-ppcp-axo',
'wc_ppcp_axo',
array(
// TODO
)
);
}
);
},
1
);
} }
/** /**

View file

@ -0,0 +1,136 @@
<?php
/**
* The AXO Gateway
*
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo\Gateway;
use WC_Payment_Gateway;
/**
* Class AXOGateway.
*/
class AxoGateway extends WC_Payment_Gateway {
const ID = 'ppcp-axo-gateway';
/**
* AXOGateway constructor.
*/
public function __construct(
) {
$this->id = self::ID;
$this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' );
$this->method_description = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' );
$this->title = $this->get_option( 'title', $this->method_title );
$this->description = $this->get_option( 'description', __( '', 'woocommerce-paypal-payments' ) );
$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->module_url = $module_url;
// $this->logger = $logger;
// $this->icon = esc_url( $this->module_url ) . 'assets/images/axo.svg'; // TODO
// $this->environment = $environment;
$this->update_option( 'enabled', 'yes' ); // TODO : depend on settings
}
/**
* 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 );
// TODO ...
WC()->cart->empty_cart();
$result = array(
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
return $result;
}
// public function is_available()
// {
// return $this->is_enabled(); // parent::is_available();
// }
//
// /**
// * Returns if the gateway is enabled.
// *
// * @return bool
// */
// private function is_enabled(): bool {
// return true;
// //return $this->config->has( 'axo_enabled' ) && $this->config->get( 'axo_enabled' ); // TODO
// }
/**
* Returns the icons of the gateway.
*
* @return string
*/
public function get_icon() {
$images = array();
$cards = 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'),
);
foreach ($cards as $card) {
$images[] = '<img
title="' . $card['title'] . '"
style="float: left;"
src="/wp-content/plugins/woocommerce-paypal-payments/modules/ppcp-wc-gateway/assets/images/' . $card['file'] . '"
class="ppcp-card-icon"
> ';
}
return '<div style="padding: 4px 0 16px 25px;">' . implode( '', $images ) . '</div>';
}
}

View file

@ -0,0 +1,41 @@
<?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 possible list of possible address widget options.
*
* @return array
*/
public static function address_widget_options(): array {
return array(
'render' => __( 'Render address options.', 'woocommerce-paypal-payments' ),
'use_widget' => __( 'Use address widget.', 'woocommerce-paypal-payments' ),
);
}
/**
* Returns the possible list of possible address widget options.
*
* @return array
*/
public static function payment_widget_options(): array {
return array(
'render' => __( 'Render payment options.', 'woocommerce-paypal-payments' ),
'use_widget' => __( 'Use payment widget.', 'woocommerce-paypal-payments' ),
);
}
}

View file

@ -9,6 +9,8 @@ module.exports = {
target: 'web', target: 'web',
plugins: [ new DependencyExtractionWebpackPlugin() ], plugins: [ new DependencyExtractionWebpackPlugin() ],
entry: { entry: {
'boot': path.resolve('./resources/js/boot.js'),
"styles": path.resolve('./resources/css/styles.scss')
}, },
output: { output: {
path: path.resolve(__dirname, 'assets/'), path: path.resolve(__dirname, 'assets/'),

2262
modules/ppcp-axo/yarn.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -72,6 +72,8 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => {
scriptOptions['data-user-id-token'] = userIdToken; scriptOptions['data-user-id-token'] = userIdToken;
} }
scriptOptions['data-client-metadata-id'] = 'ppcp-cm-id';
// Load PayPal script // Load PayPal script
loadScript(scriptOptions) loadScript(scriptOptions)
.then(callback) .then(callback)

View file

@ -17,6 +17,7 @@
"install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install", "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-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-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-onboarding": "cd modules/ppcp-onboarding && yarn install",
"install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install", "install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install",
"install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install", "install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install",
@ -29,6 +30,7 @@
"build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build", "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-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-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-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run build",
"build:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && 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", "build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build",
@ -44,6 +46,7 @@
"watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch", "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-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-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-onboarding": "cd modules/ppcp-onboarding && yarn run watch",
"watch:modules:ppcp-compat": "cd modules/ppcp-compat && 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", "watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch",