mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 12:25:15 +08:00
Merge branch 'trunk' into modularity-module-migration
# Conflicts: # modules/ppcp-wc-gateway/src/WCGatewayModule.php
This commit is contained in:
commit
161e933d39
96 changed files with 6599 additions and 223 deletions
|
@ -1,5 +1,8 @@
|
|||
@use "../../../ppcp-button/resources/css/mixins/apm-button" as apm-button;
|
||||
|
||||
$border-color: #c3c3c3;
|
||||
$background-ident-color: #fbfbfb;
|
||||
|
||||
.ppcp-field-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
@ -11,9 +14,10 @@
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ppcp-field-indent {
|
||||
th {
|
||||
padding-left: 20px;
|
||||
.ppcp-active-spacer {
|
||||
th, td {
|
||||
padding: 0;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,3 +43,51 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-align-label-center {
|
||||
th {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-valign-label-middle {
|
||||
th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// Box indented fields.
|
||||
@media screen and (min-width: 800px) {
|
||||
.ppcp-settings-field {
|
||||
border-left: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
|
||||
&.active {
|
||||
background-color: $background-ident-color;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
th {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.ppcp-field-indent {
|
||||
background-color: $background-ident-color;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
th, &.ppcp-settings-field-heading td {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
|
||||
& + .ppcp-field-indent {
|
||||
th, td {
|
||||
border-top: 1px solid $background-ident-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import ElementAction from "./action/ElementAction";
|
||||
import VisibilityAction from "./action/VisibilityAction";
|
||||
import AttributeAction from "./action/AttributeAction";
|
||||
|
||||
class ActionFactory {
|
||||
static make(actionConfig) {
|
||||
switch (actionConfig.type) {
|
||||
case 'element':
|
||||
return new ElementAction(actionConfig);
|
||||
case 'visibility':
|
||||
return new VisibilityAction(actionConfig);
|
||||
case 'attribute':
|
||||
return new AttributeAction(actionConfig);
|
||||
}
|
||||
|
||||
throw new Error('[ActionFactory] Unknown action: ' + actionConfig.type);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ElementCondition from "./condition/ElementCondition";
|
||||
import BoolCondition from "./condition/BoolCondition";
|
||||
import JsVariableCondition from "./condition/JsVariableCondition";
|
||||
|
||||
class ConditionFactory {
|
||||
static make(conditionConfig, triggerUpdate) {
|
||||
|
@ -8,6 +9,8 @@ class ConditionFactory {
|
|||
return new ElementCondition(conditionConfig, triggerUpdate);
|
||||
case 'bool':
|
||||
return new BoolCondition(conditionConfig, triggerUpdate);
|
||||
case 'js_variable':
|
||||
return new JsVariableCondition(conditionConfig, triggerUpdate);
|
||||
}
|
||||
|
||||
throw new Error('[ConditionFactory] Unknown condition: ' + conditionConfig.type);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import BaseAction from "./BaseAction";
|
||||
|
||||
class AttributeAction extends BaseAction {
|
||||
|
||||
run(status) {
|
||||
|
||||
if (status) {
|
||||
jQuery(this.config.selector).addClass(this.config.html_class);
|
||||
} else {
|
||||
jQuery(this.config.selector).removeClass(this.config.html_class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AttributeAction;
|
|
@ -1,6 +1,6 @@
|
|||
import BaseAction from "./BaseAction";
|
||||
|
||||
class ElementAction extends BaseAction {
|
||||
class VisibilityAction extends BaseAction {
|
||||
|
||||
run(status) {
|
||||
|
||||
|
@ -32,4 +32,4 @@ class ElementAction extends BaseAction {
|
|||
|
||||
}
|
||||
|
||||
export default ElementAction;
|
||||
export default VisibilityAction;
|
|
@ -0,0 +1,24 @@
|
|||
import BaseCondition from "./BaseCondition";
|
||||
|
||||
class JsVariableCondition extends BaseCondition {
|
||||
|
||||
register() {
|
||||
jQuery(document).on('ppcp-display-change', () => {
|
||||
const status = this.check();
|
||||
if (status !== this.status) {
|
||||
this.status = status;
|
||||
this.triggerUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
this.status = this.check();
|
||||
}
|
||||
|
||||
check() {
|
||||
let value = document[this.config.variable];
|
||||
return this.config.value === value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default JsVariableCondition;
|
|
@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers;
|
||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
|
@ -103,7 +104,10 @@ return array(
|
|||
$api_shop_country,
|
||||
$container->get( 'api.endpoint.order' ),
|
||||
$container->get( 'api.factory.paypal-checkout-url' ),
|
||||
$container->get( 'wcgateway.place-order-button-text' )
|
||||
$container->get( 'wcgateway.place-order-button-text' ),
|
||||
$container->get( 'api.endpoint.payment-tokens' ),
|
||||
$container->get( 'vaulting.vault-v3-enabled' ),
|
||||
$container->get( 'vaulting.wc-payment-tokens' )
|
||||
);
|
||||
},
|
||||
'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway {
|
||||
|
@ -806,25 +810,6 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'vault_enabled_dcc' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'3d_secure_heading' => array(
|
||||
'heading' => __( '3D Secure', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
|
@ -881,6 +866,52 @@ return array(
|
|||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'saved_payments_heading' => array(
|
||||
'heading' => __( 'Saved Payments', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'PayPal can securely store your customers’ payment methods for
|
||||
%1$sfuture payments and subscriptions%2$s, simplifying the checkout
|
||||
process and enabling recurring transactions on your website.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
rel="noreferrer noopener"
|
||||
href="https://woo.com/document/woocommerce-paypal-payments/#vaulting-a-card"
|
||||
>',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
'vault_enabled_dcc' => array(
|
||||
'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'desc_tip' => true,
|
||||
'label' => sprintf(
|
||||
// translators: %1$s and %2$s are the opening and closing of HTML <a> tag.
|
||||
__( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ),
|
||||
'<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#vaulting-saving-a-payment-method" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'dcc',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'paypal_saved_payments' => array(
|
||||
'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ),
|
||||
'description' => sprintf(
|
||||
|
@ -919,6 +950,32 @@ return array(
|
|||
'gateway' => 'paypal',
|
||||
'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ),
|
||||
),
|
||||
'digital_wallet_heading' => array(
|
||||
'heading' => __( 'Digital Wallet Services', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-heading',
|
||||
'description' => wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s and %2$s is a link tag.
|
||||
__(
|
||||
'PayPal supports digital wallet services like Apple Pay or Google Pay
|
||||
to give your buyers more options to pay without a PayPal account.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'<a
|
||||
rel="noreferrer noopener"
|
||||
href="https://woo.com/document/woocommerce-paypal-payments/#vaulting-a-card"
|
||||
>',
|
||||
'</a>'
|
||||
)
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(
|
||||
'dcc',
|
||||
),
|
||||
'gateway' => 'dcc',
|
||||
),
|
||||
);
|
||||
|
||||
if ( ! $subscription_helper->plugin_is_active() ) {
|
||||
|
@ -962,10 +1019,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 +1045,7 @@ return array(
|
|||
array_flip(
|
||||
array(
|
||||
'paylater',
|
||||
'paypal',
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -1505,6 +1563,7 @@ return array(
|
|||
PayUponInvoiceGateway::ID,
|
||||
CardButtonGateway::ID,
|
||||
OXXOGateway::ID,
|
||||
AxoGateway::ID,
|
||||
);
|
||||
},
|
||||
'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository {
|
||||
|
|
|
@ -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 ]
|
||||
);
|
||||
|
|
|
@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait;
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
|||
*/
|
||||
class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
||||
|
||||
use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait;
|
||||
use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait, FreeTrialHandlerTrait;
|
||||
|
||||
const ID = 'ppcp-credit-card-gateway';
|
||||
|
||||
|
@ -454,6 +455,17 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$card_payment_token_id = wc_clean( wp_unslash( $_POST['wc-ppcp-credit-card-gateway-payment-token'] ?? '' ) );
|
||||
|
||||
if ( $this->is_free_trial_order( $wc_order ) && $card_payment_token_id ) {
|
||||
$customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() );
|
||||
foreach ( $customer_tokens as $token ) {
|
||||
if ( $token['payment_source']->name() === 'card' ) {
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $card_payment_token_id ) {
|
||||
$customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() );
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@ use Psr\Log\LoggerInterface;
|
|||
use WC_Order;
|
||||
use WC_Payment_Tokens;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository;
|
||||
|
@ -48,12 +50,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.
|
||||
*
|
||||
|
@ -173,26 +181,50 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
*/
|
||||
private $paypal_checkout_url_factory;
|
||||
|
||||
/**
|
||||
* Payment tokens endpoint.
|
||||
*
|
||||
* @var PaymentTokensEndpoint
|
||||
*/
|
||||
private $payment_tokens_endpoint;
|
||||
|
||||
/**
|
||||
* Whether Vault v3 module is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $vault_v3_enabled;
|
||||
|
||||
/**
|
||||
* WooCommerce payment tokens.
|
||||
*
|
||||
* @var WooCommercePaymentTokens
|
||||
*/
|
||||
private $wc_payment_tokens;
|
||||
|
||||
/**
|
||||
* PayPalGateway constructor.
|
||||
*
|
||||
* @param SettingsRenderer $settings_renderer The Settings Renderer.
|
||||
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
|
||||
* @param OrderProcessor $order_processor The Order Processor.
|
||||
* @param ContainerInterface $config The settings.
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param State $state The state.
|
||||
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param Environment $environment The environment.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $api_shop_country The api shop country.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID.
|
||||
* @param string $place_order_button_text The text for the standard "Place order" button.
|
||||
* @param SettingsRenderer $settings_renderer The Settings Renderer.
|
||||
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
|
||||
* @param OrderProcessor $order_processor The Order Processor.
|
||||
* @param ContainerInterface $config The settings.
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param State $state The state.
|
||||
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
|
||||
* @param SubscriptionHelper $subscription_helper The subscription helper.
|
||||
* @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page.
|
||||
* @param Environment $environment The environment.
|
||||
* @param PaymentTokenRepository $payment_token_repository The payment token repository.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param string $api_shop_country The api shop country.
|
||||
* @param OrderEndpoint $order_endpoint The order endpoint.
|
||||
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID.
|
||||
* @param string $place_order_button_text The text for the standard "Place order" button.
|
||||
* @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint.
|
||||
* @param bool $vault_v3_enabled Whether Vault v3 module is enabled.
|
||||
* @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens.
|
||||
*/
|
||||
public function __construct(
|
||||
SettingsRenderer $settings_renderer,
|
||||
|
@ -211,7 +243,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
string $api_shop_country,
|
||||
OrderEndpoint $order_endpoint,
|
||||
callable $paypal_checkout_url_factory,
|
||||
string $place_order_button_text
|
||||
string $place_order_button_text,
|
||||
PaymentTokensEndpoint $payment_tokens_endpoint,
|
||||
bool $vault_v3_enabled,
|
||||
WooCommercePaymentTokens $wc_payment_tokens
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
$this->settings_renderer = $settings_renderer;
|
||||
|
@ -231,6 +266,10 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$this->api_shop_country = $api_shop_country;
|
||||
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;
|
||||
$this->order_button_text = $place_order_button_text;
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->wc_payment_tokens = $wc_payment_tokens;
|
||||
|
||||
if ( $this->onboarded ) {
|
||||
$this->supports = array( 'refunds', 'tokenization' );
|
||||
|
@ -259,6 +298,8 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions'
|
||||
);
|
||||
} elseif ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' ) {
|
||||
$this->supports[] = 'gateway_scheduled_payments';
|
||||
} elseif ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) {
|
||||
$this->supports[] = 'tokenization';
|
||||
}
|
||||
|
@ -293,8 +334,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
|
||||
$this->order_endpoint = $order_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -494,7 +533,49 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
$wc_order->save();
|
||||
}
|
||||
|
||||
if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) {
|
||||
if (
|
||||
'card' !== $funding_source
|
||||
&& $this->is_free_trial_order( $wc_order )
|
||||
&& ! $this->subscription_helper->paypal_subscription_id()
|
||||
) {
|
||||
$ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial' ) ?? null;
|
||||
if ( $this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial ) {
|
||||
$customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? '';
|
||||
if ( $customer_id ) {
|
||||
update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id );
|
||||
}
|
||||
|
||||
if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal ) ) {
|
||||
$email = '';
|
||||
if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address ) ) {
|
||||
$email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address;
|
||||
}
|
||||
|
||||
$this->wc_payment_tokens->create_payment_token_paypal(
|
||||
$wc_order->get_customer_id(),
|
||||
$ppcp_guest_payment_for_free_trial->id,
|
||||
$email
|
||||
);
|
||||
}
|
||||
|
||||
WC()->session->set( 'ppcp_guest_payment_for_free_trial', null );
|
||||
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
|
||||
$customer_id = get_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', true );
|
||||
if ( $customer_id ) {
|
||||
$customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id );
|
||||
foreach ( $customer_tokens as $token ) {
|
||||
$payment_source_name = $token['payment_source']->name() ?? '';
|
||||
if ( $payment_source_name === 'paypal' || $payment_source_name === 'venmo' ) {
|
||||
$wc_order->payment_complete();
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user_id = (int) $wc_order->get_customer_id();
|
||||
$tokens = $this->payment_token_repository->all_for_user_id( $user_id );
|
||||
if ( ! array_filter(
|
||||
|
@ -507,7 +588,6 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
*/
|
||||
class DisplayRule {
|
||||
|
||||
const CONDITION_TYPE_ELEMENT = 'element';
|
||||
const CONDITION_TYPE_BOOL = 'bool';
|
||||
const CONDITION_TYPE_ELEMENT = 'element';
|
||||
const CONDITION_TYPE_BOOL = 'bool';
|
||||
const CONDITION_TYPE_JS_VARIABLE = 'js_variable';
|
||||
|
||||
const CONDITION_OPERATION_EQUALS = 'equals';
|
||||
const CONDITION_OPERATION_NOT_EQUALS = 'not_equals';
|
||||
|
@ -26,10 +27,12 @@ class DisplayRule {
|
|||
const CONDITION_OPERATION_EMPTY = 'empty';
|
||||
const CONDITION_OPERATION_NOT_EMPTY = 'not_empty';
|
||||
|
||||
const ACTION_TYPE_ELEMENT = 'element';
|
||||
const ACTION_TYPE_VISIBILITY = 'visibility';
|
||||
const ACTION_TYPE_ATTRIBUTE = 'attribute';
|
||||
|
||||
const ACTION_VISIBLE = 'visible';
|
||||
const ACTION_ENABLE = 'enable';
|
||||
const ACTION_CLASS = 'class';
|
||||
|
||||
/**
|
||||
* The element selector.
|
||||
|
@ -132,6 +135,24 @@ class DisplayRule {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition related to js variable check.
|
||||
*
|
||||
* @param string $variable_name The javascript variable name.
|
||||
* @param mixed $value The value to enable / disable the condition.
|
||||
* @return self
|
||||
*/
|
||||
public function condition_js_variable( string $variable_name, $value ): self {
|
||||
$this->add_condition(
|
||||
array(
|
||||
'type' => self::CONDITION_TYPE_JS_VARIABLE,
|
||||
'variable' => $variable_name,
|
||||
'value' => $value,
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to show/hide the element.
|
||||
*
|
||||
|
@ -140,7 +161,7 @@ class DisplayRule {
|
|||
public function action_visible( string $selector ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ELEMENT,
|
||||
'type' => self::ACTION_TYPE_VISIBILITY,
|
||||
'selector' => $selector,
|
||||
'action' => self::ACTION_VISIBLE,
|
||||
)
|
||||
|
@ -148,6 +169,24 @@ class DisplayRule {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to add/remove a html class.
|
||||
*
|
||||
* @param string $selector The condition selector.
|
||||
* @param string $class The class.
|
||||
*/
|
||||
public function action_class( string $selector, string $class ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ATTRIBUTE,
|
||||
'selector' => $selector,
|
||||
'html_class' => $class,
|
||||
'action' => self::ACTION_CLASS,
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition to enable/disable the element.
|
||||
*
|
||||
|
@ -156,7 +195,7 @@ class DisplayRule {
|
|||
public function action_enable( string $selector ): self {
|
||||
$this->add_action(
|
||||
array(
|
||||
'type' => self::ACTION_TYPE_ELEMENT,
|
||||
'type' => self::ACTION_TYPE_VISIBILITY,
|
||||
'selector' => $selector,
|
||||
'action' => self::ACTION_ENABLE,
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
@ -136,7 +140,13 @@ trait CreditCardOrderInfoHandlingTrait {
|
|||
);
|
||||
$wc_order->add_order_note( $cvv_response_order_note );
|
||||
|
||||
$meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand ) );
|
||||
$meta_details = array_merge(
|
||||
$fraud_responses,
|
||||
array(
|
||||
'card_brand' => $card_brand,
|
||||
'card_last_digits' => $card_last_digits,
|
||||
)
|
||||
);
|
||||
$wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $meta_details );
|
||||
$wc_order->save_meta_data();
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -270,6 +270,40 @@ class OrderProcessor {
|
|||
do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a given WooCommerce order and captured/authorizes the connected PayPal orders.
|
||||
*
|
||||
* @param WC_Order $wc_order The WooCommerce order.
|
||||
* @param Order $order The PayPal order.
|
||||
*
|
||||
* @throws Exception If processing fails.
|
||||
*/
|
||||
public function process_captured_and_authorized( WC_Order $wc_order, Order $order ): void {
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
if ( $order->intent() === 'AUTHORIZE' ) {
|
||||
$wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' );
|
||||
|
||||
if ( $this->subscription_helper->has_subscription( $wc_order->get_id() ) ) {
|
||||
$wc_order->update_meta_data( '_ppcp_captured_vault_webhook', 'false' );
|
||||
}
|
||||
}
|
||||
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
|
||||
if ( $transaction_id ) {
|
||||
$this->update_transaction_id( $transaction_id, $wc_order );
|
||||
}
|
||||
|
||||
$this->handle_new_order_status( $order, $wc_order );
|
||||
|
||||
if ( $this->capture_authorized_downloads( $order ) ) {
|
||||
$this->authorized_payments_processor->capture_authorized_payment( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PayPal order for the given WC order.
|
||||
*
|
||||
|
|
|
@ -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>
|
||||
|
@ -436,7 +436,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'desc_tip' => true,
|
||||
'label' => $container->get( 'wcgateway.settings.fraudnet-label' ),
|
||||
'description' => __( 'FraudNet is a JavaScript library developed by PayPal and embedded into a merchant’s web page to collect browser-based data to help reduce fraud.', 'woocommerce-paypal-payments' ),
|
||||
'default' => false,
|
||||
'default' => true,
|
||||
'screens' => array(
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
|
@ -522,8 +522,8 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'options' => array(
|
||||
PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ),
|
||||
PurchaseUnitSanitizer::MODE_EXTRA_LINE => __( 'Add another line item', 'woocommerce-paypal-payments' ),
|
||||
PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'screens' => array(
|
||||
State::STATE_START,
|
||||
|
@ -538,6 +538,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
->rule()
|
||||
->condition_element( 'subtotal_mismatch_behavior', PurchaseUnitSanitizer::MODE_EXTRA_LINE )
|
||||
->action_visible( 'subtotal_mismatch_line_name' )
|
||||
->action_class( 'subtotal_mismatch_behavior', 'active' )
|
||||
->to_array(),
|
||||
)
|
||||
),
|
||||
|
@ -548,6 +549,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'type' => 'text',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'The name of the extra line that will be sent to PayPal to correct the subtotal mismatch.', 'woocommerce-paypal-payments' ),
|
||||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'maxlength' => 22,
|
||||
'default' => '',
|
||||
'screens' => array(
|
||||
|
|
|
@ -91,7 +91,7 @@ return function ( ContainerInterface $container, array $fields ): array {
|
|||
'title' => __( 'Customize Smart Buttons Per Location', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Customize smart button style per location', 'woocommerce-paypal-payments' ),
|
||||
'default' => true,
|
||||
'default' => false,
|
||||
'screens' => array( State::STATE_START, State::STATE_ONBOARDED ),
|
||||
'requirements' => array(),
|
||||
'gateway' => 'paypal',
|
||||
|
|
|
@ -142,7 +142,7 @@ class Settings implements ContainerInterface {
|
|||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'smart_button_locations' => $this->default_button_locations,
|
||||
'smart_button_enable_styling_per_location' => true,
|
||||
'smart_button_enable_styling_per_location' => false,
|
||||
'pay_later_messaging_enabled' => true,
|
||||
'pay_later_button_enabled' => true,
|
||||
'pay_later_button_locations' => $this->default_pay_later_button_locations,
|
||||
|
|
|
@ -262,7 +262,7 @@ class SettingsRenderer {
|
|||
$html = sprintf(
|
||||
'<h3 class="wc-settings-sub-title %s">%s</h3>',
|
||||
esc_attr( implode( ' ', $config['class'] ) ),
|
||||
esc_html( $config['heading'] )
|
||||
isset( $config['heading_html'] ) ? $config['heading_html'] : esc_html( $config['heading'] )
|
||||
);
|
||||
|
||||
return $html;
|
||||
|
@ -388,7 +388,12 @@ $data_rows_html
|
|||
<th scope="row">
|
||||
<label
|
||||
for="<?php echo esc_attr( $id ); ?>"
|
||||
><?php echo esc_html( $config['title'] ); ?></label>
|
||||
>
|
||||
<?php
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo isset( $config['title_html'] ) ? $config['title_html'] : esc_html( $config['title'] );
|
||||
?>
|
||||
</label>
|
||||
<?php if ( isset( $config['desc_tip'] ) && $config['desc_tip'] ) : ?>
|
||||
<span
|
||||
class="woocommerce-help-tip"
|
||||
|
|
|
@ -456,6 +456,53 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue