From 02701ca0f506d028de622af46bb717d2efd1999a Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 15:06:15 +0200
Subject: [PATCH 01/16] =?UTF-8?q?=E2=9C=A8=20Add=20ProcessPaymentTrait=20t?=
=?UTF-8?q?o=20AxoGateway?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
We’ll use this Trait later for error handling; the trait requires the SessionHandler instance.
---
modules/ppcp-axo/services.php | 1 +
modules/ppcp-axo/src/Gateway/AxoGateway.php | 15 ++++++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php
index 0519e9209..3458f080e 100644
--- a/modules/ppcp-axo/services.php
+++ b/modules/ppcp-axo/services.php
@@ -75,6 +75,7 @@ return array(
$container->get( 'wcgateway.settings.render' ),
$container->get( 'wcgateway.settings' ),
$container->get( 'wcgateway.url' ),
+ $container->get( 'session.handler' ),
$container->get( 'wcgateway.order-processor' ),
$container->get( 'axo.card_icons' ),
$container->get( 'axo.card_icons.axo' ),
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 4fa80cd4a..ffa2ce0a0 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -26,12 +26,14 @@ use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
+use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
+use WooCommerce\PayPalCommerce\Session\SessionHandler;
/**
* Class AXOGateway.
*/
class AxoGateway extends WC_Payment_Gateway {
- use OrderMetaTrait, GatewaySettingsRendererTrait;
+ use OrderMetaTrait, GatewaySettingsRendererTrait, ProcessPaymentTrait;
const ID = 'ppcp-axo-gateway';
@@ -119,12 +121,20 @@ class AxoGateway extends WC_Payment_Gateway {
*/
protected $logger;
+ /**
+ * The Session Handler.
+ *
+ * @var SessionHandler
+ */
+ protected $session_handler;
+
/**
* AXOGateway constructor.
*
* @param SettingsRenderer $settings_renderer The settings renderer.
* @param ContainerInterface $ppcp_settings The settings.
* @param string $wcgateway_module_url The WcGateway module URL.
+ * @param SessionHandler $session_handler The session handler.
* @param OrderProcessor $order_processor The Order processor.
* @param array $card_icons The card icons.
* @param array $card_icons_axo The card icons.
@@ -139,6 +149,7 @@ class AxoGateway extends WC_Payment_Gateway {
SettingsRenderer $settings_renderer,
ContainerInterface $ppcp_settings,
string $wcgateway_module_url,
+ SessionHandler $session_handler,
OrderProcessor $order_processor,
array $card_icons,
array $card_icons_axo,
@@ -154,6 +165,7 @@ class AxoGateway extends WC_Payment_Gateway {
$this->settings_renderer = $settings_renderer;
$this->ppcp_settings = $ppcp_settings;
$this->wcgateway_module_url = $wcgateway_module_url;
+ $this->session_handler = $session_handler;
$this->order_processor = $order_processor;
$this->card_icons = $card_icons;
$this->card_icons_axo = $card_icons_axo;
@@ -245,6 +257,7 @@ class AxoGateway extends WC_Payment_Gateway {
$payment_source_properties
);
+ // TODO - this request fails in block checkout. Need to investigate.
$order = $this->order_endpoint->create(
array( $purchase_unit ),
$shipping_preference,
From a3d4a3e04fbaac0074bbf591c88155b9ea389d54 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:42:20 +0200
Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=A5=85=20Add=20generic=20error=20ha?=
=?UTF-8?q?ndling?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index ffa2ce0a0..59cfd7742 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo\Gateway;
use Psr\Log\LoggerInterface;
+use Exception;
use WC_Order;
use WC_Payment_Gateway;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
@@ -27,6 +28,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
+use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
/**
@@ -230,6 +232,13 @@ class AxoGateway extends WC_Payment_Gateway {
public function process_payment( $order_id ) {
$wc_order = wc_get_order( $order_id );
+ if ( ! is_a( $wc_order, WC_Order::class ) ) {
+ return $this->handle_payment_failure(
+ null,
+ new GatewayGenericException( new Exception( 'WC order was not found.' ) )
+ );
+ }
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$fastlane_member = wc_clean( wp_unslash( $_POST['fastlane_member'] ?? '' ) );
if ( $fastlane_member ) {
From 9c86bc23e1bed60f93d293833120bf1924831a35 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:46:07 +0200
Subject: [PATCH 03/16] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20code=20into?=
=?UTF-8?q?=20try-catch=20block?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 59cfd7742..3d7351b15 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -239,20 +239,20 @@ class AxoGateway extends WC_Payment_Gateway {
);
}
- // phpcs:ignore WordPress.Security.NonceVerification.Missing
- $fastlane_member = wc_clean( wp_unslash( $_POST['fastlane_member'] ?? '' ) );
- if ( $fastlane_member ) {
- $payment_method_title = __( 'Debit & Credit Cards (via Fastlane by PayPal)', 'woocommerce-paypal-payments' );
- $wc_order->set_payment_method_title( $payment_method_title );
- $wc_order->save();
- }
+ try {
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $fastlane_member = wc_clean( wp_unslash( $_POST['fastlane_member'] ?? '' ) );
+ if ( $fastlane_member ) {
+ $payment_method_title = __( 'Debit & Credit Cards (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'
From c011e05ed897764dffa4b84ac407301c1866377d Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:50:15 +0200
Subject: [PATCH 04/16] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20order-crea?=
=?UTF-8?q?tion=20to=20own=20method?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 74 +++++++++++++--------
1 file changed, 45 insertions(+), 29 deletions(-)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 3d7351b15..222cc923e 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -20,6 +20,7 @@ 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\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\GatewaySettingsRendererTrait;
@@ -248,36 +249,12 @@ class AxoGateway extends WC_Payment_Gateway {
$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'] ?? '' ) );
+ // The `axo_nonce` is not a WP nonce, but a single use token generated by the JS SDK.
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $token = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) );
- $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
- );
-
- // TODO - this request fails in block checkout. Need to investigate.
- $order = $this->order_endpoint->create(
- array( $purchase_unit ),
- $shipping_preference,
- null,
- null,
- '',
- ApplicationContext::USER_ACTION_CONTINUE,
- '',
- array(),
- $payment_source
- );
+ $order = $this->create_paypal_order( $wc_order, $token );
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
@@ -307,8 +284,47 @@ class AxoGateway extends WC_Payment_Gateway {
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
+ }
- return $result;
+ /**
+ * Create a new PayPal order from the existing WC_Order instance.
+ *
+ * @param WC_Order $wc_order The WooCommerce order to use as a base.
+ * @param string $single_use_token The single use token, generated by the JS SDK.
+ *
+ * @return Order The PayPal order.
+ */
+ protected function create_paypal_order( WC_Order $wc_order, string $single_use_token ) : Order {
+ $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
+
+ $shipping_preference = $this->shipping_preference_factory->from_state(
+ $purchase_unit,
+ 'checkout'
+ );
+
+ $payment_source_properties = (object) array(
+ 'single_use_token' => $single_use_token,
+ );
+
+ $payment_source = new PaymentSource(
+ 'card',
+ $payment_source_properties
+ );
+
+ // TODO - this request fails in block checkout. Need to investigate.
+ $order = $this->order_endpoint->create(
+ array( $purchase_unit ),
+ $shipping_preference,
+ null,
+ null,
+ '',
+ ApplicationContext::USER_ACTION_CONTINUE,
+ '',
+ array(),
+ $payment_source
+ );
+
+ return $order;
}
/**
From d41b967e9019429982522934f1eff8e2f850c567 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:51:35 +0200
Subject: [PATCH 05/16] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reuse=20existing=20e?=
=?UTF-8?q?rror=20handling=20logic?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 21 ++-------------------
1 file changed, 2 insertions(+), 19 deletions(-)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 222cc923e..c2a101582 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -257,25 +257,8 @@ class AxoGateway extends WC_Payment_Gateway {
$order = $this->create_paypal_order( $wc_order, $token );
$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(),
- );
+ } catch ( Exception $exception ) {
+ return $this->handle_payment_failure( $wc_order, $exception );
}
WC()->cart->empty_cart();
From a4231ab77c3294cd7abd4dd826345205330ce587 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:52:40 +0200
Subject: [PATCH 06/16] =?UTF-8?q?=E2=9C=A8=20Support=20PayPalApiExceptions?=
=?UTF-8?q?=20in=20error=20logging?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/Gateway/ProcessPaymentTrait.php | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-wc-gateway/src/Gateway/ProcessPaymentTrait.php b/modules/ppcp-wc-gateway/src/Gateway/ProcessPaymentTrait.php
index 3cf401b29..ed940db78 100644
--- a/modules/ppcp-wc-gateway/src/Gateway/ProcessPaymentTrait.php
+++ b/modules/ppcp-wc-gateway/src/Gateway/ProcessPaymentTrait.php
@@ -13,6 +13,7 @@ use Exception;
use Throwable;
use WC_Order;
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
+use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
/**
* Trait ProcessPaymentTrait
@@ -74,8 +75,13 @@ trait ProcessPaymentTrait {
* @param Throwable $exception The exception to format.
* @return string
*/
- protected function format_exception( Throwable $exception ): string {
- $output = $exception->getMessage() . ' ' . basename( $exception->getFile() ) . ':' . $exception->getLine();
+ protected function format_exception( Throwable $exception ) : string {
+ $message = $exception->getMessage();
+ if ( is_a( $exception, PayPalApiException::class ) ) {
+ $message = $exception->get_details( $message );
+ }
+
+ $output = $message . ' ' . basename( $exception->getFile() ) . ':' . $exception->getLine();
$prev = $exception->getPrevious();
if ( ! $prev ) {
return $output;
From 9564fd8d561fc79036b23e1d893693c4a8168f32 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:58:23 +0200
Subject: [PATCH 07/16] =?UTF-8?q?=F0=9F=8E=A8=20Apply=20phpcs=20rules,=20m?=
=?UTF-8?q?inor=20cleanup?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 37 ++++++++++-----------
1 file changed, 18 insertions(+), 19 deletions(-)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index c2a101582..60ad6322f 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -5,7 +5,7 @@
* @package WooCommerce\PayPalCommerce\WcGateway\Gateway
*/
-declare(strict_types=1);
+declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Axo\Gateway;
@@ -16,8 +16,6 @@ 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\ApiClient\Entity\Order;
@@ -134,19 +132,19 @@ class AxoGateway extends WC_Payment_Gateway {
/**
* AXOGateway constructor.
*
- * @param SettingsRenderer $settings_renderer The settings renderer.
- * @param ContainerInterface $ppcp_settings The settings.
- * @param string $wcgateway_module_url The WcGateway module URL.
- * @param SessionHandler $session_handler The session handler.
- * @param OrderProcessor $order_processor The Order processor.
- * @param array $card_icons The card icons.
- * @param array $card_icons_axo 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.
+ * @param SettingsRenderer $settings_renderer The settings renderer.
+ * @param ContainerInterface $ppcp_settings The settings.
+ * @param string $wcgateway_module_url The WcGateway module URL.
+ * @param SessionHandler $session_handler The session handler.
+ * @param OrderProcessor $order_processor The Order processor.
+ * @param array $card_icons The card icons.
+ * @param array $card_icons_axo The card icons.
+ * @param OrderEndpoint $order_endpoint The order endpoint.
+ * @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory.
+ * @param ShippingPreferenceFactory $shipping_preference_factory 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(
SettingsRenderer $settings_renderer,
@@ -228,6 +226,7 @@ class AxoGateway extends WC_Payment_Gateway {
* Processes the order.
*
* @param int $order_id The WC order ID.
+ *
* @return array
*/
public function process_payment( $order_id ) {
@@ -263,7 +262,7 @@ class AxoGateway extends WC_Payment_Gateway {
WC()->cart->empty_cart();
- $result = array(
+ return array(
'result' => 'success',
'redirect' => $this->get_return_url( $wc_order ),
);
@@ -349,7 +348,7 @@ class AxoGateway extends WC_Payment_Gateway {
*
* @return string
*/
- public function get_transaction_url( $order ): 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 );
@@ -383,7 +382,7 @@ class AxoGateway extends WC_Payment_Gateway {
*
* @return SettingsRenderer
*/
- protected function settings_renderer(): SettingsRenderer {
+ protected function settings_renderer() : SettingsRenderer {
return $this->settings_renderer;
}
}
From 07d9ec5f889be434b19aa9d10b1de3a212e3fcfa Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Tue, 10 Sep 2024 19:59:06 +0200
Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=9A=A7=20Document=20bug=20with=20bl?=
=?UTF-8?q?ock=20checkout?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 60ad6322f..40d0df86a 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -248,6 +248,10 @@ class AxoGateway extends WC_Payment_Gateway {
$wc_order->save();
}
+ /*
+ * TODO - in block checkout this is empty, which causes an API error.
+ * Expected a 36-char UUID, like "8216a7c6-63be-1760-7451-80a858d571be"
+ */
// The `axo_nonce` is not a WP nonce, but a single use token generated by the JS SDK.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
From 9234a833ddf5ea31346a5cfa218eedfe23251cac Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 12:50:05 +0200
Subject: [PATCH 09/16] =?UTF-8?q?=E2=9C=A8=20Submit=20axo=5Fnonce?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo-block/resources/js/index.js | 26 ++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js
index bf7c5a6d4..89e45c367 100644
--- a/modules/ppcp-axo-block/resources/js/index.js
+++ b/modules/ppcp-axo-block/resources/js/index.js
@@ -76,6 +76,32 @@ const Axo = ( props ) => {
card,
] );
+ useEffect( () => {
+ const unsubscribe = onPaymentSetup( async () => {
+ // Validate payment options and emit response.
+
+ // Note: This response supports the Ryan flow (payment via saved card-token)
+ return {
+ type: emitResponse.responseTypes.SUCCESS,
+ meta: {
+ paymentMethodData: {
+ axo_nonce: card?.id,
+ },
+ },
+ };
+ } );
+
+ // Unsubscribes when this component is unmounted.
+ return () => {
+ unsubscribe();
+ };
+ }, [
+ emitResponse.responseTypes.ERROR,
+ emitResponse.responseTypes.SUCCESS,
+ onPaymentSetup,
+ card,
+ ] );
+
const { setIsAxoActive, setIsGuest, setIsAxoScriptLoaded } =
useDispatch( STORE_NAME );
From e9a3c0bb6d2f18d1e479a63415249313d9c44ed1 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 16:04:08 +0200
Subject: [PATCH 10/16] =?UTF-8?q?=E2=9C=A8=20New=20hook=20to=20return=20AX?=
=?UTF-8?q?O=20billing=20details?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/hooks/useCustomerData.js | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/modules/ppcp-axo-block/resources/js/hooks/useCustomerData.js b/modules/ppcp-axo-block/resources/js/hooks/useCustomerData.js
index 1d513f0d1..6d98196c3 100644
--- a/modules/ppcp-axo-block/resources/js/hooks/useCustomerData.js
+++ b/modules/ppcp-axo-block/resources/js/hooks/useCustomerData.js
@@ -1,5 +1,6 @@
import { useCallback, useMemo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
+import { useMemo } from '@wordpress/element';
export const useCustomerData = () => {
const customerData = useSelect( ( select ) =>
@@ -40,3 +41,48 @@ export const useCustomerData = () => {
]
);
};
+
+export const useTokenizeCustomerData = () => {
+ const customerData = useSelect( ( select ) =>
+ select( 'wc/store/cart' ).getCustomerData()
+ );
+
+ const isValidAddress = ( address ) => {
+ // At least one name must be present.
+ if ( ! address.first_name && ! address.last_name ) {
+ return false;
+ }
+
+ // Street, city, postcode, country are mandatory; state is optional.
+ return (
+ address.address_1 &&
+ address.city &&
+ address.postcode &&
+ address.country
+ );
+ };
+
+ // Memoize the customer data to avoid unnecessary re-renders (and potential infinite loops).
+ return useMemo( () => {
+ const { billingAddress, shippingAddress } = customerData;
+
+ // Prefer billing address, but fallback to shipping address if billing address is not valid.
+ const mainAddress = isValidAddress( billingAddress )
+ ? billingAddress
+ : shippingAddress;
+
+ return {
+ cardholderName: {
+ fullName: `${ mainAddress.first_name } ${ mainAddress.last_name }`,
+ },
+ billingAddress: {
+ addressLine1: mainAddress.address_1,
+ addressLine2: mainAddress.address_2,
+ adminArea1: mainAddress.state,
+ adminArea2: mainAddress.city,
+ postalCode: mainAddress.postcode,
+ countryCode: mainAddress.country,
+ },
+ };
+ }, [ customerData ] );
+};
From 7e707d5aa1ae5ebab79086c9d22a4c84063b5675 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 16:08:59 +0200
Subject: [PATCH 11/16] =?UTF-8?q?=E2=9C=A8=20Add=20dependencies=20for=20Ga?=
=?UTF-8?q?ry=20checkout?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Conflicts:
# modules/ppcp-axo-block/resources/js/index.js
---
modules/ppcp-axo-block/resources/js/index.js | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js
index 89e45c367..85a521a6f 100644
--- a/modules/ppcp-axo-block/resources/js/index.js
+++ b/modules/ppcp-axo-block/resources/js/index.js
@@ -7,7 +7,10 @@ import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Help
// Hooks
import useFastlaneSdk from './hooks/useFastlaneSdk';
-import { useCustomerData } from './hooks/useCustomerData';
+import {
+ useCustomerData,
+ useTokenizeCustomerData,
+} from './hooks/useCustomerData';
import { useShippingAddressChange } from './hooks/useShippingAddressChange';
import { useCardChange } from './hooks/useCardChange';
@@ -46,6 +49,8 @@ const Axo = ( props ) => {
const [ paypalLoaded, setPaypalLoaded ] = useState( false );
const [ shippingAddress, setShippingAddress ] = useState( null );
const [ card, setCard ] = useState( null );
+ const [ paymentComponent, setPaymentComponent ] = useState( null );
+ const tokenizedCustomerData = useTokenizeCustomerData();
const fastlaneSdk = useFastlaneSdk( axoConfig, ppcpConfig );
console.log( 'Axo component rendering' );
@@ -206,8 +211,8 @@ const Axo = ( props ) => {
};
}, [] );
- const handlePaymentLoad = useCallback( ( paymentComponent ) => {
- console.log( 'Payment component loaded', paymentComponent );
+ const handlePaymentLoad = useCallback( ( component ) => {
+ setPaymentComponent( component );
}, [] );
const handleChange = ( selectedCard ) => {
From 389860c739e13560532ff2515ec38ca08e57f3a9 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 16:17:12 +0200
Subject: [PATCH 12/16] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Re-organize=20paymen?=
=?UTF-8?q?t-setup-integration?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Performance update, and preparation for Gary-flow changes
---
modules/ppcp-axo-block/resources/js/index.js | 39 +++++++++++---------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js
index 85a521a6f..4dcbe7419 100644
--- a/modules/ppcp-axo-block/resources/js/index.js
+++ b/modules/ppcp-axo-block/resources/js/index.js
@@ -81,29 +81,22 @@ const Axo = ( props ) => {
card,
] );
- useEffect( () => {
- const unsubscribe = onPaymentSetup( async () => {
- // Validate payment options and emit response.
+ const handlePaymentSetup = useCallback( async () => {
+ const isRyanFlow = !! card?.id;
+ const cardToken = card?.id;
- // Note: This response supports the Ryan flow (payment via saved card-token)
- return {
- type: emitResponse.responseTypes.SUCCESS,
- meta: {
- paymentMethodData: {
- axo_nonce: card?.id,
- },
+ return {
+ type: emitResponse.responseTypes.SUCCESS,
+ meta: {
+ paymentMethodData: {
+ fastlane_member: isRyanFlow,
+ axo_nonce: cardToken,
},
- };
- } );
-
- // Unsubscribes when this component is unmounted.
- return () => {
- unsubscribe();
+ },
};
}, [
emitResponse.responseTypes.ERROR,
emitResponse.responseTypes.SUCCESS,
- onPaymentSetup,
card,
] );
@@ -117,6 +110,18 @@ const Axo = ( props ) => {
setBillingAddress: updateWooBillingAddress,
} = useCustomerData();
+ /**
+ * `onPaymentSetup()` fires when we enter the "PROCESSING" state in the checkout flow.
+ * It pre-processes the payment details and returns data for server-side processing.
+ */
+ useEffect( () => {
+ const unsubscribe = onPaymentSetup( handlePaymentSetup );
+
+ return () => {
+ unsubscribe();
+ };
+ }, [ onPaymentSetup, handlePaymentSetup ] );
+
useEffect( () => {
console.log( 'Initializing class toggles' );
initializeClassToggles();
From db6b860b5f64c4f98c3cff50b7672278b6783130 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 16:18:08 +0200
Subject: [PATCH 13/16] =?UTF-8?q?=E2=9C=A8=20Integrate=20Gary=20flow=20in?=
=?UTF-8?q?=20AXO=20block=20checkout?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo-block/resources/js/index.js | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js
index 4dcbe7419..a1023b03a 100644
--- a/modules/ppcp-axo-block/resources/js/index.js
+++ b/modules/ppcp-axo-block/resources/js/index.js
@@ -83,7 +83,20 @@ const Axo = ( props ) => {
const handlePaymentSetup = useCallback( async () => {
const isRyanFlow = !! card?.id;
- const cardToken = card?.id;
+ let cardToken = card?.id;
+
+ if ( ! cardToken && paymentComponent ) {
+ cardToken = await paymentComponent
+ .getPaymentToken( tokenizedCustomerData )
+ .then( ( response ) => response.id );
+ }
+
+ if ( ! cardToken ) {
+ return {
+ type: emitResponse.responseTypes.ERROR,
+ message: 'Could not process the payment (tokenization error)',
+ };
+ }
return {
type: emitResponse.responseTypes.SUCCESS,
@@ -98,6 +111,8 @@ const Axo = ( props ) => {
emitResponse.responseTypes.ERROR,
emitResponse.responseTypes.SUCCESS,
card,
+ paymentComponent,
+ tokenizedCustomerData,
] );
const { setIsAxoActive, setIsGuest, setIsAxoScriptLoaded } =
From a0d0a06dfdc3c0303b453498a3b6b40a519cf27a Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Wed, 11 Sep 2024 16:21:05 +0200
Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=92=A1=20Remove=20dev-comments,=20c?=
=?UTF-8?q?lean=20up?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/Gateway/AxoGateway.php | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php
index 40d0df86a..dc54c0e9e 100644
--- a/modules/ppcp-axo/src/Gateway/AxoGateway.php
+++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php
@@ -248,12 +248,7 @@ class AxoGateway extends WC_Payment_Gateway {
$wc_order->save();
}
- /*
- * TODO - in block checkout this is empty, which causes an API error.
- * Expected a 36-char UUID, like "8216a7c6-63be-1760-7451-80a858d571be"
- */
-
- // The `axo_nonce` is not a WP nonce, but a single use token generated by the JS SDK.
+ // The `axo_nonce` is not a WP nonce, but a card-token generated by the JS SDK.
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$token = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) );
@@ -275,12 +270,12 @@ class AxoGateway extends WC_Payment_Gateway {
/**
* Create a new PayPal order from the existing WC_Order instance.
*
- * @param WC_Order $wc_order The WooCommerce order to use as a base.
- * @param string $single_use_token The single use token, generated by the JS SDK.
+ * @param WC_Order $wc_order The WooCommerce order to use as a base.
+ * @param string $payment_token The payment token, generated by the JS SDK.
*
* @return Order The PayPal order.
*/
- protected function create_paypal_order( WC_Order $wc_order, string $single_use_token ) : Order {
+ protected function create_paypal_order( WC_Order $wc_order, string $payment_token ) : Order {
$purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
$shipping_preference = $this->shipping_preference_factory->from_state(
@@ -289,7 +284,7 @@ class AxoGateway extends WC_Payment_Gateway {
);
$payment_source_properties = (object) array(
- 'single_use_token' => $single_use_token,
+ 'single_use_token' => $payment_token,
);
$payment_source = new PaymentSource(
@@ -297,8 +292,7 @@ class AxoGateway extends WC_Payment_Gateway {
$payment_source_properties
);
- // TODO - this request fails in block checkout. Need to investigate.
- $order = $this->order_endpoint->create(
+ return $this->order_endpoint->create(
array( $purchase_unit ),
$shipping_preference,
null,
@@ -309,8 +303,6 @@ class AxoGateway extends WC_Payment_Gateway {
array(),
$payment_source
);
-
- return $order;
}
/**
From 0cdfe13102ef7910f016e33e204c3fe72b988ae9 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Thu, 12 Sep 2024 16:38:27 +0200
Subject: [PATCH 15/16] =?UTF-8?q?=E2=9C=A8=20Enforce=20Fastlane=20to=20be?=
=?UTF-8?q?=20the=20first=20payment=20method?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/AxoModule.php | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php
index 069860550..a215fc7db 100644
--- a/modules/ppcp-axo/src/AxoModule.php
+++ b/modules/ppcp-axo/src/AxoModule.php
@@ -28,6 +28,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
+use WC_Payment_Gateways;
+
/**
* Class AxoModule
*/
@@ -130,6 +132,20 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
}
);
+ // Enforce Fastlane to always be the first payment method in the list.
+ add_action(
+ 'wc_payment_gateways_initialized',
+ function ( WC_Payment_Gateways $gateways ) {
+ foreach ( $gateways->payment_gateways as $key => $gateway ) {
+ if ( $gateway->id === AxoGateway::ID ) {
+ unset( $gateways->payment_gateways[ $key ] );
+ array_unshift( $gateways->payment_gateways, $gateway );
+ break;
+ }
+ }
+ }
+ );
+
// Force 'cart-block' and 'cart' Smart Button locations in the settings.
add_action(
'admin_init',
From 63ff90f061dca95c700d368b0e796c125aae42e2 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Thu, 12 Sep 2024 16:48:20 +0200
Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=A9=B9=20Do=20not=20modify=20gatewa?=
=?UTF-8?q?y=20sort=20order=20in=20admin?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
modules/ppcp-axo/src/AxoModule.php | 3 +++
1 file changed, 3 insertions(+)
diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php
index a215fc7db..1ff7ae712 100644
--- a/modules/ppcp-axo/src/AxoModule.php
+++ b/modules/ppcp-axo/src/AxoModule.php
@@ -136,6 +136,9 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
add_action(
'wc_payment_gateways_initialized',
function ( WC_Payment_Gateways $gateways ) {
+ if ( is_admin() ) {
+ return;
+ }
foreach ( $gateways->payment_gateways as $key => $gateway ) {
if ( $gateway->id === AxoGateway::ID ) {
unset( $gateways->payment_gateways[ $key ] );