mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 10:55:00 +08:00
Merge pull request #2155 from woocommerce/PCP-2347-new-feature-accelerated-checkout
New feature: Accelerated Checkout (2347)
This commit is contained in:
commit
a3bbcfeec5
50 changed files with 5702 additions and 58 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;
|
||||
|
@ -809,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',
|
||||
|
@ -884,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(
|
||||
|
@ -922,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() ) {
|
||||
|
@ -1509,6 +1563,7 @@ return array(
|
|||
PayUponInvoiceGateway::ID,
|
||||
CardButtonGateway::ID,
|
||||
OXXOGateway::ID,
|
||||
AxoGateway::ID,
|
||||
);
|
||||
},
|
||||
'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue