From 7f3d74c8c8dd196f7cde4def6a872de1e585889c Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Thu, 18 Jan 2024 10:39:49 +0000
Subject: [PATCH 01/20] Fix warning
---
.../src/SavePaymentMethodsModule.php | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
index 1e4f70d9e..3529247a2 100644
--- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
+++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
@@ -106,8 +106,9 @@ class SavePaymentMethodsModule implements ModuleInterface {
}
if ( $payment_method === PayPalGateway::ID ) {
+ $funding_source = $request_data['funding_source'] ?? null;
- if ( $request_data['funding_source'] === 'venmo' ) {
+ if ( $funding_source === 'venmo' ) {
$data['payment_source'] = array(
'venmo' => array(
'attributes' => array(
@@ -118,6 +119,20 @@ class SavePaymentMethodsModule implements ModuleInterface {
),
),
);
+ } else if ( $funding_source === 'apple_pay' ) {
+ $data['payment_source'] = array(
+ 'apple_pay' => array(
+ 'stored_credential' => array(
+ 'payment_initiator' => 'CUSTOMER',
+ 'payment_type' => 'RECURRING',
+ ),
+ 'attributes' => array(
+ 'vault' => array(
+ 'store_in_vault' => 'ON_SUCCESS',
+ ),
+ ),
+ ),
+ );
} else {
$data['payment_source'] = array(
'paypal' => array(
From 827bd2568db908e7f5afff0c2377ebb9d27c336b Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 19 Jan 2024 09:52:59 +0000
Subject: [PATCH 02/20] Fix passing funding_source on block pages.
---
modules/ppcp-blocks/resources/js/checkout-block.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js
index 654868447..1662d7d89 100644
--- a/modules/ppcp-blocks/resources/js/checkout-block.js
+++ b/modules/ppcp-blocks/resources/js/checkout-block.js
@@ -77,7 +77,7 @@ const PayPalComponent = ({
window.ppcpContinuationFilled = true;
}, [])
- const createOrder = async () => {
+ const createOrder = async (data, actions) => {
try {
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
method: 'POST',
@@ -87,6 +87,7 @@ const PayPalComponent = ({
bn_code: '',
context: config.scriptData.context,
payment_method: 'ppcp-gateway',
+ funding_source: data.paymentSource,
createaccount: false
}),
});
From 96b83c9d0d607f5efacab0392729520f24594235 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 24 Jan 2024 08:47:48 +0000
Subject: [PATCH 03/20] ApplePay Vaulting Integration
---
.../resources/js/ApplepayButton.js | 6 ++-
.../resources/js/checkout-block.js | 2 +-
.../js/modules/ButtonModuleWatcher.js | 1 -
.../src/SavePaymentMethodsModule.php | 7 +--
.../src/WooCommercePaymentTokens.php | 14 ++++--
.../ppcp-vaulting/src/PaymentTokenPayPal.php | 21 ++++++++-
modules/ppcp-vaulting/src/VaultingModule.php | 47 ++++++++++++++++++-
7 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 4484bba21..e9ec47bde 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -209,11 +209,15 @@ class ApplepayButton {
/**
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
- async onButtonClick() {
+ async onButtonClick(data, actions) {
+ console.log('data, actions', data, actions);
+
this.log('onButtonClick', this.context);
const paymentRequest = this.paymentRequest();
+ window.ppcpFundingSource = 'apple_pay'; // Do this on another place like on create order endpoint handler.
+
// Trigger woocommerce validation if we are in the checkout page.
if (this.context === 'checkout') {
const checkoutFormSelector = 'form.woocommerce-checkout';
diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js
index 1662d7d89..d999efa80 100644
--- a/modules/ppcp-blocks/resources/js/checkout-block.js
+++ b/modules/ppcp-blocks/resources/js/checkout-block.js
@@ -87,7 +87,7 @@ const PayPalComponent = ({
bn_code: '',
context: config.scriptData.context,
payment_method: 'ppcp-gateway',
- funding_source: data.paymentSource,
+ funding_source: window.ppcpFundingSource ?? 'paypal',
createaccount: false
}),
});
diff --git a/modules/ppcp-button/resources/js/modules/ButtonModuleWatcher.js b/modules/ppcp-button/resources/js/modules/ButtonModuleWatcher.js
index c6165674a..3fccca178 100644
--- a/modules/ppcp-button/resources/js/modules/ButtonModuleWatcher.js
+++ b/modules/ppcp-button/resources/js/modules/ButtonModuleWatcher.js
@@ -7,7 +7,6 @@ class ButtonModuleWatcher {
}
watchContextBootstrap(callable) {
- console.log('ButtonModuleWatcher.js: watchContextBootstrap', this.contextBootstrapRegistry)
this.contextBootstrapWatchers.push(callable);
Object.values(this.contextBootstrapRegistry).forEach(callable);
}
diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
index 71441f562..e79ec11f6 100644
--- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
+++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
@@ -119,14 +119,14 @@ class SavePaymentMethodsModule implements ModuleInterface {
),
),
);
- } else if ( $funding_source === 'apple_pay' ) {
+ } elseif ( $funding_source && $funding_source === 'apple_pay' ) {
$data['payment_source'] = array(
'apple_pay' => array(
'stored_credential' => array(
'payment_initiator' => 'CUSTOMER',
'payment_type' => 'RECURRING',
),
- 'attributes' => array(
+ 'attributes' => array(
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
),
@@ -191,7 +191,8 @@ class SavePaymentMethodsModule implements ModuleInterface {
$wc_payment_tokens->create_payment_token_paypal(
$wc_order->get_customer_id(),
$token_id,
- $payment_source->properties()->email_address ?? ''
+ $payment_source->properties()->email_address ?? '',
+ $payment_source->name()
);
}
}
diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
index 520099994..3e5f99b04 100644
--- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
+++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
@@ -66,16 +66,18 @@ class WooCommercePaymentTokens {
/**
* Creates a WC Payment Token for PayPal payment.
*
- * @param int $customer_id The WC customer ID.
- * @param string $token The PayPal payment token.
- * @param string $email The PayPal customer email.
+ * @param int $customer_id The WC customer ID.
+ * @param string $token The PayPal payment token.
+ * @param string $email The PayPal customer email.
+ * @param string $payment_source The funding source.
*
* @return int
*/
public function create_payment_token_paypal(
int $customer_id,
string $token,
- string $email
+ string $email,
+ string $payment_source = ''
): int {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
@@ -94,6 +96,10 @@ class WooCommercePaymentTokens {
$payment_token_paypal->set_email( $email );
}
+ if ( $payment_source ) {
+ $payment_token_paypal->set_payment_source( $payment_source );
+ }
+
try {
$payment_token_paypal->save();
} catch ( Exception $exception ) {
diff --git a/modules/ppcp-vaulting/src/PaymentTokenPayPal.php b/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
index 1a5858118..3b8eac849 100644
--- a/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
+++ b/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
@@ -28,7 +28,8 @@ class PaymentTokenPayPal extends WC_Payment_Token {
* @var string[]
*/
protected $extra_data = array(
- 'email' => '',
+ 'email' => '',
+ 'payment_source' => '',
);
/**
@@ -48,4 +49,22 @@ class PaymentTokenPayPal extends WC_Payment_Token {
public function set_email( $email ) {
$this->add_meta_data( 'email', $email, true );
}
+
+ /**
+ * Get the payment source.
+ *
+ * @return string The payment source.
+ */
+ public function get_payment_source() {
+ return $this->get_meta( 'payment_source' );
+ }
+
+ /**
+ * Set the payment source.
+ *
+ * @param string $payment_source The payment source.
+ */
+ public function set_payment_source( string $payment_source ) {
+ $this->add_meta_data( 'payment_source', $payment_source, true );
+ }
}
diff --git a/modules/ppcp-vaulting/src/VaultingModule.php b/modules/ppcp-vaulting/src/VaultingModule.php
index bba8e5464..1860a889d 100644
--- a/modules/ppcp-vaulting/src/VaultingModule.php
+++ b/modules/ppcp-vaulting/src/VaultingModule.php
@@ -86,6 +86,37 @@ class VaultingModule implements ModuleInterface {
}
);
+ add_filter(
+ 'woocommerce_get_customer_payment_tokens',
+ /**
+ * Filter available payment tokens depending on context.
+ *
+ * @psalm-suppress MissingClosureParamType
+ * @psalm-suppress MissingClosureReturnType
+ */
+ function( $tokens, $customer_id, $gateway_id ) {
+ if ( ! is_array( $tokens ) ) {
+ return $tokens;
+ }
+
+ // Exclude ApplePay tokens from payment pages.
+ if ( is_checkout() || is_cart() || is_product() ) {
+ foreach ( $tokens as $index => $token ) {
+ if (
+ $token instanceof PaymentTokenPayPal
+ && $token->get_payment_source() === 'apple_pay'
+ ) {
+ unset( $tokens[ $index ] );
+ }
+ }
+ }
+
+ return $tokens;
+ },
+ 10,
+ 3
+ );
+
add_filter(
'woocommerce_payment_methods_list_item',
/**
@@ -100,8 +131,22 @@ class VaultingModule implements ModuleInterface {
if ( strtolower( $payment_token->get_type() ) === 'paypal' ) {
assert( $payment_token instanceof PaymentTokenPayPal );
- $item['method']['brand'] = $payment_token->get_email();
+ $email = $payment_token->get_email();
+ $payment_source = $payment_token->get_payment_source();
+ $brand_parts = array();
+
+ if ( $payment_source !== 'paypal' ) {
+ $brand_parts[] = ucwords( $payment_source );
+ }
+
+ if ( $email ) {
+ $brand_parts[] = $email;
+ } else {
+ $brand_parts[] = '#' . ( (string) $payment_token->get_id() );
+ }
+
+ $item['method']['brand'] = implode( ' / ', array_filter( $brand_parts ) );
return $item;
}
From 80df0c9b2752076435f18f68cacfe4744adbf58a Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 24 Jan 2024 15:13:57 +0000
Subject: [PATCH 04/20] Fix lint
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 2 --
.../src/WooCommercePaymentTokens.php | 6 +++---
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index e9ec47bde..9786805a4 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -210,8 +210,6 @@ class ApplepayButton {
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
async onButtonClick(data, actions) {
- console.log('data, actions', data, actions);
-
this.log('onButtonClick', this.context);
const paymentRequest = this.paymentRequest();
diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
index 3e5f99b04..e57198a0d 100644
--- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
+++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
@@ -66,9 +66,9 @@ class WooCommercePaymentTokens {
/**
* Creates a WC Payment Token for PayPal payment.
*
- * @param int $customer_id The WC customer ID.
- * @param string $token The PayPal payment token.
- * @param string $email The PayPal customer email.
+ * @param int $customer_id The WC customer ID.
+ * @param string $token The PayPal payment token.
+ * @param string $email The PayPal customer email.
* @param string $payment_source The funding source.
*
* @return int
From 3b4bab512fd8d6849230ef2a265391e88aeafc5b Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 24 Jan 2024 18:05:00 +0000
Subject: [PATCH 05/20] Add process merchant payments for ApplePay vaulted
tokens
---
.../src/RenewalHandler.php | 29 ++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
index 636de372d..9d0f2f691 100644
--- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
+++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
@@ -22,6 +22,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
+use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
@@ -202,11 +203,31 @@ class RenewalHandler {
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_order->get_customer_id(), PayPalGateway::ID );
foreach ( $wc_tokens as $token ) {
+ $name = 'paypal';
+ $properties = array(
+ 'vault_id' => $token->get_token(),
+ );
+
+ if ( $token instanceof PaymentTokenPayPal ) {
+ $payment_source_name = $token->get_payment_source();
+
+ if ( $payment_source_name ) {
+ $name = $payment_source_name;
+ }
+
+ // Add required stored_credentials for apple_pay
+ if ( $payment_source_name === 'apple_pay' ) {
+ $properties['stored_credential'] = array(
+ 'payment_initiator' => 'MERCHANT',
+ 'payment_type' => 'RECURRING',
+ 'usage' => 'SUBSEQUENT',
+ );
+ }
+ }
+
$payment_source = new PaymentSource(
- 'paypal',
- (object) array(
- 'vault_id' => $token->get_token(),
- )
+ $name,
+ (object) $properties
);
break;
From 94b92d84345f319aecf1d54d1b0be9e6ae08217f Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 24 Jan 2024 18:15:50 +0000
Subject: [PATCH 06/20] Remove not needed code.
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 2 +-
modules/ppcp-blocks/resources/js/checkout-block.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 9786805a4..89015d673 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -209,7 +209,7 @@ class ApplepayButton {
/**
* Show Apple Pay payment sheet when Apple Pay payment button is clicked
*/
- async onButtonClick(data, actions) {
+ async onButtonClick() {
this.log('onButtonClick', this.context);
const paymentRequest = this.paymentRequest();
diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js
index d999efa80..0c01962cf 100644
--- a/modules/ppcp-blocks/resources/js/checkout-block.js
+++ b/modules/ppcp-blocks/resources/js/checkout-block.js
@@ -77,7 +77,7 @@ const PayPalComponent = ({
window.ppcpContinuationFilled = true;
}, [])
- const createOrder = async (data, actions) => {
+ const createOrder = async () => {
try {
const res = await fetch(config.scriptData.ajax.create_order.endpoint, {
method: 'POST',
From b3b6693aa270f762d9130e3c33f059b36d5fb7b2 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 24 Jan 2024 18:25:17 +0000
Subject: [PATCH 07/20] Fix lint
---
modules/ppcp-wc-subscriptions/src/RenewalHandler.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
index 9d0f2f691..e3ce5bbfd 100644
--- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
+++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
@@ -203,7 +203,7 @@ class RenewalHandler {
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $wc_order->get_customer_id(), PayPalGateway::ID );
foreach ( $wc_tokens as $token ) {
- $name = 'paypal';
+ $name = 'paypal';
$properties = array(
'vault_id' => $token->get_token(),
);
@@ -215,7 +215,7 @@ class RenewalHandler {
$name = $payment_source_name;
}
- // Add required stored_credentials for apple_pay
+ // Add required stored_credentials for apple_pay.
if ( $payment_source_name === 'apple_pay' ) {
$properties['stored_credential'] = array(
'payment_initiator' => 'MERCHANT',
From 0d4fbc4c0b3d1212139dbf43ca82441daeff85aa Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 26 Jan 2024 14:22:20 +0000
Subject: [PATCH 08/20] Refactor Venmo and ApplePay payment tokens.
---
.../src/SavePaymentMethodsModule.php | 29 ++++--
.../src/WooCommercePaymentTokens.php | 90 +++++++++++++++++--
.../src/PaymentTokenApplePay.php | 31 +++++++
.../ppcp-vaulting/src/PaymentTokenFactory.php | 6 +-
.../ppcp-vaulting/src/PaymentTokenHelper.php | 11 ++-
.../ppcp-vaulting/src/PaymentTokenPayPal.php | 21 +----
.../ppcp-vaulting/src/PaymentTokenVenmo.php | 51 +++++++++++
.../src/PaymentTokensMigration.php | 2 +-
modules/ppcp-vaulting/src/VaultingModule.php | 37 ++++----
.../src/RenewalHandler.php | 26 +++---
10 files changed, 234 insertions(+), 70 deletions(-)
create mode 100644 modules/ppcp-vaulting/src/PaymentTokenApplePay.php
create mode 100644 modules/ppcp-vaulting/src/PaymentTokenVenmo.php
diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
index e79ec11f6..5ea2a78dd 100644
--- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
+++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
@@ -188,12 +188,29 @@ class SavePaymentMethodsModule implements ModuleInterface {
}
if ( $wc_order->get_payment_method() === PayPalGateway::ID ) {
- $wc_payment_tokens->create_payment_token_paypal(
- $wc_order->get_customer_id(),
- $token_id,
- $payment_source->properties()->email_address ?? '',
- $payment_source->name()
- );
+ switch ( $payment_source->name() ) {
+ case 'venmo':
+ $wc_payment_tokens->create_payment_token_venmo(
+ $wc_order->get_customer_id(),
+ $token_id,
+ $payment_source->properties()->email_address ?? ''
+ );
+ break;
+ case 'apple_pay':
+ $wc_payment_tokens->create_payment_token_applepay(
+ $wc_order->get_customer_id(),
+ $token_id
+ );
+ break;
+ case 'paypal':
+ default:
+ $wc_payment_tokens->create_payment_token_paypal(
+ $wc_order->get_customer_id(),
+ $token_id,
+ $payment_source->properties()->email_address ?? ''
+ );
+ break;
+ }
}
}
},
diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
index e57198a0d..c878f10d1 100644
--- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
+++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
@@ -14,9 +14,11 @@ use Psr\Log\LoggerInterface;
use stdClass;
use WC_Payment_Token_CC;
use WC_Payment_Tokens;
+use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenApplePay;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenFactory;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenHelper;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
+use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenVenmo;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@@ -69,19 +71,17 @@ class WooCommercePaymentTokens {
* @param int $customer_id The WC customer ID.
* @param string $token The PayPal payment token.
* @param string $email The PayPal customer email.
- * @param string $payment_source The funding source.
*
* @return int
*/
public function create_payment_token_paypal(
int $customer_id,
string $token,
- string $email,
- string $payment_source = ''
+ string $email
): int {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
- if ( $this->payment_token_helper->token_exist( $wc_tokens, $token ) ) {
+ if ( $this->payment_token_helper->token_exist( $wc_tokens, $token, PaymentTokenPayPal::class ) ) {
return 0;
}
@@ -96,10 +96,6 @@ class WooCommercePaymentTokens {
$payment_token_paypal->set_email( $email );
}
- if ( $payment_source ) {
- $payment_token_paypal->set_payment_source( $payment_source );
- }
-
try {
$payment_token_paypal->save();
} catch ( Exception $exception ) {
@@ -111,6 +107,84 @@ class WooCommercePaymentTokens {
return $payment_token_paypal->get_id();
}
+ /**
+ * Creates a WC Payment Token for Venmo payment.
+ *
+ * @param int $customer_id The WC customer ID.
+ * @param string $token The Venmo payment token.
+ * @param string $email The Venmo customer email.
+ *
+ * @return int
+ */
+ public function create_payment_token_venmo(
+ int $customer_id,
+ string $token,
+ string $email
+ ): int {
+
+ $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
+ if ( $this->payment_token_helper->token_exist( $wc_tokens, $token, PaymentTokenVenmo::class ) ) {
+ return 0;
+ }
+
+ $payment_token_venmo = $this->payment_token_factory->create( 'venmo' );
+ assert( $payment_token_venmo instanceof PaymentTokenVenmo );
+
+ $payment_token_venmo->set_token( $token );
+ $payment_token_venmo->set_user_id( $customer_id );
+ $payment_token_venmo->set_gateway_id( PayPalGateway::ID );
+
+ if ( $email && is_email( $email ) ) {
+ $payment_token_venmo->set_email( $email );
+ }
+
+ try {
+ $payment_token_venmo->save();
+ } catch ( Exception $exception ) {
+ $this->logger->error(
+ "Could not create WC payment token Venmo for customer {$customer_id}. " . $exception->getMessage()
+ );
+ }
+
+ return $payment_token_venmo->get_id();
+ }
+
+ /**
+ * Creates a WC Payment Token for ApplePay payment.
+ *
+ * @param int $customer_id The WC customer ID.
+ * @param string $token The ApplePay payment token.
+ *
+ * @return int
+ */
+ public function create_payment_token_applepay(
+ int $customer_id,
+ string $token
+ ): int {
+
+ $wc_tokens = WC_Payment_Tokens::get_customer_tokens( $customer_id, PayPalGateway::ID );
+ if ( $this->payment_token_helper->token_exist( $wc_tokens, $token, PaymentTokenApplePay::class ) ) {
+ return 0;
+ }
+
+ $payment_token_applepay = $this->payment_token_factory->create( 'apple_pay' );
+ assert( $payment_token_applepay instanceof PaymentTokenApplePay );
+
+ $payment_token_applepay->set_token( $token );
+ $payment_token_applepay->set_user_id( $customer_id );
+ $payment_token_applepay->set_gateway_id( PayPalGateway::ID );
+
+ try {
+ $payment_token_applepay->save();
+ } catch ( Exception $exception ) {
+ $this->logger->error(
+ "Could not create WC payment token ApplePay for customer {$customer_id}. " . $exception->getMessage()
+ );
+ }
+
+ return $payment_token_applepay->get_id();
+ }
+
/**
* Creates a WC Payment Token for Credit Card payment.
*
diff --git a/modules/ppcp-vaulting/src/PaymentTokenApplePay.php b/modules/ppcp-vaulting/src/PaymentTokenApplePay.php
new file mode 100644
index 000000000..a53aa254a
--- /dev/null
+++ b/modules/ppcp-vaulting/src/PaymentTokenApplePay.php
@@ -0,0 +1,31 @@
+get_token() === $token_id ) {
- return true;
+ if ( null !== $class_name ) {
+ if ( $wc_token instanceof $class_name ) {
+ return true;
+ }
+ } else {
+ return true;
+ }
}
}
diff --git a/modules/ppcp-vaulting/src/PaymentTokenPayPal.php b/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
index 3b8eac849..1a5858118 100644
--- a/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
+++ b/modules/ppcp-vaulting/src/PaymentTokenPayPal.php
@@ -28,8 +28,7 @@ class PaymentTokenPayPal extends WC_Payment_Token {
* @var string[]
*/
protected $extra_data = array(
- 'email' => '',
- 'payment_source' => '',
+ 'email' => '',
);
/**
@@ -49,22 +48,4 @@ class PaymentTokenPayPal extends WC_Payment_Token {
public function set_email( $email ) {
$this->add_meta_data( 'email', $email, true );
}
-
- /**
- * Get the payment source.
- *
- * @return string The payment source.
- */
- public function get_payment_source() {
- return $this->get_meta( 'payment_source' );
- }
-
- /**
- * Set the payment source.
- *
- * @param string $payment_source The payment source.
- */
- public function set_payment_source( string $payment_source ) {
- $this->add_meta_data( 'payment_source', $payment_source, true );
- }
}
diff --git a/modules/ppcp-vaulting/src/PaymentTokenVenmo.php b/modules/ppcp-vaulting/src/PaymentTokenVenmo.php
new file mode 100644
index 000000000..d53a4b4fb
--- /dev/null
+++ b/modules/ppcp-vaulting/src/PaymentTokenVenmo.php
@@ -0,0 +1,51 @@
+ '',
+ );
+
+ /**
+ * Get PayPal account email.
+ *
+ * @return string PayPal account email.
+ */
+ public function get_email() {
+ return $this->get_meta( 'email' );
+ }
+
+ /**
+ * Set PayPal account email.
+ *
+ * @param string $email PayPal account email.
+ */
+ public function set_email( $email ) {
+ $this->add_meta_data( 'email', $email, true );
+ }
+}
diff --git a/modules/ppcp-vaulting/src/PaymentTokensMigration.php b/modules/ppcp-vaulting/src/PaymentTokensMigration.php
index a7d3511cc..1ef41df4a 100644
--- a/modules/ppcp-vaulting/src/PaymentTokensMigration.php
+++ b/modules/ppcp-vaulting/src/PaymentTokensMigration.php
@@ -107,7 +107,7 @@ class PaymentTokensMigration {
}
} elseif ( $token->source()->paypal ) {
$wc_tokens = WC_Payment_Tokens::get_customer_tokens( $id, PayPalGateway::ID );
- if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id() ) ) {
+ if ( $this->payment_token_helper->token_exist( $wc_tokens, $token->id(), PaymentTokenPayPal::class ) ) {
$this->logger->info( 'Token already exist for user ' . (string) $id );
continue;
}
diff --git a/modules/ppcp-vaulting/src/VaultingModule.php b/modules/ppcp-vaulting/src/VaultingModule.php
index 1860a889d..ff8acdf3b 100644
--- a/modules/ppcp-vaulting/src/VaultingModule.php
+++ b/modules/ppcp-vaulting/src/VaultingModule.php
@@ -81,6 +81,12 @@ class VaultingModule implements ModuleInterface {
if ( $type === 'WC_Payment_Token_PayPal' ) {
return PaymentTokenPayPal::class;
}
+ if ( $type === 'WC_Payment_Token_Venmo' ) {
+ return PaymentTokenVenmo::class;
+ }
+ if ( $type === 'WC_Payment_Token_ApplePay' ) {
+ return PaymentTokenApplePay::class;
+ }
return $type;
}
@@ -102,10 +108,7 @@ class VaultingModule implements ModuleInterface {
// Exclude ApplePay tokens from payment pages.
if ( is_checkout() || is_cart() || is_product() ) {
foreach ( $tokens as $index => $token ) {
- if (
- $token instanceof PaymentTokenPayPal
- && $token->get_payment_source() === 'apple_pay'
- ) {
+ if ( $token instanceof PaymentTokenApplePay ) {
unset( $tokens[ $index ] );
}
}
@@ -129,24 +132,18 @@ class VaultingModule implements ModuleInterface {
return $item;
}
- if ( strtolower( $payment_token->get_type() ) === 'paypal' ) {
- assert( $payment_token instanceof PaymentTokenPayPal );
+ if ( $payment_token instanceof PaymentTokenPayPal ) {
+ $item['method']['brand'] = 'PayPal / ' . $payment_token->get_email();
+ return $item;
+ }
- $email = $payment_token->get_email();
- $payment_source = $payment_token->get_payment_source();
- $brand_parts = array();
+ if ( $payment_token instanceof PaymentTokenVenmo ) {
+ $item['method']['brand'] = 'Venmo / ' . $payment_token->get_email();
+ return $item;
+ }
- if ( $payment_source !== 'paypal' ) {
- $brand_parts[] = ucwords( $payment_source );
- }
-
- if ( $email ) {
- $brand_parts[] = $email;
- } else {
- $brand_parts[] = '#' . ( (string) $payment_token->get_id() );
- }
-
- $item['method']['brand'] = implode( ' / ', array_filter( $brand_parts ) );
+ if ( $payment_token instanceof PaymentTokenApplePay ) {
+ $item['method']['brand'] = 'ApplePay #' . ( (string) $payment_token->get_id() );
return $item;
}
diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
index e3ce5bbfd..a4ce028ee 100644
--- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
+++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
@@ -22,9 +22,11 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory;
use WooCommerce\PayPalCommerce\Onboarding\Environment;
+use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenApplePay;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenPayPal;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use Psr\Log\LoggerInterface;
+use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenVenmo;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
@@ -209,20 +211,20 @@ class RenewalHandler {
);
if ( $token instanceof PaymentTokenPayPal ) {
- $payment_source_name = $token->get_payment_source();
+ $name = 'paypal';
+ }
- if ( $payment_source_name ) {
- $name = $payment_source_name;
- }
+ if ( $token instanceof PaymentTokenVenmo ) {
+ $name = 'venmo';
+ }
- // Add required stored_credentials for apple_pay.
- if ( $payment_source_name === 'apple_pay' ) {
- $properties['stored_credential'] = array(
- 'payment_initiator' => 'MERCHANT',
- 'payment_type' => 'RECURRING',
- 'usage' => 'SUBSEQUENT',
- );
- }
+ if ( $token instanceof PaymentTokenApplePay ) {
+ $name = 'apple_pay';
+ $properties['stored_credential'] = array(
+ 'payment_initiator' => 'MERCHANT',
+ 'payment_type' => 'RECURRING',
+ 'usage' => 'SUBSEQUENT',
+ );
}
$payment_source = new PaymentSource(
From 2167c7c78b4215ce0c40a112799482f097492b3f Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 26 Jan 2024 17:57:59 +0000
Subject: [PATCH 09/20] Add permit multiple tokens so Venmo can work alongside
PayPal
---
.../src/SavePaymentMethodsModule.php | 10 ++++++----
.../src/WcSubscriptionsModule.php | 2 +-
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
index 5ea2a78dd..2d4fd8ec6 100644
--- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
+++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
@@ -113,8 +113,9 @@ class SavePaymentMethodsModule implements ModuleInterface {
'venmo' => array(
'attributes' => array(
'vault' => array(
- 'store_in_vault' => 'ON_SUCCESS',
- 'usage_type' => 'MERCHANT',
+ 'store_in_vault' => 'ON_SUCCESS',
+ 'usage_type' => 'MERCHANT',
+ 'permit_multiple_payment_tokens' => true,
),
),
),
@@ -138,8 +139,9 @@ class SavePaymentMethodsModule implements ModuleInterface {
'paypal' => array(
'attributes' => array(
'vault' => array(
- 'store_in_vault' => 'ON_SUCCESS',
- 'usage_type' => 'MERCHANT',
+ 'store_in_vault' => 'ON_SUCCESS',
+ 'usage_type' => 'MERCHANT',
+ 'permit_multiple_payment_tokens' => true,
),
),
),
diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
index ea20d5c69..573711a3d 100644
--- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
+++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
@@ -301,7 +301,7 @@ class WcSubscriptionsModule implements ModuleInterface {
foreach ( $tokens as $token ) {
$output .= '';
$output .= sprintf( '', $token->get_id() );
- $output .= sprintf( '', $token->get_meta( 'email' ) ?? '' );
+ $output .= sprintf( '', $token->get_type(), $token->get_meta( 'email' ) ?? '' );
$output .= '';
}
$output .= '';
From be7ece91da8dd37f5c4b2218f7012bb2bf5579cd Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 26 Jan 2024 18:01:39 +0000
Subject: [PATCH 10/20] Fix lint
---
.../src/SavePaymentMethodsModule.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
index 2d4fd8ec6..f9032e681 100644
--- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
+++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php
@@ -113,8 +113,8 @@ class SavePaymentMethodsModule implements ModuleInterface {
'venmo' => array(
'attributes' => array(
'vault' => array(
- 'store_in_vault' => 'ON_SUCCESS',
- 'usage_type' => 'MERCHANT',
+ 'store_in_vault' => 'ON_SUCCESS',
+ 'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
),
),
@@ -139,8 +139,8 @@ class SavePaymentMethodsModule implements ModuleInterface {
'paypal' => array(
'attributes' => array(
'vault' => array(
- 'store_in_vault' => 'ON_SUCCESS',
- 'usage_type' => 'MERCHANT',
+ 'store_in_vault' => 'ON_SUCCESS',
+ 'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
),
),
From c342a4a0d8551962e1fad717b070b81569c67659 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Tue, 30 Jan 2024 17:46:28 +0000
Subject: [PATCH 11/20] Add ApplePay subscription validations in block pages.
---
modules/ppcp-applepay/resources/js/ApplepayButton.js | 4 ----
modules/ppcp-applepay/resources/js/Context/BaseHandler.js | 7 ++++++-
.../ppcp-applepay/resources/js/Context/PayNowHandler.js | 2 +-
.../resources/js/Context/SingleProductHandler.js | 2 +-
modules/ppcp-applepay/resources/js/boot-block.js | 5 +++++
5 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js
index 89015d673..3526a0f03 100644
--- a/modules/ppcp-applepay/resources/js/ApplepayButton.js
+++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js
@@ -70,10 +70,6 @@ class ApplepayButton {
if (this.isEligible) {
this.fetchTransactionInfo().then(() => {
- const isSubscriptionProduct = this.ppcpConfig?.data_client_id?.has_subscriptions === true;
- if (isSubscriptionProduct) {
- return;
- }
this.addButton();
const id_minicart = "#apple-" + this.buttonConfig.button.mini_cart_wrapper;
const id = "#apple-" + this.buttonConfig.button.wrapper;
diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
index 61d614ab9..02d8ca0b2 100644
--- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
@@ -9,9 +9,14 @@ class BaseHandler {
this.ppcpConfig = ppcpConfig;
}
+ isVaultV3Mode() {
+ return this.ppcpConfig?.save_payment_methods?.id_token // vault v3
+ && ! this.ppcpConfig.data_client_id.paypal_subscriptions_enabled; // not PayPal Subscriptions mode
+ }
+
validateContext() {
if ( this.ppcpConfig?.locations_with_subscription_product?.cart ) {
- return false;
+ return this.isVaultV3Mode();
}
return true;
}
diff --git a/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js b/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js
index a580c4084..a12fd3636 100644
--- a/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/PayNowHandler.js
@@ -7,7 +7,7 @@ class PayNowHandler extends BaseHandler {
validateContext() {
if ( this.ppcpConfig?.locations_with_subscription_product?.payorder ) {
- return false;
+ return this.isVaultV3Mode();
}
return true;
}
diff --git a/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js b/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js
index 5825a1f2c..5ad5857be 100644
--- a/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/SingleProductHandler.js
@@ -9,7 +9,7 @@ class SingleProductHandler extends BaseHandler {
validateContext() {
if ( this.ppcpConfig?.locations_with_subscription_product?.product ) {
- return false;
+ return this.isVaultV3Mode();
}
return true;
}
diff --git a/modules/ppcp-applepay/resources/js/boot-block.js b/modules/ppcp-applepay/resources/js/boot-block.js
index b8f905b6d..df20e67f5 100644
--- a/modules/ppcp-applepay/resources/js/boot-block.js
+++ b/modules/ppcp-applepay/resources/js/boot-block.js
@@ -1,6 +1,7 @@
import {useEffect, useState} from '@wordpress/element';
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
+import {cartHasSubscriptionProducts} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
import ApplepayManager from "./ApplepayManager";
import {loadCustomScript} from "@paypal/paypal-js";
@@ -50,6 +51,10 @@ const ApplePayComponent = () => {
const features = ['products'];
+if (cartHasSubscriptionProducts(ppcpConfig)) {
+ features.push('subscriptions');
+}
+
registerExpressPaymentMethod({
name: buttonData.id,
label: ,
From ebcc2ba3f9a77f6c07296291ce7f22f4382effe2 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Tue, 30 Jan 2024 18:22:53 +0000
Subject: [PATCH 12/20] Fix subscription validations
---
modules/ppcp-applepay/resources/js/boot-block.js | 10 ++++++++--
modules/ppcp-blocks/resources/js/checkout-block.js | 8 ++++++++
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/boot-block.js b/modules/ppcp-applepay/resources/js/boot-block.js
index df20e67f5..5dfb62b1f 100644
--- a/modules/ppcp-applepay/resources/js/boot-block.js
+++ b/modules/ppcp-applepay/resources/js/boot-block.js
@@ -1,7 +1,10 @@
import {useEffect, useState} from '@wordpress/element';
import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
-import {cartHasSubscriptionProducts} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
+import {
+ cartHasSubscriptionProducts,
+ isPayPalSubscription
+} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
import ApplepayManager from "./ApplepayManager";
import {loadCustomScript} from "@paypal/paypal-js";
@@ -51,7 +54,10 @@ const ApplePayComponent = () => {
const features = ['products'];
-if (cartHasSubscriptionProducts(ppcpConfig)) {
+if (cartHasSubscriptionProducts(ppcpConfig)
+ && ! isPayPalSubscription(ppcpConfig)
+ && ppcpConfig.can_save_vault_token
+) {
features.push('subscriptions');
}
diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js
index 0c01962cf..f14417e1c 100644
--- a/modules/ppcp-blocks/resources/js/checkout-block.js
+++ b/modules/ppcp-blocks/resources/js/checkout-block.js
@@ -477,6 +477,14 @@ if(cartHasSubscriptionProducts(config.scriptData)) {
block_enabled = false;
}
+ // Don't render if vaulting disabled and is in vault subscription mode
+ if(
+ ! isPayPalSubscription(config.scriptData)
+ && ! config.scriptData.can_save_vault_token
+ ) {
+ block_enabled = false;
+ }
+
// Don't render buttons if in subscription mode and product not associated with a PayPal subscription
if(
isPayPalSubscription(config.scriptData)
From 8d82e515068174c387b16473bcda5ba0bbccbb54 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 31 Jan 2024 10:32:21 +0000
Subject: [PATCH 13/20] Fix subscription validations
---
.../resources/js/Context/BaseHandler.js | 4 +++-
.../ppcp-applepay/resources/js/boot-block.js | 14 +++++------
.../src/WooCommercePaymentTokens.php | 24 ++++++++++++++++---
.../ppcp-vaulting/src/PaymentTokenHelper.php | 17 +++++++++++++
.../src/Settings/SettingsListener.php | 3 ++-
5 files changed, 49 insertions(+), 13 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
index 02d8ca0b2..ba3214c34 100644
--- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
@@ -1,6 +1,7 @@
import ErrorHandler from "../../../../ppcp-button/resources/js/modules/ErrorHandler";
import CartActionHandler
from "../../../../ppcp-button/resources/js/modules/ActionHandler/CartActionHandler";
+import {isPayPalSubscription} from "../../../../ppcp-blocks/resources/js/Helper/Subscription";
class BaseHandler {
@@ -11,7 +12,8 @@ class BaseHandler {
isVaultV3Mode() {
return this.ppcpConfig?.save_payment_methods?.id_token // vault v3
- && ! this.ppcpConfig.data_client_id.paypal_subscriptions_enabled; // not PayPal Subscriptions mode
+ && ! this.ppcpConfig?.data_client_id?.paypal_subscriptions_enabled // not PayPal Subscriptions mode
+ && this.ppcpConfig?.ppcpConfig.can_save_vault_token; // vault is enabled
}
validateContext() {
diff --git a/modules/ppcp-applepay/resources/js/boot-block.js b/modules/ppcp-applepay/resources/js/boot-block.js
index 5dfb62b1f..b1d29c9c9 100644
--- a/modules/ppcp-applepay/resources/js/boot-block.js
+++ b/modules/ppcp-applepay/resources/js/boot-block.js
@@ -1,12 +1,10 @@
import {useEffect, useState} from '@wordpress/element';
-import {registerExpressPaymentMethod, registerPaymentMethod} from '@woocommerce/blocks-registry';
+import {registerExpressPaymentMethod} from '@woocommerce/blocks-registry';
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
-import {
- cartHasSubscriptionProducts,
- isPayPalSubscription
-} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
+import {cartHasSubscriptionProducts} from '../../../ppcp-blocks/resources/js/Helper/Subscription'
import ApplepayManager from "./ApplepayManager";
import {loadCustomScript} from "@paypal/paypal-js";
+import CheckoutHandler from "./Context/CheckoutHandler";
const ppcpData = wc.wcSettings.getSetting('ppcp-gateway_data');
const ppcpConfig = ppcpData.scriptData;
@@ -54,9 +52,9 @@ const ApplePayComponent = () => {
const features = ['products'];
-if (cartHasSubscriptionProducts(ppcpConfig)
- && ! isPayPalSubscription(ppcpConfig)
- && ppcpConfig.can_save_vault_token
+if (
+ cartHasSubscriptionProducts(ppcpConfig)
+ && (new CheckoutHandler(buttonConfig, ppcpConfig)).isVaultV3Mode()
) {
features.push('subscriptions');
}
diff --git a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
index c878f10d1..b0f37a449 100644
--- a/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
+++ b/modules/ppcp-save-payment-methods/src/WooCommercePaymentTokens.php
@@ -85,7 +85,13 @@ class WooCommercePaymentTokens {
return 0;
}
- $payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
+ // Try to update existing token of type before creating a new one.
+ $payment_token_paypal = $this->payment_token_helper->first_token_of_type( $wc_tokens, PaymentTokenPayPal::class );
+
+ if ( ! $payment_token_paypal ) {
+ $payment_token_paypal = $this->payment_token_factory->create( 'paypal' );
+ }
+
assert( $payment_token_paypal instanceof PaymentTokenPayPal );
$payment_token_paypal->set_token( $token );
@@ -127,7 +133,13 @@ class WooCommercePaymentTokens {
return 0;
}
- $payment_token_venmo = $this->payment_token_factory->create( 'venmo' );
+ // Try to update existing token of type before creating a new one.
+ $payment_token_venmo = $this->payment_token_helper->first_token_of_type( $wc_tokens, PaymentTokenVenmo::class );
+
+ if ( ! $payment_token_venmo ) {
+ $payment_token_venmo = $this->payment_token_factory->create( 'venmo' );
+ }
+
assert( $payment_token_venmo instanceof PaymentTokenVenmo );
$payment_token_venmo->set_token( $token );
@@ -167,7 +179,13 @@ class WooCommercePaymentTokens {
return 0;
}
- $payment_token_applepay = $this->payment_token_factory->create( 'apple_pay' );
+ // Try to update existing token of type before creating a new one.
+ $payment_token_applepay = $this->payment_token_helper->first_token_of_type( $wc_tokens, PaymentTokenApplePay::class );
+
+ if ( ! $payment_token_applepay ) {
+ $payment_token_applepay = $this->payment_token_factory->create( 'apple_pay' );
+ }
+
assert( $payment_token_applepay instanceof PaymentTokenApplePay );
$payment_token_applepay->set_token( $token );
diff --git a/modules/ppcp-vaulting/src/PaymentTokenHelper.php b/modules/ppcp-vaulting/src/PaymentTokenHelper.php
index 9edac18a9..368e146b1 100644
--- a/modules/ppcp-vaulting/src/PaymentTokenHelper.php
+++ b/modules/ppcp-vaulting/src/PaymentTokenHelper.php
@@ -39,4 +39,21 @@ class PaymentTokenHelper {
return false;
}
+
+ /**
+ * Checks if given token exist as WC Payment Token.
+ *
+ * @param array $wc_tokens WC Payment Tokens.
+ * @param string $class_name Class name of the token.
+ * @return null|WC_Payment_Token
+ */
+ public function first_token_of_type( array $wc_tokens, string $class_name ) {
+ foreach ( $wc_tokens as $wc_token ) {
+ if ( $wc_token instanceof $class_name ) {
+ return $wc_token;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php
index 5f1bd38ad..35ff96fff 100644
--- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php
+++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php
@@ -346,9 +346,10 @@ class SettingsListener {
/**
* Prevent enabling both Pay Later messaging and PayPal vaulting
*
+ * @return void
* @throws RuntimeException When API request fails.
*/
- public function listen_for_vaulting_enabled() {
+ public function listen_for_vaulting_enabled(): void {
if ( ! $this->is_valid_site_request() || State::STATE_ONBOARDED !== $this->state->current_state() ) {
return;
}
From 0cf3f946c6373531bfeb1b67eb505c9d708933de Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 31 Jan 2024 14:59:47 +0000
Subject: [PATCH 14/20] Fix ApplePay token update
---
modules/ppcp-applepay/resources/js/Context/BaseHandler.js | 2 +-
modules/ppcp-vaulting/src/VaultingModule.php | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
index ba3214c34..69745082e 100644
--- a/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
+++ b/modules/ppcp-applepay/resources/js/Context/BaseHandler.js
@@ -13,7 +13,7 @@ class BaseHandler {
isVaultV3Mode() {
return this.ppcpConfig?.save_payment_methods?.id_token // vault v3
&& ! this.ppcpConfig?.data_client_id?.paypal_subscriptions_enabled // not PayPal Subscriptions mode
- && this.ppcpConfig?.ppcpConfig.can_save_vault_token; // vault is enabled
+ && this.ppcpConfig?.can_save_vault_token; // vault is enabled
}
validateContext() {
diff --git a/modules/ppcp-vaulting/src/VaultingModule.php b/modules/ppcp-vaulting/src/VaultingModule.php
index 497ebecbe..5216b82c3 100644
--- a/modules/ppcp-vaulting/src/VaultingModule.php
+++ b/modules/ppcp-vaulting/src/VaultingModule.php
@@ -105,8 +105,13 @@ class VaultingModule implements ModuleInterface {
return $tokens;
}
+ $is_post = isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST';
+
// Exclude ApplePay tokens from payment pages.
- if ( is_checkout() || is_cart() || is_product() ) {
+ if (
+ ( is_checkout() || is_cart() || is_product() )
+ && ! $is_post // Don't check on POST so we have all payment methods on form submissions.
+ ) {
foreach ( $tokens as $index => $token ) {
if ( $token instanceof PaymentTokenApplePay ) {
unset( $tokens[ $index ] );
From 77741f584371e0a2c9b50b537bc87b66c8e4affb Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Thu, 1 Feb 2024 09:43:50 +0000
Subject: [PATCH 15/20] Fix unset property warning
---
.../src/Factory/FraudProcessorResponseFactory.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php b/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
index e48d7fd3e..86ac2d7ee 100644
--- a/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
+++ b/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
@@ -25,8 +25,8 @@ class FraudProcessorResponseFactory {
* @return FraudProcessorResponse
*/
public function from_paypal_response( stdClass $data ): FraudProcessorResponse {
- $avs_code = $data->avs_code ?: null;
- $cvv_code = $data->cvv_code ?: null;
+ $avs_code = ($data->avs_code ?? null) ?: null;
+ $cvv_code = ($data->cvv_code ?? null) ?: null;
return new FraudProcessorResponse( $avs_code, $cvv_code );
}
From 977f9836e295442bb5e6a1341038780856fd6697 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Thu, 1 Feb 2024 09:53:26 +0000
Subject: [PATCH 16/20] Fix lint
---
.../src/Factory/FraudProcessorResponseFactory.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php b/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
index 86ac2d7ee..2a2e0b68c 100644
--- a/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
+++ b/modules/ppcp-api-client/src/Factory/FraudProcessorResponseFactory.php
@@ -25,8 +25,8 @@ class FraudProcessorResponseFactory {
* @return FraudProcessorResponse
*/
public function from_paypal_response( stdClass $data ): FraudProcessorResponse {
- $avs_code = ($data->avs_code ?? null) ?: null;
- $cvv_code = ($data->cvv_code ?? null) ?: null;
+ $avs_code = ( $data->avs_code ?? null ) ?: null;
+ $cvv_code = ( $data->cvv_code ?? null ) ?: null;
return new FraudProcessorResponse( $avs_code, $cvv_code );
}
From 6a99a9befcd0e5cd1133b0701b645d02e8940e12 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Mon, 5 Feb 2024 16:30:19 +0000
Subject: [PATCH 17/20] Fix update payment source on WC Order subscription
renewals.
---
modules/ppcp-wc-subscriptions/services.php | 4 +-
.../src/RenewalHandler.php | 43 ++++++++++++++++++-
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-wc-subscriptions/services.php b/modules/ppcp-wc-subscriptions/services.php
index 86599b3f2..e9b324b07 100644
--- a/modules/ppcp-wc-subscriptions/services.php
+++ b/modules/ppcp-wc-subscriptions/services.php
@@ -27,6 +27,7 @@ return array(
$environment = $container->get( 'onboarding.environment' );
$settings = $container->get( 'wcgateway.settings' );
$authorized_payments_processor = $container->get( 'wcgateway.processor.authorized-payments' );
+ $funding_source_renderer = $container->get( 'wcgateway.funding-source.renderer' );
return new RenewalHandler(
$logger,
$repository,
@@ -36,7 +37,8 @@ return array(
$payer_factory,
$environment,
$settings,
- $authorized_payments_processor
+ $authorized_payments_processor,
+ $funding_source_renderer
);
},
'wc-subscriptions.repository.payment-token' => static function ( ContainerInterface $container ): PaymentTokenRepository {
diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
index a4ce028ee..06d223c35 100644
--- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
+++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
@@ -28,6 +28,7 @@ use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenVenmo;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
+use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor;
@@ -108,6 +109,13 @@ class RenewalHandler {
*/
protected $authorized_payments_processor;
+ /**
+ * The funding source renderer.
+ *
+ * @var FundingSourceRenderer
+ */
+ protected $funding_source_renderer;
+
/**
* RenewalHandler constructor.
*
@@ -120,6 +128,7 @@ class RenewalHandler {
* @param Environment $environment The environment.
* @param Settings $settings The Settings.
* @param AuthorizedPaymentsProcessor $authorized_payments_processor The Authorized Payments Processor.
+ * @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
*/
public function __construct(
LoggerInterface $logger,
@@ -130,7 +139,8 @@ class RenewalHandler {
PayerFactory $payer_factory,
Environment $environment,
Settings $settings,
- AuthorizedPaymentsProcessor $authorized_payments_processor
+ AuthorizedPaymentsProcessor $authorized_payments_processor,
+ FundingSourceRenderer $funding_source_renderer
) {
$this->logger = $logger;
@@ -142,6 +152,7 @@ class RenewalHandler {
$this->environment = $environment;
$this->settings = $settings;
$this->authorized_payments_processor = $authorized_payments_processor;
+ $this->funding_source_renderer = $funding_source_renderer;
}
/**
@@ -410,6 +421,11 @@ class RenewalHandler {
if ( $transaction_id ) {
$this->update_transaction_id( $transaction_id, $wc_order );
+ $payment_source = $order->payment_source();
+ if ( $payment_source instanceof PaymentSource ) {
+ $this->update_payment_source( $payment_source, $wc_order );
+ }
+
$subscriptions = wcs_get_subscriptions_for_order( $wc_order->get_id(), array( 'order_type' => 'any' ) );
foreach ( $subscriptions as $id => $subscription ) {
$subscription->update_meta_data( 'ppcp_previous_transaction_reference', $transaction_id );
@@ -463,4 +479,29 @@ class RenewalHandler {
(object) $properties
);
}
+
+ /**
+ * Updates the payment source name to the one really used for the payment.
+ *
+ * @param PaymentSource $payment_source
+ * @param \WC_Order $wc_order
+ * @return void
+ */
+ private function update_payment_source( PaymentSource $payment_source, \WC_Order $wc_order ): void {
+ if ( ! $payment_source->name() ) {
+ return;
+ }
+ try {
+ $wc_order->set_payment_method_title( $this->funding_source_renderer->render_name( $payment_source->name() ) );
+ $wc_order->save();
+ } catch ( \Exception $e ) {
+ $this->logger->error(
+ sprintf(
+ 'Failed to update payment source to "%1$s" on order %2$d',
+ $payment_source->name(),
+ $wc_order->get_id()
+ )
+ );
+ }
+ }
}
From 9f859da8cb662d05277a186c99544fe772b52f90 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Mon, 5 Feb 2024 16:43:22 +0000
Subject: [PATCH 18/20] Fix lint
---
modules/ppcp-wc-subscriptions/src/RenewalHandler.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
index 06d223c35..b559c18c6 100644
--- a/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
+++ b/modules/ppcp-wc-subscriptions/src/RenewalHandler.php
@@ -483,8 +483,8 @@ class RenewalHandler {
/**
* Updates the payment source name to the one really used for the payment.
*
- * @param PaymentSource $payment_source
- * @param \WC_Order $wc_order
+ * @param PaymentSource $payment_source The Payment Source.
+ * @param \WC_Order $wc_order WC order.
* @return void
*/
private function update_payment_source( PaymentSource $payment_source, \WC_Order $wc_order ): void {
From 8266e1bce7c7c9bb33301f573db0e3ff6cf0cd8c Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Mon, 5 Feb 2024 18:26:41 +0000
Subject: [PATCH 19/20] Fix subscription initial payment method name
---
modules/ppcp-blocks/resources/js/checkout-block.js | 2 --
.../src/WcSubscriptionsModule.php | 11 +++++++++++
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js
index f14417e1c..5e6450045 100644
--- a/modules/ppcp-blocks/resources/js/checkout-block.js
+++ b/modules/ppcp-blocks/resources/js/checkout-block.js
@@ -326,8 +326,6 @@ const PayPalComponent = ({
};
handleSubscriptionShippingChange = async (data, actions) => {
- console.log('--- handleSubscriptionShippingChange', data, actions);
-
try {
const shippingOptionId = data.selected_shipping_option?.id;
if (shippingOptionId) {
diff --git a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
index 3335d24e9..994e139b7 100644
--- a/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
+++ b/modules/ppcp-wc-subscriptions/src/WcSubscriptionsModule.php
@@ -111,6 +111,17 @@ class WcSubscriptionsModule implements ModuleInterface {
$subscription->update_meta_data( 'ppcp_previous_transaction_reference', $transaction_id );
$subscription->save();
}
+
+ // Update the initial payment method title if not the same as the first order.
+ $payment_method_title = $parent_order->get_payment_method_title();
+ if (
+ $payment_method_title
+ && $subscription instanceof \WC_Subscription
+ && $subscription->get_payment_method_title() !== $payment_method_title
+ ) {
+ $subscription->set_payment_method_title( $payment_method_title );
+ $subscription->save();
+ }
}
}
}
From 52720872de7f92254c42467257faa5c4e868bc7d Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Tue, 6 Feb 2024 17:40:39 +0000
Subject: [PATCH 20/20] Fix apple pay funding source renderer.
---
.../src/FundingSource/FundingSourceRenderer.php | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php b/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php
index a465db721..071410615 100644
--- a/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php
+++ b/modules/ppcp-wc-gateway/src/FundingSource/FundingSourceRenderer.php
@@ -56,6 +56,8 @@ class FundingSourceRenderer {
* @param string $id The ID of the funding source, such as 'venmo'.
*/
public function render_name( string $id ): string {
+ $id = $this->sanitize_id( $id );
+
if ( array_key_exists( $id, $this->funding_sources ) ) {
if ( in_array( $id, $this->own_funding_sources, true ) ) {
return $this->funding_sources[ $id ];
@@ -78,6 +80,8 @@ class FundingSourceRenderer {
* @param string $id The ID of the funding source, such as 'venmo'.
*/
public function render_description( string $id ): string {
+ $id = $this->sanitize_id( $id );
+
if ( array_key_exists( $id, $this->funding_sources ) ) {
return sprintf(
/* translators: %s - Sofort, BLIK, iDeal, Mercado Pago, etc. */
@@ -90,4 +94,14 @@ class FundingSourceRenderer {
$this->settings->get( 'description' )
: __( 'Pay via PayPal.', 'woocommerce-paypal-payments' );
}
+
+ /**
+ * Sanitizes the id to a standard format.
+ *
+ * @param string $id The funding source id.
+ * @return string
+ */
+ private function sanitize_id( string $id ): string {
+ return str_replace( '_', '', strtolower( $id ) );
+ }
}