Sync with trunk

This commit is contained in:
Daniel Dudzic 2024-04-10 11:28:51 +02:00
commit 7af2162f1c
No known key found for this signature in database
GPG key ID: 31B40D33E3465483
28 changed files with 359 additions and 109 deletions

View file

@ -1,5 +1,23 @@
*** Changelog ***
= 2.6.1 - 2024-04-09 =
* Fix - Payment tokens fixes and adjustments #2106
* Fix - Pay upon Invoice: Add input validation to Experience Context fields #2092
* Fix - Disable markup in get_plugin_data() returns to fix an issue with wptexturize() #2094
* Fix - Problem changing the shipping option in block pages #2142
* Fix - Saved payment token deleted after payment with another saved payment token #2146
* Enhancement - Pay later messaging configurator improvements #2107
* Enhancement - Replace the middleware URL from connect.woocommerce.com to api.woocommerce.com/integrations #2130
* Enhancement - Remove all Sofort references as it has been deprecated #2124
* Enhancement - Improve funding source names #2118
* Enhancement - More fraud prevention capabilities by storing additional data in the order #2125
* Enhancement - Update ACDC currency eligibility for AMEX #2129
* Enhancement - Sync shipping options with Venmo when skipping final confirmation on Checkout #2108
* Enhancement - Card Fields: Add a filter for the CVC field and update the placeholder to match the label #2089
* Enhancement - Product Title: Sanitize before sending to PayPal #2090
* Enhancement - Add filter for disabling permit_multiple_payment_tokens vault attribute #2136
* Enhancement - Filter to hide PayPal email address not working on order detail #2137
= 2.6.0 - 2024-03-20 =
* Fix - invoice_id not included in API call when creating payment with saved card #2086
* Fix - Typo in SCA indicators for ACDC Vault transactions #2083

View file

@ -1408,32 +1408,32 @@ return array(
'BE' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD', 'CAD' ),
'amex' => array(),
),
'BG' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'CY' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'CZ' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'CZK' ),
'amex' => array(),
),
'DE' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'DK' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'DKK' ),
'amex' => array(),
),
'EE' => array(
'mastercard' => array(),
@ -1443,32 +1443,32 @@ return array(
'ES' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'FI' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'FR' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'GB' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'GBP', 'USD' ),
'amex' => array(),
),
'GR' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'HU' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'HUF' ),
'amex' => array(),
),
'IE' => array(
'mastercard' => array(),
@ -1478,7 +1478,7 @@ return array(
'IT' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'US' => array(
'mastercard' => array(),
@ -1489,7 +1489,7 @@ return array(
'CA' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'CAD' ),
'amex' => array( 'CAD', 'USD' ),
'jcb' => array( 'CAD' ),
),
'LI' => array(
@ -1500,22 +1500,22 @@ return array(
'LT' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'LU' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'LV' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD' ),
'amex' => array(),
),
'MT' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'MX' => array(
'mastercard' => array(),
@ -1525,7 +1525,7 @@ return array(
'NL' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD' ),
'amex' => array(),
),
'NO' => array(
'mastercard' => array(),
@ -1535,32 +1535,32 @@ return array(
'PL' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD', 'GBP', 'PLN' ),
'amex' => array(),
),
'PT' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD', 'CAD', 'GBP' ),
'amex' => array(),
),
'RO' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'USD' ),
'amex' => array(),
),
'SE' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'SEK' ),
'amex' => array(),
),
'SI' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR' ),
'amex' => array(),
),
'SK' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'EUR', 'GBP' ),
'amex' => array(),
),
'JP' => array(
'mastercard' => array(),

View file

@ -300,6 +300,7 @@ const PayPalComponent = ({
try {
const shippingOptionId = data.selectedShippingOption?.id;
if (shippingOptionId) {
await wp.data.dispatch('wc/store/cart').selectShippingRate(shippingOptionId);
await shippingData.setSelectedRates(shippingOptionId);
}
@ -359,6 +360,7 @@ const PayPalComponent = ({
try {
const shippingOptionId = data.selectedShippingOption?.id;
if (shippingOptionId) {
await wp.data.dispatch('wc/store/cart').selectShippingRate(shippingOptionId);
await shippingData.setSelectedRates(shippingOptionId);
}
} catch (e) {

View file

@ -1009,11 +1009,21 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
if ( $this->settings->has( '3d_secure_contingency' ) ) {
$value = $this->settings->get( '3d_secure_contingency' );
if ( $value ) {
return $value;
return $this->return_3ds_contingency( $value );
}
}
return 'SCA_WHEN_REQUIRED';
return $this->return_3ds_contingency( 'SCA_WHEN_REQUIRED' );
}
/**
* Processes and returns the 3D Secure contingency.
*
* @param string $contingency The ThreeD secure contingency.
* @return string
*/
private function return_3ds_contingency( string $contingency ): string {
return apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $contingency );
}
/**
@ -1337,7 +1347,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
$disable_funding,
array_diff(
array_keys( $this->all_funding_sources ),
array( 'venmo', 'paylater' )
array( 'venmo', 'paylater', 'paypal' )
)
);
}
@ -1358,6 +1368,20 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
$disable_funding[] = 'paylater';
}
$disable_funding = array_filter(
$disable_funding,
/**
* Make sure paypal is not sent in disable funding.
*
* @param string $funding_source The funding_source.
*
* @psalm-suppress MissingClosureParamType
*/
function( $funding_source ) {
return $funding_source !== 'paypal';
}
);
if ( count( $disable_funding ) > 0 ) {
$params['disable-funding'] = implode( ',', array_unique( $disable_funding ) );
}

View file

@ -329,6 +329,21 @@ class CreateOrderEndpoint implements EndpointInterface {
if ( 'pay-now' === $data['context'] && is_a( $wc_order, \WC_Order::class ) ) {
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
$payment_source = $order->payment_source();
$payment_source_name = $payment_source ? $payment_source->name() : null;
$payer = $order->payer();
if (
$payer
&& $payment_source_name
&& in_array( $payment_source_name, PayPalGateway::PAYMENT_SOURCES_WITH_PAYER_EMAIL, true )
) {
$payer_email = $payer->email_address();
if ( $payer_email ) {
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email );
}
}
$wc_order->save_meta_data();
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );

View file

@ -159,6 +159,22 @@ class EarlyOrderHandler {
$wc_order = wc_get_order( $order_id );
$wc_order->update_meta_data( PayPalGateway::ORDER_ID_META_KEY, $order->id() );
$wc_order->update_meta_data( PayPalGateway::INTENT_META_KEY, $order->intent() );
$payment_source = $order->payment_source();
$payment_source_name = $payment_source ? $payment_source->name() : null;
$payer = $order->payer();
if (
$payer
&& $payment_source_name
&& in_array( $payment_source_name, PayPalGateway::PAYMENT_SOURCES_WITH_PAYER_EMAIL, true )
&& $wc_order instanceof \WC_Order
) {
$payer_email = $payer->email_address();
if ( $payer_email ) {
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email );
}
}
$wc_order->save_meta_data();
/**

View file

@ -57,21 +57,24 @@ class ThreeDSecure {
*
* @link https://developer.paypal.com/docs/business/checkout/add-capabilities/3d-secure/#authenticationresult
*
* @param Order $order The order for which the decission is needed.
* @param Order $order The order for which the decision is needed.
*
* @return int
*/
public function proceed_with_order( Order $order ): int {
do_action( 'woocommerce_paypal_payments_three_d_secure_before_check', $order );
$payment_source = $order->payment_source();
if ( ! $payment_source ) {
return self::NO_DECISION;
return $this->return_decision( self::NO_DECISION, $order );
}
if ( ! ( $payment_source->properties()->brand ?? '' ) ) {
return self::NO_DECISION;
return $this->return_decision( self::NO_DECISION, $order );
}
if ( ! ( $payment_source->properties()->authentication_result ?? '' ) ) {
return self::NO_DECISION;
return $this->return_decision( self::NO_DECISION, $order );
}
$authentication_result = $payment_source->properties()->authentication_result ?? null;
@ -81,18 +84,31 @@ class ThreeDSecure {
$this->logger->info( '3DS Authentication Result: ' . wc_print_r( $result->to_array(), true ) );
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) {
return self::PROCCEED;
return $this->return_decision( self::PROCCEED, $order );
}
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_UNKNOWN ) {
return self::RETRY;
return $this->return_decision( self::RETRY, $order );
}
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_NO ) {
return $this->no_liability_shift( $result );
return $this->return_decision( $this->no_liability_shift( $result ), $order );
}
}
return self::NO_DECISION;
return $this->return_decision( self::NO_DECISION, $order );
}
/**
* Processes and returns a ThreeD secure decision.
*
* @param int $decision The ThreeD secure decision.
* @param Order $order The PayPal Order object.
* @return int
*/
public function return_decision( int $decision, Order $order ) {
$decision = apply_filters( 'woocommerce_paypal_payments_three_d_secure_decision', $decision, $order );
do_action( 'woocommerce_paypal_payments_three_d_secure_after_check', $order, $decision );
return $decision;
}
/**

View file

@ -115,17 +115,19 @@ class CardFieldsModule implements ModuleInterface {
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$three_d_secure_contingency =
$settings->has( '3d_secure_contingency' )
? apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $settings->get( '3d_secure_contingency' ) )
: '';
if (
$settings->has( '3d_secure_contingency' )
&& (
$settings->get( '3d_secure_contingency' ) === 'SCA_ALWAYS'
|| $settings->get( '3d_secure_contingency' ) === 'SCA_WHEN_REQUIRED'
)
$three_d_secure_contingency === 'SCA_ALWAYS'
|| $three_d_secure_contingency === 'SCA_WHEN_REQUIRED'
) {
$data['payment_source']['card'] = array(
'attributes' => array(
'verification' => array(
'method' => $settings->get( '3d_secure_contingency' ),
'method' => $three_d_secure_contingency,
),
),
);

View file

@ -199,19 +199,18 @@ export default function Edit({ attributes, clientId, setAttributes }) {
/>
)}
<SelectControl
label={__('Placement page', 'woocommerce-paypal-payments')}
help={__('Used for the analytics dashboard in the merchant account.', 'woocommerce-paypal-payments')}
options={[
{ label: __('Detect automatically', 'woocommerce-paypal-payments'), value: 'auto' },
{ label: __('Cart', 'woocommerce-paypal-payments'), value: 'cart' },
{ label: __('Payment', 'woocommerce-paypal-payments'), value: 'payment' },
{ label: __('Product', 'woocommerce-paypal-payments'), value: 'product' },
{ label: __('Product list', 'woocommerce-paypal-payments'), value: 'product-list' },
{ label: __('Home', 'woocommerce-paypal-payments'), value: 'home' },
{ label: __('Category', 'woocommerce-paypal-payments'), value: 'category' },
]}
value={placement}
onChange={(value) => setAttributes({ placement: value })}
label={ __( 'Placement page', 'woocommerce-paypal-payments' ) }
help={ __( 'Used for the analytics dashboard in the merchant account.', 'woocommerce-paypal-payments' ) }
options={ [
{ label: __( 'Detect automatically', 'woocommerce-paypal-payments' ), value: 'auto' },
{ label: __( 'Product Page', 'woocommerce-paypal-payments' ), value: 'product' },
{ label: __( 'Cart', 'woocommerce-paypal-payments' ), value: 'cart' },
{ label: __( 'Checkout', 'woocommerce-paypal-payments' ), value: 'checkout' },
{ label: __( 'Home', 'woocommerce-paypal-payments' ), value: 'home' },
{ label: __( 'Shop', 'woocommerce-paypal-payments' ), value: 'shop' },
] }
value={ placement }
onChange={ ( value ) => setAttributes( { placement: value } ) }
/>
</PanelBody>
</InspectorControls>

View file

@ -40,7 +40,7 @@ class PayLaterBlockModule implements ModuleInterface {
* @return bool true if the block is enabled, otherwise false.
*/
public static function is_block_enabled( SettingsStatus $settings_status ): bool {
return self::is_module_loading_required() && $settings_status->is_pay_later_messaging_enabled_for_location( 'woocommerceBlock' );
return self::is_module_loading_required() && $settings_status->is_pay_later_messaging_enabled_for_location( 'custom_placement' );
}
/**

View file

@ -29,21 +29,17 @@ document.addEventListener( 'DOMContentLoaded', () => {
setTimeout(() => {
saveChangesButton.click(); // Trigger click event on saveChangesButton
isSaving = false; // Reset flag when saving is complete
}, 500); // Adjust the delay as needed
}, 1000); // Adjust the delay as needed
}
});
merchantConfigurators.Messaging({
config: PcpPayLaterConfigurator.config,
merchantClientId: PcpPayLaterConfigurator.merchantClientId,
partnerClientId: PcpPayLaterConfigurator.partnerClientId,
partnerName: 'WooCommerce',
bnCode: 'Woo_PPCP',
placements: ['cart', 'checkout', 'product', 'shop', 'home'],
custom_placement:[{
message_reference: 'woocommerceBlock',
}],
placements: ['cart', 'checkout', 'product', 'shop', 'home', 'custom_placement'],
styleOverrides: {
button: publishButtonClassName,
header: PcpPayLaterConfigurator.headerClassName,

View file

@ -99,10 +99,13 @@ class SaveConfig {
$this->settings->set( 'pay_later_messaging_enabled', true );
$enabled_locations = array();
foreach ( $config as $placement => $data ) {
$this->save_config_for_location( $data, $placement );
if ( $placement === 'custom_placement' ) {
$data = $data[0] ?? array();
}
if ( $data['status'] === 'enabled' ) {
$enabled_locations[] = $placement;
}
@ -129,6 +132,7 @@ class SaveConfig {
$this->set_value_if_present( $config, 'logo-type', "pay_later_{$location}_message_logo" );
$this->set_value_if_present( $config, 'logo-color', "pay_later_{$location}_message_color" );
$this->set_value_if_present( $config, 'text-size', "pay_later_{$location}_message_text_size" );
$this->set_value_if_present( $config, 'text-color', "pay_later_{$location}_message_color" );
}
/**

View file

@ -27,7 +27,7 @@ class ConfigFactory {
'product' => $this->for_location( $settings, 'product' ),
'shop' => $this->for_location( $settings, 'shop' ),
'home' => $this->for_location( $settings, 'home' ),
'woocommerceBlock' => $this->for_location( $settings, 'woocommerceBlock' ),
'custom_placement' => array( $this->for_location( $settings, 'woocommerceBlock' ) ),
);
}
@ -40,29 +40,87 @@ class ConfigFactory {
private function for_location( Settings $settings, string $location ): array {
$selected_locations = $settings->has( 'pay_later_messaging_locations' ) ? $settings->get( 'pay_later_messaging_locations' ) : array();
if ( in_array( $location, array( 'shop', 'home' ), true ) ) {
$config = array(
'layout' => $this->get_or_default( $settings, "pay_later_{$location}_message_layout", 'flex' ),
'color' => $this->get_or_default( $settings, "pay_later_{$location}_message_flex_color", 'black' ),
'ratio' => $this->get_or_default( $settings, "pay_later_{$location}_message_flex_ratio", '8x1' ),
);
} elseif ( $location !== 'woocommerceBlock' ) {
$config = array(
'layout' => $this->get_or_default( $settings, "pay_later_{$location}_message_layout", 'text' ),
'logo-position' => $this->get_or_default( $settings, "pay_later_{$location}_message_position", 'left' ),
'logo-type' => $this->get_or_default( $settings, "pay_later_{$location}_message_logo", 'inline' ),
'text-color' => $this->get_or_default( $settings, "pay_later_{$location}_message_color", 'black' ),
'text-size' => $this->get_or_default( $settings, "pay_later_{$location}_message_text_size", '12' ),
);
switch ( $location ) {
case 'shop':
case 'home':
$config = $this->for_shop_or_home( $settings, $location, $selected_locations );
break;
case 'woocommerceBlock':
$config = $this->for_woocommerce_block( $selected_locations );
break;
default:
$config = $this->for_default_location( $settings, $location, $selected_locations );
break;
}
return array_merge(
array(
'status' => in_array( $location, $selected_locations, true ) ? 'enabled' : 'disabled',
'placement' => $location,
),
$config ?? array()
return $config;
}
/**
* Returns the configurator config for shop, home locations.
*
* @param Settings $settings The settings.
* @param string $location The location.
* @param string[] $selected_locations The list of selected locations.
* @return array{
* layout: string,
* color: string,
* ratio: string,
* status: "disabled"|"enabled",
* placement: string
* } The configurator config map.
*/
private function for_shop_or_home( Settings $settings, string $location, array $selected_locations ): array {
return array(
'layout' => $this->get_or_default( $settings, "pay_later_{$location}_message_layout", 'flex' ),
'color' => $this->get_or_default( $settings, "pay_later_{$location}_message_flex_color", 'black' ),
'ratio' => $this->get_or_default( $settings, "pay_later_{$location}_message_flex_ratio", '8x1' ),
'status' => in_array( $location, $selected_locations, true ) ? 'enabled' : 'disabled',
'placement' => $location,
);
}
/**
* Returns the configurator config for woocommerceBlock location.
*
* @param array $selected_locations The list of selected locations.
* @return array{
* status: "disabled"|"enabled",
* message_reference: string
* } The configurator config map.
*/
private function for_woocommerce_block( array $selected_locations ): array {
return array(
'status' => in_array( 'custom_placement', $selected_locations, true ) ? 'enabled' : 'disabled',
'message_reference' => 'woocommerceBlock',
);
}
/**
* Returns the configurator config for default locations.
*
* @param Settings $settings The settings.
* @param string $location The location.
* @param string[] $selected_locations The list of selected locations.
* @return array{
* layout: string,
* logo-position: string,
* logo-type: string,
* text-color: string,
* text-size: string,
* status: "disabled"|"enabled",
* placement: string
* } The configurator config map.
*/
private function for_default_location( Settings $settings, string $location, array $selected_locations ): array {
return array(
'layout' => $this->get_or_default( $settings, "pay_later_{$location}_message_layout", 'text' ),
'logo-position' => $this->get_or_default( $settings, "pay_later_{$location}_message_position", 'left' ),
'logo-type' => $this->get_or_default( $settings, "pay_later_{$location}_message_logo", 'inline' ),
'text-color' => $this->get_or_default( $settings, "pay_later_{$location}_message_color", 'black' ),
'text-size' => $this->get_or_default( $settings, "pay_later_{$location}_message_text_size", '12' ),
'status' => in_array( $location, $selected_locations, true ) ? 'enabled' : 'disabled',
'placement' => $location,
);
}
@ -73,9 +131,9 @@ class ConfigFactory {
* @param string $key The key.
* @param mixed $default The default value.
* @param array|null $allowed_values The list of allowed values, or null if all values are allowed.
* @return mixed
* @return string
*/
private function get_or_default( Settings $settings, string $key, $default, ?array $allowed_values = null ) {
private function get_or_default( Settings $settings, string $key, $default, ?array $allowed_values = null ): string {
if ( $settings->has( $key ) ) {
$value = $settings->get( $key );
if ( ! $allowed_values || in_array( $value, $allowed_values, true ) ) {

View file

@ -141,7 +141,7 @@ class SavePaymentMethodsModule implements ModuleInterface {
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
'permit_multiple_payment_tokens' => apply_filters( 'woocommerce_paypal_payments_permit_multiple_payment_tokens', false ),
),
),
),
@ -167,7 +167,7 @@ class SavePaymentMethodsModule implements ModuleInterface {
'vault' => array(
'store_in_vault' => 'ON_SUCCESS',
'usage_type' => 'MERCHANT',
'permit_multiple_payment_tokens' => true,
'permit_multiple_payment_tokens' => apply_filters( 'woocommerce_paypal_payments_permit_multiple_payment_tokens', false ),
),
),
),
@ -281,7 +281,11 @@ class SavePaymentMethodsModule implements ModuleInterface {
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$verification_method = $settings->has( '3d_secure_contingency' ) ? $settings->get( '3d_secure_contingency' ) : '';
$verification_method =
$settings->has( '3d_secure_contingency' )
? apply_filters( 'woocommerce_paypal_payments_three_d_secure_contingency', $settings->get( '3d_secure_contingency' ) )
: '';
$change_payment_method = wc_clean( wp_unslash( $_GET['change_payment_method'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification

View file

@ -962,10 +962,10 @@ return array(
'ideal' => _x( 'iDEAL', 'Name of payment method', 'woocommerce-paypal-payments' ),
'mybank' => _x( 'MyBank', 'Name of payment method', 'woocommerce-paypal-payments' ),
'p24' => _x( 'Przelewy24', 'Name of payment method', 'woocommerce-paypal-payments' ),
'sofort' => _x( 'Sofort', 'Name of payment method', 'woocommerce-paypal-payments' ),
'venmo' => _x( 'Venmo', 'Name of payment method', 'woocommerce-paypal-payments' ),
'trustly' => _x( 'Trustly', 'Name of payment method', 'woocommerce-paypal-payments' ),
'paylater' => _x( 'Pay Later', 'Name of payment method', 'woocommerce-paypal-payments' ),
'paylater' => _x( 'PayPal Pay Later', 'Name of payment method', 'woocommerce-paypal-payments' ),
'paypal' => _x( 'PayPal', 'Name of payment method', 'woocommerce-paypal-payments' ),
);
},
@ -988,6 +988,7 @@ return array(
array_flip(
array(
'paylater',
'paypal',
)
)
);

View file

@ -34,7 +34,7 @@ class FundingSourceRenderer {
*
* @var string[]
*/
protected $own_funding_sources = array( 'venmo', 'paylater' );
protected $own_funding_sources = array( 'venmo', 'paylater', 'paypal' );
/**
* FundingSourceRenderer constructor.
@ -63,7 +63,7 @@ class FundingSourceRenderer {
return $this->funding_sources[ $id ];
}
return sprintf(
/* translators: %s - Sofort, BLIK, iDeal, Mercado Pago, etc. */
/* translators: %s - BLIK, iDeal, Mercado Pago, etc. */
__( '%s (via PayPal)', 'woocommerce-paypal-payments' ),
$this->funding_sources[ $id ]
);
@ -84,7 +84,7 @@ class FundingSourceRenderer {
if ( array_key_exists( $id, $this->funding_sources ) ) {
return sprintf(
/* translators: %s - Sofort, BLIK, iDeal, Mercado Pago, etc. */
/* translators: %s - BLIK, iDeal, Mercado Pago, etc. */
__( 'Pay via %s.', 'woocommerce-paypal-payments' ),
$this->funding_sources[ $id ]
);

View file

@ -48,12 +48,18 @@ class PayPalGateway extends \WC_Payment_Gateway {
const ORDER_ID_META_KEY = '_ppcp_paypal_order_id';
const ORDER_PAYMENT_MODE_META_KEY = '_ppcp_paypal_payment_mode';
const ORDER_PAYMENT_SOURCE_META_KEY = '_ppcp_paypal_payment_source';
const ORDER_PAYER_EMAIL_META_KEY = '_ppcp_paypal_payer_email';
const FEES_META_KEY = '_ppcp_paypal_fees';
const REFUND_FEES_META_KEY = '_ppcp_paypal_refund_fees';
const REFUNDS_META_KEY = '_ppcp_refunds';
const THREE_D_AUTH_RESULT_META_KEY = '_ppcp_paypal_3DS_auth_result';
const FRAUD_RESULT_META_KEY = '_ppcp_paypal_fraud_result';
/**
* List of payment sources wich we are expected to store the payer email in the WC Order metadata.
*/
const PAYMENT_SOURCES_WITH_PAYER_EMAIL = array( 'paypal', 'paylater', 'venmo' );
/**
* The Settings Renderer.
*

View file

@ -76,7 +76,7 @@ trait CreditCardOrderInfoHandlingTrait {
/**
* Fired when the 3DS information is added to WC order.
*/
do_action( 'woocommerce_paypal_payments_thee_d_secure_added', $wc_order, $order );
do_action( 'woocommerce_paypal_payments_three_d_secure_added', $wc_order, $order );
}
}
@ -96,8 +96,9 @@ trait CreditCardOrderInfoHandlingTrait {
return;
}
$fraud_responses = $fraud->to_array();
$card_brand = $payment_source->properties()->brand ?? __( 'N/A', 'woocommerce-paypal-payments' );
$fraud_responses = $fraud->to_array();
$card_brand = $payment_source->properties()->brand ?? __( 'N/A', 'woocommerce-paypal-payments' );
$card_last_digits = $payment_source->properties()->last_digits ?? __( 'N/A', 'woocommerce-paypal-payments' );
$avs_response_order_note_title = __( 'Address Verification Result', 'woocommerce-paypal-payments' );
/* translators: %1$s is AVS order note title, %2$s is AVS order note result markup */
@ -109,6 +110,7 @@ trait CreditCardOrderInfoHandlingTrait {
<li>%3$s</li>
</ul>
<li>%4$s</li>
<li>%5$s</li>
</ul>';
$avs_response_order_note_result = sprintf(
$avs_response_order_note_result_format,
@ -119,7 +121,9 @@ trait CreditCardOrderInfoHandlingTrait {
/* translators: %s is fraud AVS postal match */
sprintf( __( 'Postal Match: %s', 'woocommerce-paypal-payments' ), esc_html( $fraud_responses['postal_match'] ) ),
/* translators: %s is card brand */
sprintf( __( 'Card Brand: %s', 'woocommerce-paypal-payments' ), esc_html( $card_brand ) )
sprintf( __( 'Card Brand: %s', 'woocommerce-paypal-payments' ), esc_html( $card_brand ) ),
/* translators: %s card last digits */
sprintf( __( 'Card Last Digits: %s', 'woocommerce-paypal-payments' ), esc_html( $card_last_digits ) )
);
$avs_response_order_note = sprintf(
$avs_response_order_note_format,

View file

@ -45,6 +45,18 @@ trait OrderMetaTrait {
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY, $payment_source );
}
$payer = $order->payer();
if (
$payer
&& $payment_source
&& in_array( $payment_source, PayPalGateway::PAYMENT_SOURCES_WITH_PAYER_EMAIL, true )
) {
$payer_email = $payer->email_address();
if ( $payer_email ) {
$wc_order->update_meta_data( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY, $payer_email );
}
}
$wc_order->save();
do_action( 'woocommerce_paypal_payments_woocommerce_order_created', $wc_order, $order );

View file

@ -65,7 +65,7 @@ return function ( ContainerInterface $container, array $fields ): array {
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="American Express" src="' . esc_url( $module_url ) . 'assets/images/amex.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#paypal-card-processing-acdc" target="_blank"><img alt="Discover" src="' . esc_url( $module_url ) . 'assets/images/discover.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#alternative-payment-methods" target="_blank"><img alt="iDEAL" src="' . esc_url( $module_url ) . 'assets/images/ideal-dark.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#alternative-payment-methods" target="_blank"><img alt="Sofort" src="' . esc_url( $module_url ) . 'assets/images/sofort.svg"/></a>
<a href="https://woo.com/document/woocommerce-paypal-payments/#alternative-payment-methods" target="_blank"><img alt="BLIK" src="' . esc_url( $module_url ) . 'assets/images/blik.svg"/></a>
</div>
<div class="ppcp-onboarding-header-apm-logos">
<a href="https://woo.com/document/woocommerce-paypal-payments/#apple-pay" target="_blank"><img alt="Apple Pay" src="' . esc_url( $module_url ) . 'assets/images/button-Apple-Pay.png"/></a>

View file

@ -448,6 +448,53 @@ class WCGatewayModule implements ModuleInterface {
delete_transient( 'ppcp_reference_transaction_enabled' );
}
);
/**
* Param types removed to avoid third-party issues.
*
* @psalm-suppress MissingClosureParamType
*/
add_filter(
'woocommerce_admin_billing_fields',
function ( $fields ) {
global $theorder;
if ( ! apply_filters( 'woocommerce_paypal_payments_order_details_show_paypal_email', true ) ) {
return $fields;
}
if ( ! is_array( $fields ) ) {
return $fields;
}
if ( ! $theorder instanceof WC_Order ) {
return $fields;
}
$email = $theorder->get_meta( PayPalGateway::ORDER_PAYER_EMAIL_META_KEY ) ?: '';
if ( ! $email ) {
return $fields;
}
// Is payment source is paypal exclude all non paypal funding sources.
$payment_source = $theorder->get_meta( PayPalGateway::ORDER_PAYMENT_SOURCE_META_KEY ) ?: '';
$is_paypal_funding_source = ( strpos( $theorder->get_payment_method_title(), '(via PayPal)' ) === false );
if ( $payment_source === 'paypal' && ! $is_paypal_funding_source ) {
return $fields;
}
$fields['paypal_email'] = array(
'label' => __( 'PayPal email address', 'woocommerce-paypal-payments' ),
'value' => $email,
'wrapper_class' => 'form-field-wide',
'custom_attributes' => array( 'disabled' => 'disabled' ),
);
return $fields;
}
);
}
/**

View file

@ -1,6 +1,6 @@
{
"name": "woocommerce-paypal-payments",
"version": "2.6.0",
"version": "2.6.1",
"description": "WooCommerce PayPal Payments",
"repository": "https://github.com/woocommerce/woocommerce-paypal-payments",
"license": "GPL-2.0",

View file

@ -2,9 +2,9 @@
Contributors: woocommerce, automattic, inpsyde
Tags: woocommerce, paypal, payments, ecommerce, checkout, cart, pay later, apple pay, subscriptions, debit card, credit card, google pay
Requires at least: 5.3
Tested up to: 6.4
Tested up to: 6.5
Requires PHP: 7.2
Stable tag: 2.6.0
Stable tag: 2.6.1
License: GPLv2
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -179,6 +179,24 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
== Changelog ==
= 2.6.1 - 2024-04-09 =
* Fix - Payment tokens fixes and adjustments #2106
* Fix - Pay upon Invoice: Add input validation to Experience Context fields #2092
* Fix - Disable markup in get_plugin_data() returns to fix an issue with wptexturize() #2094
* Fix - Problem changing the shipping option in block pages #2142
* Fix - Saved payment token deleted after payment with another saved payment token #2146
* Enhancement - Pay later messaging configurator improvements #2107
* Enhancement - Replace the middleware URL from connect.woocommerce.com to api.woocommerce.com/integrations #2130
* Enhancement - Remove all Sofort references as it has been deprecated #2124
* Enhancement - Improve funding source names #2118
* Enhancement - More fraud prevention capabilities by storing additional data in the order #2125
* Enhancement - Update ACDC currency eligibility for AMEX #2129
* Enhancement - Sync shipping options with Venmo when skipping final confirmation on Checkout #2108
* Enhancement - Card Fields: Add a filter for the CVC field and update the placeholder to match the label #2089
* Enhancement - Product Title: Sanitize before sending to PayPal #2090
* Enhancement - Add filter for disabling permit_multiple_payment_tokens vault attribute #2136
* Enhancement - Filter to hide PayPal email address not working on order detail #2137
= 2.6.0 - 2024-03-20 =
* Fix - invoice_id not included in API call when creating payment with saved card #2086
* Fix - Typo in SCA indicators for ACDC Vault transactions #2083

View file

@ -92,6 +92,8 @@ class VaultedCreditCardHandlerTest extends TestCase
$customer = Mockery::mock(WC_Customer::class);
$payer = Mockery::mock(Payer::class);
$payer->shouldReceive('email_address');
$this->payerFactory->shouldReceive('from_wc_order')
->andReturn($payer);
$this->shippingPreferenceFactory->shouldReceive('from_state')
@ -100,6 +102,7 @@ class VaultedCreditCardHandlerTest extends TestCase
$order = Mockery::mock(Order::class);
$order->shouldReceive('id')->andReturn('1');
$order->shouldReceive('intent')->andReturn('CAPTURE');
$order->shouldReceive('payer')->andReturn($payer);
$paymentSource = Mockery::mock(PaymentSource::class);
$paymentSource->shouldReceive('name')->andReturn('card');

View file

@ -89,6 +89,7 @@ private $testee;
$order->shouldReceive('id')->andReturn('1');
$order->shouldReceive('intent');
$order->shouldReceive('payment_source');
$order->shouldReceive('payer');
$this->orderEndpoint
->shouldReceive('create')

View file

@ -93,6 +93,7 @@ class OrderProcessorTest extends TestCase
$currentOrder
->shouldReceive('payment_source')
->andReturn(null);
$currentOrder->shouldReceive('payer');
$wcOrder
->shouldReceive('get_meta')
@ -230,6 +231,7 @@ class OrderProcessorTest extends TestCase
$currentOrder
->shouldReceive('payment_source')
->andReturn(null);
$currentOrder->shouldReceive('payer');
$wcOrder
->shouldReceive('get_meta')
@ -357,6 +359,7 @@ class OrderProcessorTest extends TestCase
$currentOrder
->shouldReceive('purchase_units')
->andReturn([$purchaseUnit]);
$currentOrder->shouldReceive('payer');
$wcOrder
->shouldReceive('get_meta')

View file

@ -13,7 +13,7 @@ PRODUCT_ID=123
SUBSCRIPTION_URL="/product/sub"
APM_ID="sofort"
APM_ID="paypal"
WP_MERCHANT_USER="admin"
WP_MERCHANT_PASSWORD="admin"

View file

@ -3,13 +3,14 @@
* Plugin Name: WooCommerce PayPal Payments
* Plugin URI: https://woocommerce.com/products/woocommerce-paypal-payments/
* Description: PayPal's latest complete payments processing solution. Accept PayPal, Pay Later, credit/debit cards, alternative digital wallets local payment types and bank accounts. Turn on only PayPal options or process a full suite of payment methods. Enable global transaction with extensive currency and country coverage.
* Version: 2.6.0
* Version: 2.6.1
* Author: WooCommerce
* Author URI: https://woocommerce.com/
* License: GPL-2.0
* Requires PHP: 7.2
* Requires Plugins: woocommerce
* WC requires at least: 3.9
* WC tested up to: 8.6
* WC tested up to: 8.7
* Text Domain: woocommerce-paypal-payments
*
* @package WooCommerce\PayPalCommerce
@ -25,14 +26,14 @@ define( 'PAYPAL_API_URL', 'https://api-m.paypal.com' );
define( 'PAYPAL_URL', 'https://www.paypal.com' );
define( 'PAYPAL_SANDBOX_API_URL', 'https://api-m.sandbox.paypal.com' );
define( 'PAYPAL_SANDBOX_URL', 'https://www.sandbox.paypal.com' );
define( 'PAYPAL_INTEGRATION_DATE', '2024-03-12' );
define( 'PAYPAL_INTEGRATION_DATE', '2024-04-03' );
! defined( 'CONNECT_WOO_CLIENT_ID' ) && define( 'CONNECT_WOO_CLIENT_ID', 'AcCAsWta_JTL__OfpjspNyH7c1GGHH332fLwonA5CwX4Y10mhybRZmHLA0GdRbwKwjQIhpDQy0pluX_P' );
! defined( 'CONNECT_WOO_SANDBOX_CLIENT_ID' ) && define( 'CONNECT_WOO_SANDBOX_CLIENT_ID', 'AYmOHbt1VHg-OZ_oihPdzKEVbU3qg0qXonBcAztuzniQRaKE0w1Hr762cSFwd4n8wxOl-TCWohEa0XM_' );
! defined( 'CONNECT_WOO_MERCHANT_ID' ) && define( 'CONNECT_WOO_MERCHANT_ID', 'K8SKZ36LQBWXJ' );
! defined( 'CONNECT_WOO_SANDBOX_MERCHANT_ID' ) && define( 'CONNECT_WOO_SANDBOX_MERCHANT_ID', 'MPMFHQTVMBZ6G' );
! defined( 'CONNECT_WOO_URL' ) && define( 'CONNECT_WOO_URL', 'https://connect.woocommerce.com/ppc' );
! defined( 'CONNECT_WOO_SANDBOX_URL' ) && define( 'CONNECT_WOO_SANDBOX_URL', 'https://connect.woocommerce.com/ppcsandbox' );
! defined( 'CONNECT_WOO_URL' ) && define( 'CONNECT_WOO_URL', 'https://api.woocommerce.com/integrations/ppc' );
! defined( 'CONNECT_WOO_SANDBOX_URL' ) && define( 'CONNECT_WOO_SANDBOX_URL', 'https://api.woocommerce.com/integrations/ppcsandbox' );
( function () {
$autoload_filepath = __DIR__ . '/vendor/autoload.php';