diff --git a/.ddev/config.yaml b/.ddev/config.yaml
index 041455441..9fc8d4a29 100644
--- a/.ddev/config.yaml
+++ b/.ddev/config.yaml
@@ -27,7 +27,7 @@ web_environment:
- ADMIN_USER=admin
- ADMIN_PASS=admin
- ADMIN_EMAIL=admin@example.com
- - WC_VERSION=7.7.2
+ - WC_VERSION=9.5.1
# Key features of ddev's config.yaml:
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index e7957d210..c3e07d1bb 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -50,7 +50,7 @@ jobs:
if: github.event.inputs.filePrefix
- name: Upload
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: ${{ env.FILENAME }}
path: dist/
diff --git a/changelog.txt b/changelog.txt
index b0ffc609c..1efeb16a3 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,22 +1,34 @@
*** Changelog ***
+= 2.9.6 - XXXX-XX-XX =
+* Fix - NOT_ENABLED_TO_VAULT_PAYMENT_SOURCE on PayPal transactions when using ACDC Vaulting without PayPal Vault approval #2955
+* Fix - Express buttons for Free Trial Subscription products on Block Cart/Checkout trigger CANNOT_BE_ZERO_OR_NEGATIVE error #2872
+* Fix - String translations not applied to Card Fields on Block Checkout #2934
+* Fix - Fastlane component included in script when Fastlane is disabled #2911
+* Fix - Zero amount line items may trigger CANNOT_BE_ZERO_OR_NEGATIVE error after rounding error #2906
+* Fix - “Save changes” is grey and unclickable when switching from Sandbox to Live #2895
+* Fix - plugin queries variations when button/messaging is disabled on single product page #2896
+* Fix - Use get_id instead of get_order_number on setting custom_id (author @0verscore) #2930
+* Enhancement - Improve fraud response order notes for Advanced Card Processing transactions #2905
+* Tweak - Update the minimum plugin requirements to WordPress 6.5 & WooCommerce 9.2 #2920
+
= 2.9.5 - 2024-12-10 =
-Fix - Early translation loading triggers `Function _load_textdomain_just_in_time was called incorrectly.` notice #2816
-Fix - ACDC card fields not loading and payment not successful when Classic Checkout Smart Button Location disabled #2852
-Fix - ACDC gateway does not appear for guests when is Fastlane enabled and a subscription product is in the cart #2745
-Fix - "Voide authorization" button does not appear for Apple Pay/Google Pay orders when payment buttons are separated #2752
-Fix - Additional payment tokens saved with new customer_id #2820
-Fix - Vaulted payment method may not be displayed in PayPal button for return buyer #2809
-Fix - Conflict with EasyShip plugin due to shipping methods loading too early #2845
-Fix - Restore accidentally removed ACDC currencies #2838
-Enhancement - Native gateway icon for PayPal & Pay upon Invoice gateways #2712
-Enhancement - Allow disabling specific card types for Fastlane #2704
-Enhancement - Fastlane Insights SDK implementation for block Checkout #2737
-Enhancement - Hide split local APMs in Payments settings tab when PayPal is not enabled #2703
-Enhancement - Do not load split local APMs on Checkout when PayPal is not enabled #2792
-Enhancement - Add support for Button Options in the Block Checkout for Apple Pay & Google Pay buttons #2797 #2772
-Enhancement - Disable “Add payment method” button while saving ACDC payment #2794
-Enhancement - Sanitize soft_descriptor field #2846 #2854
+* Fix - Early translation loading triggers `Function _load_textdomain_just_in_time was called incorrectly.` notice #2816
+* Fix - ACDC card fields not loading and payment not successful when Classic Checkout Smart Button Location disabled #2852
+* Fix - ACDC gateway does not appear for guests when is Fastlane enabled and a subscription product is in the cart #2745
+* Fix - "Voide authorization" button does not appear for Apple Pay/Google Pay orders when payment buttons are separated #2752
+* Fix - Additional payment tokens saved with new customer_id #2820
+* Fix - Vaulted payment method may not be displayed in PayPal button for return buyer #2809
+* Fix - Conflict with EasyShip plugin due to shipping methods loading too early #2845
+* Fix - Restore accidentally removed ACDC currencies #2838
+* Enhancement - Native gateway icon for PayPal & Pay upon Invoice gateways #2712
+* Enhancement - Allow disabling specific card types for Fastlane #2704
+* Enhancement - Fastlane Insights SDK implementation for block Checkout #2737
+* Enhancement - Hide split local APMs in Payments settings tab when PayPal is not enabled #2703
+* Enhancement - Do not load split local APMs on Checkout when PayPal is not enabled #2792
+* Enhancement - Add support for Button Options in the Block Checkout for Apple Pay & Google Pay buttons #2797 #2772
+* Enhancement - Disable “Add payment method” button while saving ACDC payment #2794
+* Enhancement - Sanitize soft_descriptor field #2846 #2854
= 2.9.4 - 2024-11-11 =
* Fix - Apple Pay button preview missing in Standard payment and Advanced Processing tabs #2755
diff --git a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php
index 2cc7c5480..baecabf73 100644
--- a/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php
+++ b/modules/ppcp-api-client/src/Entity/FraudProcessorResponse.php
@@ -17,44 +17,44 @@ class FraudProcessorResponse {
/**
* The AVS response code.
*
- * @var string|null
+ * @var string
*/
- protected $avs_code;
+ protected string $avs_code;
/**
* The CVV response code.
*
- * @var string|null
+ * @var string
*/
- protected $cvv_code;
+ protected string $cvv2_code;
/**
* FraudProcessorResponse constructor.
*
* @param string|null $avs_code The AVS response code.
- * @param string|null $cvv_code The CVV response code.
+ * @param string|null $cvv2_code The CVV response code.
*/
- public function __construct( ?string $avs_code, ?string $cvv_code ) {
- $this->avs_code = $avs_code;
- $this->cvv_code = $cvv_code;
+ public function __construct( ?string $avs_code, ?string $cvv2_code ) {
+ $this->avs_code = (string) $avs_code;
+ $this->cvv2_code = (string) $cvv2_code;
}
/**
* Returns the AVS response code.
*
- * @return string|null
+ * @return string
*/
- public function avs_code(): ?string {
+ public function avs_code(): string {
return $this->avs_code;
}
/**
* Returns the CVV response code.
*
- * @return string|null
+ * @return string
*/
- public function cvv_code(): ?string {
- return $this->cvv_code;
+ public function cvv_code(): string {
+ return $this->cvv2_code;
}
/**
@@ -64,11 +64,99 @@ class FraudProcessorResponse {
*/
public function to_array(): array {
return array(
- 'avs_code' => $this->avs_code() ?: '',
+ 'avs_code' => $this->avs_code(),
+ 'cvv2_code' => $this->cvv_code(),
+ // For backwards compatibility.
'address_match' => $this->avs_code() === 'M' ? 'Y' : 'N',
'postal_match' => $this->avs_code() === 'M' ? 'Y' : 'N',
'cvv_match' => $this->cvv_code() === 'M' ? 'Y' : 'N',
);
}
+ /**
+ * Retrieves the AVS (Address Verification System) code messages based on the AVS response code.
+ *
+ * Provides human-readable descriptions for various AVS response codes
+ * and returns the corresponding message for the given code.
+ *
+ * @return string The AVS response code message. If the code is not found, an error message is returned.
+ */
+ public function get_avs_code_message(): string {
+ if ( ! $this->avs_code() ) {
+ return '';
+ }
+ $messages = array(
+ /* Visa, Mastercard, Discover, American Express */
+ 'A' => 'A: Address - Address only (no ZIP code)',
+ 'B' => 'B: International "A" - Address only (no ZIP code)',
+ 'C' => 'C: International "N" - None. The transaction is declined.',
+ 'D' => 'D: International "X" - Address and Postal Code',
+ 'E' => 'E: Not allowed for MOTO (Internet/Phone) transactions - Not applicable. The transaction is declined.',
+ 'F' => 'F: UK-specific "X" - Address and Postal Code',
+ 'G' => 'G: Global Unavailable - Not applicable',
+ 'I' => 'I: International Unavailable - Not applicable',
+ 'M' => 'M: Address - Address and Postal Code',
+ 'N' => 'N: No - None. The transaction is declined.',
+ 'P' => 'P: Postal (International "Z") - Postal Code only (no Address)',
+ 'R' => 'R: Retry - Not applicable',
+ 'S' => 'S: Service not Supported - Not applicable',
+ 'U' => 'U: Unavailable / Address not checked, or acquirer had no response. Service not available.',
+ 'W' => 'W: Whole ZIP - Nine-digit ZIP code (no Address)',
+ 'X' => 'X: Exact match - Address and nine-digit ZIP code)',
+ 'Y' => 'Y: Yes - Address and five-digit ZIP',
+ 'Z' => 'Z: ZIP - Five-digit ZIP code (no Address)',
+ /* Maestro */
+ '0' => '0: All the address information matched.',
+ '1' => '1: None of the address information matched. The transaction is declined.',
+ '2' => '2: Part of the address information matched.',
+ '3' => '3: The merchant did not provide AVS information. Not processed.',
+ '4' => '4: Address not checked, or acquirer had no response. Service not available.',
+ );
+
+ /**
+ * Psalm suppress
+ *
+ * @psalm-suppress PossiblyNullArrayOffset
+ * @psalm-suppress PossiblyNullArgument
+ */
+ return $messages[ $this->avs_code() ] ?? sprintf( '%s: Error', $this->avs_code() );
+ }
+
+ /**
+ * Retrieves the CVV2 code message based on the CVV code provided.
+ *
+ * This method maps CVV response codes to their corresponding descriptive messages.
+ *
+ * @return string The descriptive message corresponding to the CVV2 code, or a formatted error message if the code is unrecognized.
+ */
+ public function get_cvv2_code_message(): string {
+ if ( ! $this->cvv_code() ) {
+ return '';
+ }
+ $messages = array(
+ /* Visa, Mastercard, Discover, American Express */
+ 'E' => 'E: Error - Unrecognized or Unknown response',
+ 'I' => 'I: Invalid or Null',
+ 'M' => 'M: Match or CSC',
+ 'N' => 'N: No match',
+ 'P' => 'P: Not processed',
+ 'S' => 'S: Service not supported',
+ 'U' => 'U: Unknown - Issuer is not certified',
+ 'X' => 'X: No response / Service not available',
+ /* Maestro */
+ '0' => '0: Matched CVV2',
+ '1' => '1: No match',
+ '2' => '2: The merchant has not implemented CVV2 code handling',
+ '3' => '3: Merchant has indicated that CVV2 is not present on card',
+ '4' => '4: Service not available',
+ );
+
+ /**
+ * Psalm suppress
+ *
+ * @psalm-suppress PossiblyNullArrayOffset
+ * @psalm-suppress PossiblyNullArgument
+ */
+ return $messages[ $this->cvv_code() ] ?? sprintf( '%s: Error', $this->cvv_code() );
+ }
}
diff --git a/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php
index 7cb0c048f..1d222f605 100644
--- a/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php
+++ b/modules/ppcp-api-client/src/Helper/PurchaseUnitSanitizer.php
@@ -178,6 +178,11 @@ class PurchaseUnitSanitizer {
// Get a more intelligent adjustment mechanism.
$increment = ( new MoneyFormatter() )->minimum_increment( $item['unit_amount']['currency_code'] );
+ // not floor items that will be negative then.
+ if ( (float) $item['unit_amount']['value'] < $increment ) {
+ continue;
+ }
+
$this->purchase_unit['items'][ $index ]['unit_amount'] = ( new Money(
( (float) $item['unit_amount']['value'] ) - $increment,
$item['unit_amount']['currency_code']
diff --git a/modules/ppcp-axo-block/src/AxoBlockModule.php b/modules/ppcp-axo-block/src/AxoBlockModule.php
index c8216bf62..af94976a3 100644
--- a/modules/ppcp-axo-block/src/AxoBlockModule.php
+++ b/modules/ppcp-axo-block/src/AxoBlockModule.php
@@ -92,7 +92,10 @@ class AxoBlockModule implements ServiceModule, ExtendingModule, ExecutableModule
*/
add_filter(
'woocommerce_paypal_payments_sdk_components_hook',
- function( $components ) {
+ function( $components ) use ( $c ) {
+ if ( ! $c->has( 'axo.available' ) || ! $c->get( 'axo.available' ) ) {
+ return $components;
+ }
$components[] = 'fastlane';
return $components;
}
diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php
index 121b17805..ca427700d 100644
--- a/modules/ppcp-axo/services.php
+++ b/modules/ppcp-axo/services.php
@@ -44,7 +44,9 @@ return array(
// If AXO is configured and onboarded.
'axo.available' => static function ( ContainerInterface $container ): bool {
- return true;
+ $settings = $container->get( 'wcgateway.settings' );
+ assert( $settings instanceof Settings );
+ return $settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' );
},
'axo.url' => static function ( ContainerInterface $container ): string {
diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php
index 3ac0ff157..21ee84a35 100644
--- a/modules/ppcp-axo/src/AxoModule.php
+++ b/modules/ppcp-axo/src/AxoModule.php
@@ -246,7 +246,13 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
*/
add_filter(
'woocommerce_paypal_payments_sdk_components_hook',
- function( $components ) {
+ function( $components ) use ( $c ) {
+ $dcc_configuration = $c->get( 'wcgateway.configuration.dcc' );
+ assert( $dcc_configuration instanceof DCCGatewayConfiguration );
+
+ if ( ! $dcc_configuration->use_fastlane() ) {
+ return $components;
+ }
$components[] = 'fastlane';
return $components;
}
@@ -255,14 +261,18 @@ class AxoModule implements ServiceModule, ExtendingModule, ExecutableModule {
add_action(
'wp_head',
function () use ( $c ) {
- // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
- echo '';
-
// Add meta tag to allow feature-detection of the site's AXO payment state.
$dcc_configuration = $c->get( 'wcgateway.configuration.dcc' );
assert( $dcc_configuration instanceof DCCGatewayConfiguration );
- $this->add_feature_detection_tag( $dcc_configuration->use_fastlane() );
+ if ( $dcc_configuration->use_fastlane() ) {
+ // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
+ echo '';
+
+ $this->add_feature_detection_tag( true );
+ } else {
+ $this->add_feature_detection_tag( false );
+ }
}
);
diff --git a/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js b/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js
new file mode 100644
index 000000000..37d99539c
--- /dev/null
+++ b/modules/ppcp-blocks/resources/js/Components/block-editor-paypal.js
@@ -0,0 +1,52 @@
+import { useMemo } from '@wordpress/element';
+import { normalizeStyleForFundingSource } from '../../../../ppcp-button/resources/js/modules/Helper/Style';
+import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js';
+
+export const BlockEditorPayPalComponent = ( {
+ config,
+ fundingSource,
+ buttonAttributes,
+} ) => {
+ const urlParams = useMemo(
+ () => ( {
+ clientId: 'test',
+ ...config.scriptData.url_params,
+ dataNamespace: 'ppcp-blocks-editor-paypal-buttons',
+ components: 'buttons',
+ } ),
+ []
+ );
+
+ const style = useMemo( () => {
+ const configStyle = normalizeStyleForFundingSource(
+ config.scriptData.button.style,
+ fundingSource
+ );
+
+ if ( buttonAttributes ) {
+ return {
+ ...configStyle,
+ height: buttonAttributes.height
+ ? Number( buttonAttributes.height )
+ : configStyle.height,
+ borderRadius: buttonAttributes.borderRadius
+ ? Number( buttonAttributes.borderRadius )
+ : configStyle.borderRadius,
+ };
+ }
+
+ return configStyle;
+ }, [ fundingSource, buttonAttributes ] );
+
+ return (
+