🔀 Merge branch 'trunk'
76
.github/workflows-config/typos.toml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
[default]
|
||||
check-comments = true
|
||||
check-docs = true
|
||||
check-filenames = true
|
||||
binary = false
|
||||
ignore-non-words = false
|
||||
|
||||
type = ["de", "en"]
|
||||
locale = "en"
|
||||
|
||||
# Ignore API keys and two character IDs like 'FO' or "FO"
|
||||
extend-ignore-re = [
|
||||
"\\b[0-9A-Za-z+/_-]{50,}(=|==)?\\b",
|
||||
"'[A-Za-z0-9]{2}'",
|
||||
"\"[A-Za-z0-9]{2}\"",
|
||||
]
|
||||
|
||||
# Known good words
|
||||
[default.extend-words]
|
||||
paypal = "paypal"
|
||||
PayPal = "PayPal"
|
||||
woocommerce = "woocommerce"
|
||||
Automattic = "Automattic"
|
||||
automattic = "automattic"
|
||||
Sie = "Sie"
|
||||
sie = "sie"
|
||||
oder = "oder"
|
||||
als = "als"
|
||||
|
||||
# Define known typos to catch
|
||||
[default.extend-corrections]
|
||||
Fatslane = "Fastlane"
|
||||
Fastalne = "Fastlane"
|
||||
Faslane = "Fastlane"
|
||||
Fastlain = "Fastlane"
|
||||
Fasltane = "Fastlane"
|
||||
Fastlan = "Fastlane"
|
||||
Fastlanne = "Fastlane"
|
||||
Fatstlane = "Fastlane"
|
||||
Fstlane = "Fastlane"
|
||||
|
||||
# Explicit identifier corrections
|
||||
[default.extend-identifiers]
|
||||
"Fatslane" = "Fastlane"
|
||||
"Fastalne" = "Fastlane"
|
||||
"Faslane" = "Fastlane"
|
||||
"Fastlain" = "Fastlane"
|
||||
"Fasltane" = "Fastlane"
|
||||
"Fastlan" = "Fastlane"
|
||||
"Fastlanne" = "Fastlane"
|
||||
"Fatstlane" = "Fastlane"
|
||||
"Fstlane" = "Fastlane"
|
||||
|
||||
# Type-specific configurations
|
||||
[type.php]
|
||||
check-comments = true
|
||||
check-docs = true
|
||||
|
||||
[type.js]
|
||||
extend-glob = []
|
||||
binary = false
|
||||
check-filename = true
|
||||
check-file = true
|
||||
unicode = true
|
||||
ignore-hex = true
|
||||
identifier-leading-digits = false
|
||||
|
||||
[files]
|
||||
extend-exclude = [
|
||||
"vendor/*",
|
||||
"node_modules/*",
|
||||
"*.min.js",
|
||||
"*.min.css",
|
||||
"modules/ppcp-order-tracking/*",
|
||||
"/tests/*"
|
||||
]
|
20
.github/workflows/spell-check.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: Spell Check
|
||||
|
||||
on:
|
||||
# Run on all pull requests
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
typos:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check for typos
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling
|
||||
id: spelling
|
||||
uses: crate-ci/typos@v1.30.2
|
||||
with:
|
||||
# Path to config file
|
||||
config: .github/workflows-config/typos.toml
|
|
@ -2094,6 +2094,16 @@ function wcs_order_contains_product($order, $product)
|
|||
*/
|
||||
function wc_get_page_screen_id( $for ) {}
|
||||
|
||||
/**
|
||||
* Checks if manual renewals are enabled.
|
||||
*
|
||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v4.0.0
|
||||
* @return bool Whether manual renewal is enabled.
|
||||
*/
|
||||
function wcs_is_manual_renewal_enabled()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscription Product Variation Class
|
||||
*
|
||||
|
|
|
@ -159,7 +159,7 @@
|
|||
* Fix - Shipping methods during callback not updated correctly #2421
|
||||
* Fix - Preserve subscription renewal processing when switching Subscriptions Mode or disabling gateway #2394
|
||||
* Fix - Remove shipping callback for Venmo express button #2374
|
||||
* Fix - Google Pay: Fix issuse with data.paymentSource being undefined #2390
|
||||
* Fix - Google Pay: Fix issue with data.paymentSource being undefined #2390
|
||||
* Fix - Loading of non-Order as a WC_Order causes warnings and potential data corruption #2343
|
||||
* Fix - Apple Pay and Google Pay buttons don't appear in PayPal Button stack on multi-step Checkout #2372
|
||||
* Fix - Apple Pay: Fix when shipping is disabled #2391
|
||||
|
|
|
@ -106,7 +106,7 @@ class PartnersEndpoint {
|
|||
* Returns the current seller status.
|
||||
*
|
||||
* @return SellerStatus
|
||||
* @throws RuntimeException When request could not be fullfilled.
|
||||
* @throws RuntimeException When request could not be fulfilled.
|
||||
*/
|
||||
public function seller_status() : SellerStatus {
|
||||
$url = trailingslashit( $this->host ) . 'v1/customer/partners/' . $this->partner_id . '/merchant-integrations/' . $this->merchant_id;
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
|||
*/
|
||||
class PhoneWithType {
|
||||
|
||||
const VALLID_TYPES = array(
|
||||
const VALID_TYPES = array(
|
||||
'FAX',
|
||||
'HOME',
|
||||
'MOBILE',
|
||||
|
@ -43,7 +43,7 @@ class PhoneWithType {
|
|||
* @param Phone $phone The phone.
|
||||
*/
|
||||
public function __construct( string $type, Phone $phone ) {
|
||||
$this->type = in_array( $type, self::VALLID_TYPES, true ) ? $type : 'OTHER';
|
||||
$this->type = in_array( $type, self::VALID_TYPES, true ) ? $type : 'OTHER';
|
||||
$this->phone = $phone;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class PatchCollectionFactory {
|
|||
/**
|
||||
* Creates a Patch Collection by comparing two orders.
|
||||
*
|
||||
* @param Order $from The inital order.
|
||||
* @param Order $from The initial order.
|
||||
* @param Order $to The target order.
|
||||
*
|
||||
* @return PatchCollection
|
||||
|
|
|
@ -21,7 +21,7 @@ class ShippingPreferenceFactory {
|
|||
/**
|
||||
* Returns shipping_preference for the given state.
|
||||
*
|
||||
* @param PurchaseUnit $purchase_unit Thw PurchaseUnit.
|
||||
* @param PurchaseUnit $purchase_unit The PurchaseUnit.
|
||||
* @param string $context The operation context like 'checkout', 'cart'.
|
||||
* @param WC_Cart|null $cart The current cart if relevant.
|
||||
* @param string $funding_source The funding source (PayPal button) like 'paypal', 'venmo', 'card'.
|
||||
|
|
|
@ -88,7 +88,10 @@ class PartnerReferralsData {
|
|||
);
|
||||
|
||||
if ( true === $use_subscriptions ) {
|
||||
$capabilities[] = 'PAYPAL_WALLET_VAULTING_ADVANCED';
|
||||
if ( $this->dcc_applies->for_country_currency() ) {
|
||||
$capabilities[] = 'PAYPAL_WALLET_VAULTING_ADVANCED';
|
||||
}
|
||||
|
||||
$first_party_features[] = 'BILLING_AGREEMENT';
|
||||
}
|
||||
|
||||
|
|
|
@ -180,11 +180,22 @@ class ApplePayGateway extends WC_Payment_Gateway {
|
|||
);
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order );
|
||||
do_action_deprecated( 'woocommerce_paypal_payments_before_process_order', array( $wc_order ), '3.0.1', 'woocommerce_paypal_payments_before_order_process', __( 'Usage of this action is deprecated. Please use the filter woocommerce_paypal_payments_before_order_process instead.', 'woocommerce-paypal-payments' ) );
|
||||
|
||||
try {
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
|
|
|
@ -370,8 +370,8 @@ class ApplepayModule implements ServiceModule, ExtendingModule, ExecutableModule
|
|||
}
|
||||
$env = $c->get( 'settings.environment' );
|
||||
assert( $env instanceof Environment );
|
||||
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
|
||||
$this->load_domain_association_file( $is_sandobx );
|
||||
$is_sandbox = $env->current_environment_is( Environment::SANDBOX );
|
||||
$this->load_domain_association_file( $is_sandbox );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -316,7 +316,7 @@ class ApplePayDataObjectHttp {
|
|||
/**
|
||||
* Checks if the array contains all required fields and if those
|
||||
* are not empty.
|
||||
* If not it adds an unkown error to the object's error list, as this errors
|
||||
* If not it adds an unknown error to the object's error list, as this errors
|
||||
* are not supported by ApplePay.
|
||||
*
|
||||
* @param array $data The data.
|
||||
|
@ -396,7 +396,7 @@ class ApplePayDataObjectHttp {
|
|||
/**
|
||||
* Checks if the address array contains all required fields and if those
|
||||
* are not empty.
|
||||
* If not it adds a contacField error to the object's error list.
|
||||
* If not it adds a contactField error to the object's error list.
|
||||
*
|
||||
* @param array $post The address to check.
|
||||
* @param array $required The required fields for the given address.
|
||||
|
|
|
@ -254,7 +254,18 @@ class AxoGateway extends WC_Payment_Gateway {
|
|||
|
||||
$order = $this->create_paypal_order( $wc_order, $token );
|
||||
|
||||
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
return $this->handle_payment_failure( $wc_order, $exception );
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import MessagesBootstrap from '../../../../ppcp-button/resources/js/modules/ContextBootstrap/MessagesBootstap';
|
||||
import MessagesBootstrap from '../../../../ppcp-button/resources/js/modules/ContextBootstrap/MessagesBootstrap';
|
||||
import { debounce } from '../Helper/debounce';
|
||||
|
||||
class BlockCheckoutMessagesBootstrap {
|
||||
|
|
|
@ -59,7 +59,7 @@ if ( cartHasSubscriptionProducts( config.scriptData ) ) {
|
|||
blockEnabled = false;
|
||||
}
|
||||
|
||||
// Don't show buttons if cart contains free trial product and the stroe is not eligible for saving payment methods.
|
||||
// Don't show buttons if cart contains free trial product and the store is not eligible for saving payment methods.
|
||||
if (
|
||||
! config.scriptData.vault_v3_enabled &&
|
||||
config.scriptData.is_free_trial_cart
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import MiniCartBootstap from './modules/ContextBootstrap/MiniCartBootstap';
|
||||
import SingleProductBootstap from './modules/ContextBootstrap/SingleProductBootstap';
|
||||
import CartBootstrap from './modules/ContextBootstrap/CartBootstap';
|
||||
import CheckoutBootstap from './modules/ContextBootstrap/CheckoutBootstap';
|
||||
import MiniCartBootstrap from './modules/ContextBootstrap/MiniCartBootstrap';
|
||||
import SingleProductBootstrap from './modules/ContextBootstrap/SingleProductBootstrap';
|
||||
import CartBootstrap from './modules/ContextBootstrap/CartBootstrap';
|
||||
import CheckoutBootstrap from './modules/ContextBootstrap/CheckoutBootstrap';
|
||||
import PayNowBootstrap from './modules/ContextBootstrap/PayNowBootstrap';
|
||||
import Renderer from './modules/Renderer/Renderer';
|
||||
import ErrorHandler from './modules/ErrorHandler';
|
||||
|
@ -23,7 +23,7 @@ import FormSaver from './modules/Helper/FormSaver';
|
|||
import FormValidator from './modules/Helper/FormValidator';
|
||||
import { loadPaypalScript } from './modules/Helper/ScriptLoading';
|
||||
import buttonModuleWatcher from './modules/ButtonModuleWatcher';
|
||||
import MessagesBootstrap from './modules/ContextBootstrap/MessagesBootstap';
|
||||
import MessagesBootstrap from './modules/ContextBootstrap/MessagesBootstrap';
|
||||
import { apmButtonsInit } from './modules/Helper/ApmButtons';
|
||||
|
||||
// TODO: could be a good idea to have a separate spinner for each gateway,
|
||||
|
@ -246,7 +246,7 @@ const bootstrap = () => {
|
|||
);
|
||||
|
||||
if ( PayPalCommerceGateway.mini_cart_buttons_enabled === '1' ) {
|
||||
const miniCartBootstrap = new MiniCartBootstap(
|
||||
const miniCartBootstrap = new MiniCartBootstrap(
|
||||
PayPalCommerceGateway,
|
||||
renderer,
|
||||
errorHandler
|
||||
|
@ -264,7 +264,7 @@ const bootstrap = () => {
|
|||
( PayPalCommerceGateway.single_product_buttons_enabled === '1' ||
|
||||
hasMessages() )
|
||||
) {
|
||||
const singleProductBootstrap = new SingleProductBootstap(
|
||||
const singleProductBootstrap = new SingleProductBootstrap(
|
||||
PayPalCommerceGateway,
|
||||
renderer,
|
||||
errorHandler
|
||||
|
@ -289,17 +289,17 @@ const bootstrap = () => {
|
|||
}
|
||||
|
||||
if ( context === 'checkout' ) {
|
||||
const checkoutBootstap = new CheckoutBootstap(
|
||||
const checkoutBootstrap = new CheckoutBootstrap(
|
||||
PayPalCommerceGateway,
|
||||
renderer,
|
||||
spinner,
|
||||
errorHandler
|
||||
);
|
||||
|
||||
checkoutBootstap.init();
|
||||
checkoutBootstrap.init();
|
||||
buttonModuleWatcher.registerContextBootstrap(
|
||||
'checkout',
|
||||
checkoutBootstap
|
||||
checkoutBootstrap
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
dispatchButtonEvent,
|
||||
} from '../Helper/PaymentButtonHelpers';
|
||||
|
||||
class CheckoutBootstap {
|
||||
class CheckoutBootstrap {
|
||||
constructor( gateway, renderer, spinner, errorHandler ) {
|
||||
this.gateway = gateway;
|
||||
this.renderer = renderer;
|
||||
|
@ -344,4 +344,4 @@ class CheckoutBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default CheckoutBootstap;
|
||||
export default CheckoutBootstrap;
|
|
@ -1,7 +1,7 @@
|
|||
import CartActionHandler from '../ActionHandler/CartActionHandler';
|
||||
import BootstrapHelper from '../Helper/BootstrapHelper';
|
||||
|
||||
class MiniCartBootstap {
|
||||
class MiniCartBootstrap {
|
||||
constructor( gateway, renderer, errorHandler ) {
|
||||
this.gateway = gateway;
|
||||
this.renderer = renderer;
|
||||
|
@ -71,4 +71,4 @@ class MiniCartBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default MiniCartBootstap;
|
||||
export default MiniCartBootstrap;
|
|
@ -1,7 +1,7 @@
|
|||
import CheckoutBootstap from './CheckoutBootstap';
|
||||
import CheckoutBootstrap from './CheckoutBootstrap';
|
||||
import { isChangePaymentPage } from '../Helper/Subscriptions';
|
||||
|
||||
class PayNowBootstrap extends CheckoutBootstap {
|
||||
class PayNowBootstrap extends CheckoutBootstrap {
|
||||
constructor( gateway, renderer, spinner, errorHandler ) {
|
||||
super( gateway, renderer, spinner, errorHandler );
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { strRemoveWord, strAddWord, throttle } from '../Helper/Utils';
|
|||
import merge from 'deepmerge';
|
||||
import { debounce } from '../../../../../ppcp-blocks/resources/js/Helper/debounce';
|
||||
|
||||
class SingleProductBootstap {
|
||||
class SingleProductBootstrap {
|
||||
constructor( gateway, renderer, errorHandler ) {
|
||||
this.gateway = gateway;
|
||||
this.renderer = renderer;
|
||||
|
@ -162,6 +162,12 @@ class SingleProductBootstap {
|
|||
},
|
||||
]
|
||||
.map( ( f ) => f() )
|
||||
.sort((a, b) => {
|
||||
if (parseInt(a.replace(/\D/g, '')) < parseInt(b.replace(/\D/g, '')) ) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
})
|
||||
.find( ( val ) => val );
|
||||
|
||||
if ( typeof priceText === 'undefined' ) {
|
||||
|
@ -368,4 +374,4 @@ class SingleProductBootstap {
|
|||
}
|
||||
}
|
||||
|
||||
export default SingleProductBootstap;
|
||||
export default SingleProductBootstrap;
|
|
@ -15,7 +15,7 @@ class PreviewButtonManager {
|
|||
|
||||
/**
|
||||
* Resolves the promise.
|
||||
* Used by `this.boostrap()` to process enqueued initialization logic.
|
||||
* Used by `this.bootstrap()` to process enqueued initialization logic.
|
||||
*/
|
||||
#onInitResolver;
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ class DisabledFundingSources {
|
|||
/**
|
||||
* Filters the final list of disabled funding sources.
|
||||
*
|
||||
* @param array $diabled_funding The filter value, funding sources to be disabled.
|
||||
* @param array $disable_funding The filter value, funding sources to be disabled.
|
||||
* @param array $flags Decision flags to provide more context to filters.
|
||||
*/
|
||||
$disable_funding = apply_filters(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Handles the Early Order logic, when we need to create the WC_Order by ourselfs.
|
||||
* Handles the Early Order logic, when we need to create the WC_Order by ourselves.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Button\Helper
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\CardAuthenticationResultFactory
|
|||
class ThreeDSecure {
|
||||
|
||||
const NO_DECISION = 0;
|
||||
const PROCCEED = 1;
|
||||
const PROCEED = 1;
|
||||
const REJECT = 2;
|
||||
const RETRY = 3;
|
||||
|
||||
|
@ -84,7 +84,7 @@ class ThreeDSecure {
|
|||
$this->logger->info( '3DS Authentication Result: ' . wc_print_r( $result->to_array(), true ) );
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_POSSIBLE ) {
|
||||
return $this->return_decision( self::PROCCEED, $order );
|
||||
return $this->return_decision( self::PROCEED, $order );
|
||||
}
|
||||
|
||||
if ( $result->liability_shift() === AuthResult::LIABILITY_SHIFT_UNKNOWN ) {
|
||||
|
@ -124,19 +124,19 @@ class ThreeDSecure {
|
|||
$result->enrollment_status() === AuthResult::ENROLLMENT_STATUS_BYPASS
|
||||
&& ! $result->authentication_result()
|
||||
) {
|
||||
return self::PROCCEED;
|
||||
return self::PROCEED;
|
||||
}
|
||||
if (
|
||||
$result->enrollment_status() === AuthResult::ENROLLMENT_STATUS_UNAVAILABLE
|
||||
&& ! $result->authentication_result()
|
||||
) {
|
||||
return self::PROCCEED;
|
||||
return self::PROCEED;
|
||||
}
|
||||
if (
|
||||
$result->enrollment_status() === AuthResult::ENROLLMENT_STATUS_NO
|
||||
&& ! $result->authentication_result()
|
||||
) {
|
||||
return self::PROCCEED;
|
||||
return self::PROCEED;
|
||||
}
|
||||
|
||||
if ( $result->authentication_result() === AuthResult::AUTHENTICATION_RESULT_REJECTED ) {
|
||||
|
|
|
@ -189,11 +189,22 @@ class GooglePayGateway extends WC_Payment_Gateway {
|
|||
}
|
||||
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order );
|
||||
do_action_deprecated( 'woocommerce_paypal_payments_before_process_order', array( $wc_order ), '3.0.1', 'woocommerce_paypal_payments_before_order_process', __( 'Usage of this action is deprecated. Please use the filter woocommerce_paypal_payments_before_order_process instead.', 'woocommerce-paypal-payments' ) );
|
||||
|
||||
try {
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
|
|
|
@ -236,10 +236,10 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) {
|
|||
( element.style.display = ! isExpress ? '' : 'none' )
|
||||
);
|
||||
|
||||
const screemImg = document.querySelector(
|
||||
const screenImg = document.querySelector(
|
||||
'#ppcp-onboarding-cards-screen-img'
|
||||
);
|
||||
if ( screemImg ) {
|
||||
if ( screenImg ) {
|
||||
const currentRb =
|
||||
Array.from(
|
||||
document.querySelectorAll(
|
||||
|
@ -248,7 +248,7 @@ window.ppcp_onboarding_productionCallback = function ( ...args ) {
|
|||
).filter( ( rb ) => rb.checked )[ 0 ] ?? null;
|
||||
|
||||
const imgUrl = currentRb.getAttribute( 'data-screen-url' );
|
||||
screemImg.src = imgUrl;
|
||||
screenImg.src = imgUrl;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -359,7 +359,7 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
payLaterMessagingInputSelectorByLocation( location )
|
||||
)
|
||||
: inputSelectros.concat(
|
||||
butttonInputSelectorByLocation( location )
|
||||
buttonInputSelectorByLocation( location )
|
||||
);
|
||||
} );
|
||||
|
||||
|
@ -386,7 +386,7 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
return inputSelectors;
|
||||
};
|
||||
|
||||
const butttonInputSelectorByLocation = ( location ) => {
|
||||
const buttonInputSelectorByLocation = ( location ) => {
|
||||
const locationPrefix = location === 'checkout' ? '' : '_' + location;
|
||||
const inputSelectors = [
|
||||
'#field-button' + locationPrefix + '_layout',
|
||||
|
|
|
@ -9,7 +9,7 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
const variableId = children[ i ]
|
||||
.querySelector( 'h3' )
|
||||
.getElementsByClassName( 'variable_post_id' )[ 0 ].value;
|
||||
if ( parseInt( variableId ) === productId ) {
|
||||
if ( variableId === productId ) {
|
||||
children[ i ]
|
||||
.querySelector( '.woocommerce_variable_attributes' )
|
||||
.getElementsByClassName(
|
||||
|
@ -83,7 +83,7 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
if (! price || parseInt( price ) <= 0 ) {
|
||||
linkBtn.setAttribute('title', __( 'Prices must be above zero for PayPal Subscriptions!', 'woocommerce-paypal-subscriptions' ) );
|
||||
} else {
|
||||
linkBtn.setAttribute('title', __( 'Not allowed period intervall combination for PayPal Subscriptions!', 'woocommerce-paypal-subscriptions' ) );
|
||||
linkBtn.setAttribute('title', __( 'Not allowed period interval combination for PayPal Subscriptions!', 'woocommerce-paypal-subscriptions' ) );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -122,21 +122,26 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
|
||||
jQuery( '.wc_input_subscription_price' ).trigger( 'change' );
|
||||
|
||||
PayPalCommerceGatewayPayPalSubscriptionProducts?.forEach(
|
||||
( product ) => {
|
||||
if ( product.product_connected === 'yes' ) {
|
||||
disableFields( product.product_id );
|
||||
}
|
||||
let variationProductIds = [ PayPalCommerceGatewayPayPalSubscriptionProducts.product_id ];
|
||||
const variationsInput = document.querySelectorAll( '.variable_post_id' );
|
||||
for ( let i = 0; i < variationsInput.length; i++ ) {
|
||||
variationProductIds.push( variationsInput[ i ].value );
|
||||
}
|
||||
|
||||
variationProductIds?.forEach(
|
||||
( productId ) => {
|
||||
const linkBtn = document.getElementById(
|
||||
`ppcp_enable_subscription_product-${ product.product_id }`
|
||||
`ppcp_enable_subscription_product-${ productId }`
|
||||
);
|
||||
if ( linkBtn.checked && linkBtn.value === 'yes' ) {
|
||||
disableFields( productId );
|
||||
}
|
||||
linkBtn?.addEventListener( 'click', ( event ) => {
|
||||
const unlinkBtnP = document.getElementById(
|
||||
`ppcp-enable-subscription-${ product.product_id }`
|
||||
`ppcp-enable-subscription-${ productId }`
|
||||
);
|
||||
const titleP = document.getElementById(
|
||||
`ppcp_subscription_plan_name_p-${ product.product_id }`
|
||||
`ppcp_subscription_plan_name_p-${ productId }`
|
||||
);
|
||||
if (event.target.checked === true) {
|
||||
if ( unlinkBtnP ) {
|
||||
|
@ -156,26 +161,26 @@ document.addEventListener( 'DOMContentLoaded', () => {
|
|||
});
|
||||
|
||||
const unlinkBtn = document.getElementById(
|
||||
`ppcp-unlink-sub-plan-${ product.product_id }`
|
||||
`ppcp-unlink-sub-plan-${ productId }`
|
||||
);
|
||||
unlinkBtn?.addEventListener( 'click', ( event ) => {
|
||||
event.preventDefault();
|
||||
unlinkBtn.disabled = true;
|
||||
const spinner = document.getElementById(
|
||||
'spinner-unlink-plan'
|
||||
`spinner-unlink-plan-${ productId }`
|
||||
);
|
||||
spinner.style.display = 'inline-block';
|
||||
|
||||
fetch( product.ajax.deactivate_plan.endpoint, {
|
||||
fetch( PayPalCommerceGatewayPayPalSubscriptionProducts.ajax.deactivate_plan.endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify( {
|
||||
nonce: product.ajax.deactivate_plan.nonce,
|
||||
plan_id: product.plan_id,
|
||||
product_id: product.product_id,
|
||||
nonce: PayPalCommerceGatewayPayPalSubscriptionProducts.ajax.deactivate_plan.nonce,
|
||||
plan_id: linkBtn.dataset.subsPlan,
|
||||
product_id: productId,
|
||||
} ),
|
||||
} )
|
||||
.then( function ( res ) {
|
||||
|
|
|
@ -12,10 +12,7 @@ namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
|||
use ActionScheduler_Store;
|
||||
use WC_Order;
|
||||
use WC_Product;
|
||||
use WC_Product_Subscription;
|
||||
use WC_Product_Subscription_Variation;
|
||||
use WC_Product_Variable;
|
||||
use WC_Product_Variable_Subscription;
|
||||
use WC_Subscription;
|
||||
use WC_Subscriptions_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
|
@ -28,6 +25,7 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameI
|
|||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WP_Post;
|
||||
|
@ -56,6 +54,146 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
if ( ! $subscriptions_helper->plugin_is_active() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'woocommerce_available_payment_gateways',
|
||||
function ( array $gateways ) use ( $c ) {
|
||||
if ( is_account_page() || is_admin() || ! WC()->cart || WC()->cart->is_empty() || wcs_is_manual_renewal_enabled() ) {
|
||||
return $gateways;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( $subscriptions_mode !== 'subscriptions_api' ) {
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
$pp_subscriptions_product = false;
|
||||
foreach ( WC()->cart->get_cart() as $cart_item ) {
|
||||
$cart_product = wc_get_product( $cart_item['product_id'] );
|
||||
if ( isset( $cart_item['subscription_renewal']['subscription_id'] ) ) {
|
||||
$subscription_renewal = wcs_get_subscription( $cart_item['subscription_renewal']['subscription_id'] );
|
||||
if ( $subscription_renewal && $subscription_renewal->get_meta( 'ppcp_subscription' ) ) {
|
||||
$pp_subscriptions_product = true;
|
||||
break;
|
||||
}
|
||||
} elseif ( $cart_product instanceof \WC_Product_Subscription || $cart_product instanceof \WC_Product_Variable_Subscription ) {
|
||||
if ( $cart_product->get_meta( '_ppcp_enable_subscription_product' ) === 'yes' ) {
|
||||
$pp_subscriptions_product = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $pp_subscriptions_product ) {
|
||||
foreach ( $gateways as $id => $gateway ) {
|
||||
if ( $gateway->id !== PayPalGateway::ID ) {
|
||||
unset( $gateways[ $id ] );
|
||||
}
|
||||
}
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
return $gateways;
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_subscription_payment_gateway_supports',
|
||||
function ( bool $payment_gateway_supports, string $payment_gateway_feature, \WC_Subscription $wc_order ): bool {
|
||||
if ( ! in_array( $payment_gateway_feature, array( 'gateway_scheduled_payments', 'subscription_date_changes', 'subscription_amount_changes', 'subscription_payment_method_change', 'subscription_payment_method_change_customer', 'subscription_payment_method_change_admin' ), true ) ) {
|
||||
return $payment_gateway_supports;
|
||||
}
|
||||
|
||||
$subscription = wcs_get_subscription( $wc_order->get_id() );
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $payment_gateway_supports;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_id ) {
|
||||
return $payment_gateway_supports;
|
||||
}
|
||||
|
||||
if ( $payment_gateway_feature === 'gateway_scheduled_payments' ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
100,
|
||||
3
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_can_subscription_be_updated_to_active',
|
||||
function ( bool $can_be_updated, \WC_Subscription $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id && $subscription->get_status() === 'pending-cancel' ) {
|
||||
return true;
|
||||
}
|
||||
return $can_be_updated;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_can_subscription_be_updated_to_new-payment-method',
|
||||
function ( bool $can_be_updated, \WC_Subscription $subscription ) use ( $c ) {
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id ) {
|
||||
return false;
|
||||
}
|
||||
return $can_be_updated;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_before_order_process',
|
||||
function ( bool $process, \WC_Payment_Gateway $gateway, \WC_Order $wc_order ) use ( $c ) {
|
||||
if ( ! $gateway instanceof PayPalGateway || $gateway::ID !== 'ppcp-gateway' ) {
|
||||
return $process;
|
||||
}
|
||||
|
||||
$paypal_subscription_id = \WC()->session->get( 'ppcp_subscription_id' );
|
||||
if ( empty( $paypal_subscription_id ) || ! is_string( $paypal_subscription_id ) ) {
|
||||
return $process;
|
||||
}
|
||||
|
||||
$order = $c->get( 'session.handler' )->order();
|
||||
$gateway->add_paypal_meta( $wc_order, $order, $c->get( 'settings.environment' ) );
|
||||
|
||||
$subscriptions = function_exists( 'wcs_get_subscriptions_for_order' ) ? wcs_get_subscriptions_for_order( $wc_order ) : array();
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$subscription->update_meta_data( 'ppcp_subscription', $paypal_subscription_id );
|
||||
$subscription->save();
|
||||
// translators: %s PayPal Subscription id.
|
||||
$subscription->add_order_note( sprintf( __( 'PayPal subscription %s added.', 'woocommerce-paypal-payments' ), $paypal_subscription_id ) );
|
||||
}
|
||||
|
||||
$transaction_id = $gateway->get_paypal_order_transaction_id( $order );
|
||||
if ( $transaction_id ) {
|
||||
$gateway->update_transaction_id( $transaction_id, $wc_order, $c->get( 'woocommerce.logger.woocommerce' ) );
|
||||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
|
||||
return false;
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
add_action(
|
||||
'save_post',
|
||||
/**
|
||||
|
@ -64,12 +202,6 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $product_id ) use ( $c ) {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
if ( ! $subscriptions_helper->plugin_is_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
|
@ -82,6 +214,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
$nonce = wc_clean( wp_unslash( $_POST['_wcsnonce'] ?? '' ) );
|
||||
if (
|
||||
$subscriptions_mode !== 'subscriptions_api'
|
||||
|| wcs_is_manual_renewal_enabled()
|
||||
|| ! is_string( $nonce )
|
||||
|| ! wp_verify_nonce( $nonce, 'wcs_subscription_meta' ) ) {
|
||||
return;
|
||||
|
@ -107,7 +240,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function ( $passed_validation, $product_id ) use ( $c ) {
|
||||
if ( WC()->cart->is_empty() ) {
|
||||
if ( WC()->cart->is_empty() || wcs_is_manual_renewal_enabled() ) {
|
||||
return $passed_validation;
|
||||
}
|
||||
|
||||
|
@ -163,12 +296,9 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
function( $variation_id ) use ( $c ) {
|
||||
$wcsnonce_save_variations = wc_clean( wp_unslash( $_POST['_wcsnonce_save_variations'] ?? '' ) );
|
||||
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
if (
|
||||
! $subscriptions_helper->plugin_is_active()
|
||||
|| ! WC_Subscriptions_Product::is_subscription( $variation_id )
|
||||
! WC_Subscriptions_Product::is_subscription( $variation_id )
|
||||
|| wcs_is_manual_renewal_enabled()
|
||||
|| ! is_string( $wcsnonce_save_variations )
|
||||
|| ! wp_verify_nonce( $wcsnonce_save_variations, 'wcs_subscription_variations' )
|
||||
) {
|
||||
|
@ -234,127 +364,6 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_order_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $actions, $subscription = null ): array {
|
||||
if ( ! is_array( $actions ) || ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $actions;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id && isset( $actions['wcs_process_renewal'] ) ) {
|
||||
unset( $actions['wcs_process_renewal'] );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
},
|
||||
20,
|
||||
2
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'wcs_view_subscription_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $actions, $subscription ): array {
|
||||
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
|
||||
return $actions;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( $subscription_id && $subscription->get_status() === 'active' ) {
|
||||
$url = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'change_subscription_to' => 'cancelled',
|
||||
'ppcp_cancel_subscription' => $subscription->get_id(),
|
||||
)
|
||||
),
|
||||
'ppcp_cancel_subscription_nonce'
|
||||
);
|
||||
|
||||
array_unshift(
|
||||
$actions,
|
||||
array(
|
||||
'url' => esc_url( $url ),
|
||||
'name' => esc_html__( 'Cancel', 'woocommerce-paypal-payments' ),
|
||||
)
|
||||
);
|
||||
|
||||
$actions['cancel']['name'] = esc_html__( 'Suspend', 'woocommerce-paypal-payments' );
|
||||
unset( $actions['subscription_renewal_early'] );
|
||||
}
|
||||
|
||||
return $actions;
|
||||
},
|
||||
11,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_loaded',
|
||||
function() use ( $c ) {
|
||||
if ( ! function_exists( 'wcs_get_subscription' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cancel_subscription_id = wc_clean( wp_unslash( $_GET['ppcp_cancel_subscription'] ?? '' ) );
|
||||
$subscription = wcs_get_subscription( absint( $cancel_subscription_id ) );
|
||||
if ( ! wcs_is_subscription( $subscription ) || $subscription === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
$nonce = wc_clean( wp_unslash( $_GET['_wpnonce'] ?? '' ) );
|
||||
if ( ! is_string( $nonce ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
$subscription_id
|
||||
&& $cancel_subscription_id
|
||||
&& $nonce
|
||||
) {
|
||||
if (
|
||||
! wp_verify_nonce( $nonce, 'ppcp_cancel_subscription_nonce' )
|
||||
|| ! user_can( get_current_user_id(), 'edit_shop_subscription_status', $subscription->get_id() )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
|
||||
$subscription_id = $subscription->get_meta( 'ppcp_subscription' );
|
||||
try {
|
||||
$subscriptions_endpoint->cancel( $subscription_id );
|
||||
|
||||
$subscription->update_status( 'cancelled' );
|
||||
$subscription->add_order_note( __( 'Subscription cancelled by the subscriber from their account page.', 'woocommerce-paypal-payments' ) );
|
||||
wc_add_notice( __( 'Your subscription has been cancelled.', 'woocommerce-paypal-payments' ) );
|
||||
|
||||
wp_safe_redirect( $subscription->get_view_order_url() );
|
||||
exit;
|
||||
} catch ( RuntimeException $exception ) {
|
||||
$error = $exception->getMessage();
|
||||
if ( is_a( $exception, PayPalApiException::class ) ) {
|
||||
$error = $exception->get_details( $error );
|
||||
}
|
||||
|
||||
$logger = $c->get( 'woocommerce.logger.woocommerce' );
|
||||
$logger->error( 'Could not cancel subscription product on PayPal. ' . $error );
|
||||
}
|
||||
}
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_subscription_before_actions',
|
||||
/**
|
||||
|
@ -459,6 +468,9 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
add_action(
|
||||
'woocommerce_product_options_general_product_data',
|
||||
function() use ( $c ) {
|
||||
if ( wcs_is_manual_renewal_enabled() ) {
|
||||
return;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
|
@ -496,6 +508,9 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $loop, $variation_data, $variation ) use ( $c ) {
|
||||
if ( wcs_is_manual_renewal_enabled() ) {
|
||||
return;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
|
@ -527,34 +542,12 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $hook ) use ( $c ) {
|
||||
if ( ! is_string( $hook ) ) {
|
||||
if ( ! is_string( $hook ) || wcs_is_manual_renewal_enabled() ) {
|
||||
return;
|
||||
}
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
$subscription_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( $hook !== 'post.php' || $subscription_mode !== 'subscriptions_api' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) );
|
||||
$product = wc_get_product( $post_id );
|
||||
if ( ! ( is_a( $product, WC_Product::class ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
if (
|
||||
! $subscriptions_helper->plugin_is_active()
|
||||
|| ! (
|
||||
is_a( $product, WC_Product_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Variable_Subscription::class )
|
||||
|| is_a( $product, WC_Product_Subscription_Variation::class )
|
||||
)
|
||||
|| ! WC_Subscriptions_Product::is_subscription( $product )
|
||||
) {
|
||||
if ( $hook !== 'post.php' && $hook !== 'post-new.php' && $subscription_mode !== 'subscriptions_api' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -562,7 +555,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
wp_enqueue_script(
|
||||
'ppcp-paypal-subscription',
|
||||
untrailingslashit( $module_url ) . '/assets/js/paypal-subscription.js',
|
||||
array( 'jquery', 'wc-admin-product-editor' ),
|
||||
array( 'jquery' ),
|
||||
$c->get( 'ppcp.asset-version' ),
|
||||
true
|
||||
);
|
||||
|
@ -572,34 +565,23 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
'woocommerce-paypal-payments'
|
||||
);
|
||||
|
||||
$products = array( $this->set_product_config( $product ) );
|
||||
if ( $product->get_type() === 'variable-subscription' ) {
|
||||
$products = array();
|
||||
|
||||
/**
|
||||
* Suppress pslam.
|
||||
*
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*
|
||||
* WC_Product_Variable_Subscription extends WC_Product_Variable.
|
||||
*/
|
||||
assert( $product instanceof WC_Product_Variable );
|
||||
$available_variations = $product->get_available_variations();
|
||||
foreach ( $available_variations as $variation ) {
|
||||
/**
|
||||
* The method is defined in WooCommerce.
|
||||
*
|
||||
* @psalm-suppress UndefinedMethod
|
||||
*/
|
||||
$variation = wc_get_product_object( 'variation', $variation['variation_id'] );
|
||||
$products[] = $this->set_product_config( $variation );
|
||||
}
|
||||
$product = wc_get_product();
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-paypal-subscription',
|
||||
'PayPalCommerceGatewayPayPalSubscriptionProducts',
|
||||
$products
|
||||
array(
|
||||
'ajax' => array(
|
||||
'deactivate_plan' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
'product_id' => $product->get_id(),
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -747,29 +729,6 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns subscription product configuration.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @return array
|
||||
*/
|
||||
private function set_product_config( WC_Product $product ): array {
|
||||
$plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array();
|
||||
$plan_id = $plan['id'] ?? '';
|
||||
|
||||
return array(
|
||||
'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '',
|
||||
'plan_id' => $plan_id,
|
||||
'product_id' => $product->get_id(),
|
||||
'ajax' => array(
|
||||
'deactivate_plan' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render PayPal Subscriptions fields.
|
||||
*
|
||||
|
@ -780,15 +739,19 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void {
|
||||
$enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' );
|
||||
$style = $product->get_type() === 'subscription_variation' ? 'float:left; width:150px;' : '';
|
||||
$subscription_product = $product->get_meta( 'ppcp_subscription_product' );
|
||||
$subscription_plan = $product->get_meta( 'ppcp_subscription_plan' );
|
||||
$subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' );
|
||||
|
||||
echo '<p class="form-field">';
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s are label open and close tags.
|
||||
esc_html__( '%1$sConnect to PayPal%2$s', 'woocommerce-paypal-payments' ),
|
||||
'<label for="_ppcp_enable_subscription_product-' . esc_attr( (string) $product->get_id() ) . '" style="' . esc_attr( $style ) . '">',
|
||||
'<label for="ppcp_enable_subscription_product-' . esc_attr( (string) $product->get_id() ) . '" style="' . esc_attr( $style ) . '">',
|
||||
'</label>'
|
||||
);
|
||||
echo '<input type="checkbox" id="ppcp_enable_subscription_product-' . esc_attr( (string) $product->get_id() ) . '" name="_ppcp_enable_subscription_product" value="yes" ' . checked( $enable_subscription_product, 'yes', false ) . '/>';
|
||||
$plan_id = isset( $subscription_plan['id'] ) ?? '';
|
||||
echo '<input type="checkbox" id="ppcp_enable_subscription_product-' . esc_attr( (string) $product->get_id() ) . '" data-subs-plan="' . esc_attr( (string) $plan_id ) . '" name="_ppcp_enable_subscription_product" value="yes" ' . checked( $enable_subscription_product, 'yes', false ) . '/>';
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s are label open and close tags.
|
||||
esc_html__( '%1$sConnect Product to PayPal Subscriptions Plan%2$s', 'woocommerce-paypal-payments' ),
|
||||
|
@ -799,9 +762,6 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
echo wc_help_tip( esc_html__( 'Create a subscription product and plan to bill customers at regular intervals. Be aware that certain subscription settings cannot be modified once the PayPal Subscription is linked to this product. Unlink the product to edit disabled fields.', 'woocommerce-paypal-payments' ) );
|
||||
echo '</p>';
|
||||
|
||||
$subscription_product = $product->get_meta( 'ppcp_subscription_product' );
|
||||
$subscription_plan = $product->get_meta( 'ppcp_subscription_plan' );
|
||||
$subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' );
|
||||
if ( $subscription_product || $subscription_plan ) {
|
||||
$display_unlink_p = 'display:none;';
|
||||
if ( $enable_subscription_product !== 'yes' ) {
|
||||
|
@ -811,7 +771,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
// translators: %1$s and %2$s are button and wrapper html tags.
|
||||
esc_html__( '%1$sUnlink PayPal Subscription Plan%2$s', 'woocommerce-paypal-payments' ),
|
||||
'<p class="form-field ppcp-enable-subscription" id="ppcp-enable-subscription-' . esc_attr( (string) $product->get_id() ) . '" style="' . esc_attr( $display_unlink_p ) . '"><label></label><button class="button ppcp-unlink-sub-plan" id="ppcp-unlink-sub-plan-' . esc_attr( (string) $product->get_id() ) . '">',
|
||||
'</button><span class="spinner is-active" id="spinner-unlink-plan" style="float: none; display:none;"></span></p>'
|
||||
'</button><span class="spinner is-active" id="spinner-unlink-plan-' . esc_attr( (string) $product->get_id() ) . '" style="float: none; display:none;"></span></p>'
|
||||
);
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s is open and closing paragraph tag.
|
||||
|
@ -839,7 +799,7 @@ class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, Execu
|
|||
}
|
||||
} else {
|
||||
$display_plan_name_p = '';
|
||||
if ( $enable_subscription_product !== 'yes' && $product->get_name() !== 'AUTO-DRAFT' ) {
|
||||
if ( $enable_subscription_product !== 'yes' ) {
|
||||
$display_plan_name_p = 'display:none;';
|
||||
}
|
||||
echo sprintf(
|
||||
|
|
|
@ -55,7 +55,7 @@ class SubscriptionStatus {
|
|||
* @return void
|
||||
*/
|
||||
public function update_status( string $subscription_status, string $subscription_id ): void {
|
||||
if ( $subscription_status === 'pending-cancel' || $subscription_status === 'cancelled' ) {
|
||||
if ( $subscription_status === 'cancelled' ) {
|
||||
try {
|
||||
$current_subscription = $this->subscriptions_endpoint->subscription( $subscription_id );
|
||||
if ( $current_subscription->status === 'CANCELLED' ) {
|
||||
|
@ -81,7 +81,7 @@ class SubscriptionStatus {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $subscription_status === 'on-hold' ) {
|
||||
if ( $subscription_status === 'on-hold' || $subscription_status === 'pending-cancel' ) {
|
||||
try {
|
||||
$this->logger->info(
|
||||
sprintf(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Controlls the cancel mechanism to step out of the PayPal order session.
|
||||
* Controls the cancel mechanism to step out of the PayPal order session.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Session\Cancellation
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,7 @@ const Fastlane = ( { learnMore = '' } ) => {
|
|||
<PricingTitleBadge item="fast country currency=storeCurrency=storeCountrylane" />
|
||||
}
|
||||
description={ __(
|
||||
"Speed up guest checkout with Fatslane. Link a customer's email address to their payment details.",
|
||||
"Speed up guest checkout with Fastlane. Link a customer's email address to their payment details.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
learnMoreLink={ learnMore }
|
||||
|
|
|
@ -6,8 +6,13 @@ import useSettingDependencyState from '../../../../../hooks/useSettingDependency
|
|||
import PaymentDependencyMessage from './PaymentDependencyMessage';
|
||||
import SettingDependencyMessage from './SettingDependencyMessage';
|
||||
import SpinnerOverlay from '../../../../ReusableComponents/SpinnerOverlay';
|
||||
import { PaymentHooks, SettingsHooks } from '../../../../../data';
|
||||
import {
|
||||
PaymentHooks,
|
||||
SettingsHooks,
|
||||
OnboardingHooks,
|
||||
} from '../../../../../data';
|
||||
import { useNavigation } from '../../../../../hooks/useNavigation';
|
||||
import usePaymentGatewayRefresh from '../../../../../hooks/usePaymentGatewayRefresh';
|
||||
|
||||
/**
|
||||
* Renders a payment method card with dependency handling
|
||||
|
@ -36,6 +41,10 @@ const PaymentMethodCard = ( {
|
|||
const { isReady: isPaymentStoreReady } = PaymentHooks.useStore();
|
||||
const { isReady: isSettingsStoreReady } = SettingsHooks.useStore();
|
||||
const { handleHighlightFromUrl } = useNavigation();
|
||||
const { gatewaysRefreshed } = OnboardingHooks.useGatewayRefresh();
|
||||
|
||||
// Re-fetch payment gateway data to hide methods based on exclusion conditions.
|
||||
usePaymentGatewayRefresh();
|
||||
|
||||
const paymentDependencies = usePaymentDependencyState(
|
||||
methods,
|
||||
|
@ -50,7 +59,11 @@ const PaymentMethodCard = ( {
|
|||
}
|
||||
}, [ handleHighlightFromUrl, isPaymentStoreReady, isSettingsStoreReady ] );
|
||||
|
||||
if ( ! isPaymentStoreReady || ! isSettingsStoreReady ) {
|
||||
if (
|
||||
! isPaymentStoreReady ||
|
||||
! isSettingsStoreReady ||
|
||||
! gatewaysRefreshed
|
||||
) {
|
||||
return <SpinnerOverlay asModal={ true } />;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,16 @@ import Features from '../Components/Overview/Features/Features';
|
|||
import Help from '../Components/Overview/Help/Help';
|
||||
import { TodosHooks, CommonHooks, FeaturesHooks } from '../../../../data';
|
||||
import SpinnerOverlay from '../../../ReusableComponents/SpinnerOverlay';
|
||||
import usePaymentGatewaySync from '../../../../hooks/usePaymentGatewaySync';
|
||||
|
||||
const TabOverview = () => {
|
||||
const { isReady: areTodosReady } = TodosHooks.useTodos();
|
||||
const { isReady: merchantIsReady } = CommonHooks.useMerchantInfo();
|
||||
const { isReady: featuresIsReady } = FeaturesHooks.useFeatures();
|
||||
|
||||
// Enable payment gateways after onboarding based on relevant flags.
|
||||
usePaymentGatewaySync();
|
||||
|
||||
if ( ! areTodosReady || ! merchantIsReady || ! featuresIsReady ) {
|
||||
return <SpinnerOverlay asModal={ true } />;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export default {
|
|||
// Transient data
|
||||
SET_TRANSIENT: 'ppcp/features/SET_TRANSIENT',
|
||||
|
||||
// Persistant data
|
||||
// Persistent data
|
||||
SET_FEATURES: 'ppcp/features/SET_FEATURES',
|
||||
HYDRATE: 'ppcp/features/HYDRATE',
|
||||
};
|
||||
|
|
|
@ -12,4 +12,8 @@ export default {
|
|||
SET_PERSISTENT: 'ppcp/onboarding/SET_PERSISTENT',
|
||||
RESET: 'ppcp/onboarding/RESET',
|
||||
HYDRATE: 'ppcp/onboarding/HYDRATE',
|
||||
|
||||
// Gateway sync flag
|
||||
SYNC_GATEWAYS: 'ppcp/onboarding/SYNC_GATEWAYS',
|
||||
REFRESH_GATEWAYS: 'ppcp/onboarding/REFRESH_GATEWAYS',
|
||||
};
|
||||
|
|
|
@ -100,3 +100,43 @@ export function refresh() {
|
|||
select.persistentData();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Persistent. Updates the gateway synced status.
|
||||
*
|
||||
* @param {boolean} synced The sync status to set
|
||||
* @return {Action} The action.
|
||||
*/
|
||||
export const updateGatewaysSynced = ( synced = true ) =>
|
||||
setPersistent( 'gatewaysSynced', synced );
|
||||
|
||||
/**
|
||||
* Persistent. Updates the gateway refreshed status.
|
||||
*
|
||||
* @param {boolean} refreshed The refreshed status to set
|
||||
* @return {Action} The action.
|
||||
*/
|
||||
export const updateGatewaysRefreshed = ( refreshed = true ) =>
|
||||
setPersistent( 'gatewaysRefreshed', refreshed );
|
||||
|
||||
/**
|
||||
* Action creator to sync payment gateways.
|
||||
* This will both update the state and persist it.
|
||||
*
|
||||
* @return {Function} The thunk function.
|
||||
*/
|
||||
export function syncGateways() {
|
||||
return async ( { dispatch } ) => {
|
||||
dispatch( setPersistent( 'gatewaysSynced', true ) );
|
||||
await dispatch.persist();
|
||||
return { success: true };
|
||||
};
|
||||
}
|
||||
|
||||
export function refreshGateways() {
|
||||
return async ( { dispatch } ) => {
|
||||
dispatch( setPersistent( 'gatewaysRefreshed', true ) );
|
||||
await dispatch.persist();
|
||||
return { success: true };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,10 +12,11 @@ import { useSelect, useDispatch } from '@wordpress/data';
|
|||
import { createHooksForStore } from '../utils';
|
||||
import { PRODUCT_TYPES } from './configuration';
|
||||
import { STORE_NAME } from './constants';
|
||||
import ACTION_TYPES from './action-types';
|
||||
|
||||
const useHooks = () => {
|
||||
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
|
||||
const { persist } = useDispatch( STORE_NAME );
|
||||
const { persist, dispatch } = useDispatch( STORE_NAME );
|
||||
|
||||
// Read-only flags and derived state.
|
||||
const flags = useSelect( ( select ) => select( STORE_NAME ).flags(), [] );
|
||||
|
@ -36,6 +37,12 @@ const useHooks = () => {
|
|||
'areOptionalPaymentMethodsEnabled'
|
||||
);
|
||||
const [ products, setProducts ] = usePersistent( 'products' );
|
||||
// Add the setter for gatewaysSynced
|
||||
const [ gatewaysSynced, setGatewaysSynced ] =
|
||||
usePersistent( 'gatewaysSynced' );
|
||||
|
||||
const [ gatewaysRefreshed, setGatewaysRefreshed ] =
|
||||
usePersistent( 'gatewaysRefreshed' );
|
||||
|
||||
const savePersistent = async ( setter, value ) => {
|
||||
setter( value );
|
||||
|
@ -76,6 +83,26 @@ const useHooks = () => {
|
|||
);
|
||||
return savePersistent( setProducts, validProducts );
|
||||
},
|
||||
gatewaysSynced,
|
||||
setGatewaysSynced: ( value ) => {
|
||||
return savePersistent( setGatewaysSynced, value );
|
||||
},
|
||||
syncGateways: async () => {
|
||||
await savePersistent( setGatewaysSynced, true );
|
||||
dispatch( {
|
||||
type: ACTION_TYPES.SYNC_GATEWAYS,
|
||||
} );
|
||||
},
|
||||
gatewaysRefreshed,
|
||||
setGatewaysRefreshed: ( value ) => {
|
||||
return savePersistent( setGatewaysRefreshed, value );
|
||||
},
|
||||
refreshGateways: async () => {
|
||||
await savePersistent( setGatewaysRefreshed, true );
|
||||
dispatch( {
|
||||
type: ACTION_TYPES.REFRESH_GATEWAYS,
|
||||
} );
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -145,3 +172,13 @@ export const useFlags = () => {
|
|||
const { flags } = useHooks();
|
||||
return flags;
|
||||
};
|
||||
|
||||
export const useGatewaySync = () => {
|
||||
const { gatewaysSynced, syncGateways } = useHooks();
|
||||
return { gatewaysSynced, syncGateways };
|
||||
};
|
||||
|
||||
export const useGatewayRefresh = () => {
|
||||
const { gatewaysRefreshed, refreshGateways } = useHooks();
|
||||
return { gatewaysRefreshed, refreshGateways };
|
||||
};
|
||||
|
|
|
@ -35,6 +35,8 @@ const defaultPersistent = Object.freeze( {
|
|||
isCasualSeller: null, // null value will uncheck both options in the UI.
|
||||
areOptionalPaymentMethodsEnabled: null,
|
||||
products: [],
|
||||
gatewaysSynced: false,
|
||||
gatewaysRefreshed: false,
|
||||
} );
|
||||
|
||||
// Reducer logic.
|
||||
|
@ -77,6 +79,14 @@ const onboardingReducer = createReducer( defaultTransient, defaultPersistent, {
|
|||
|
||||
return newState;
|
||||
},
|
||||
|
||||
[ ACTION_TYPES.SYNC_GATEWAYS ]: ( state ) => {
|
||||
return changePersistent( state, { gatewaysSynced: true } );
|
||||
},
|
||||
|
||||
[ ACTION_TYPES.REFRESH_GATEWAYS ]: ( state ) => {
|
||||
return changePersistent( state, { gatewaysRefreshed: true } );
|
||||
},
|
||||
} );
|
||||
|
||||
export default onboardingReducer;
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import { useState, useEffect, useCallback } from '@wordpress/element';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { STORE_NAME as PAYMENT_STORE_NAME } from '../data/payment';
|
||||
import { OnboardingHooks } from '../data';
|
||||
import { STORE_NAME as ONBOARDING_STORE_NAME } from '../data/onboarding';
|
||||
|
||||
/**
|
||||
* Custom hook for refreshing payment gateway data.
|
||||
*
|
||||
* @return {Object} Refresh API including the refresh function and state
|
||||
*/
|
||||
export const usePaymentGatewayRefresh = () => {
|
||||
const paymentDispatch = useDispatch( PAYMENT_STORE_NAME );
|
||||
const onboardingDispatch = useDispatch( ONBOARDING_STORE_NAME );
|
||||
const { gatewaysRefreshed } = OnboardingHooks.useGatewayRefresh();
|
||||
const { gatewaysSynced } = OnboardingHooks.useGatewaySync();
|
||||
const { refreshGateways } = onboardingDispatch;
|
||||
const { hydrate, refresh, reset } = paymentDispatch;
|
||||
|
||||
const [ refreshCompleted, setRefreshCompleted ] = useState( false );
|
||||
const [ isRefreshing, setIsRefreshing ] = useState( false );
|
||||
const [ refreshError, setRefreshError ] = useState( null );
|
||||
// Only refresh if gateways are synced.
|
||||
const isReadyToRefresh = gatewaysSynced;
|
||||
|
||||
/**
|
||||
* Refreshes payment gateway data
|
||||
*
|
||||
* @return {Promise<boolean|Object>} True when completed successfully, or object with error details
|
||||
*/
|
||||
const refreshPaymentGateways = useCallback( async () => {
|
||||
if ( isRefreshing ) {
|
||||
return {
|
||||
success: false,
|
||||
skipped: true,
|
||||
reason: 'already-refreshing',
|
||||
};
|
||||
}
|
||||
|
||||
if ( ! isReadyToRefresh ) {
|
||||
return {
|
||||
success: false,
|
||||
skipped: true,
|
||||
reason: 'not-ready',
|
||||
};
|
||||
}
|
||||
|
||||
setIsRefreshing( true );
|
||||
setRefreshError( null );
|
||||
|
||||
try {
|
||||
// Reset payment store if available.
|
||||
if ( typeof reset === 'function' ) {
|
||||
await reset();
|
||||
}
|
||||
|
||||
// Fetch payment data.
|
||||
const response = await apiFetch( {
|
||||
path: `/wc/v3/wc_paypal/payment`,
|
||||
method: 'GET',
|
||||
} );
|
||||
|
||||
// Update store with data.
|
||||
hydrate( response );
|
||||
|
||||
// Refresh payment store if available.
|
||||
if ( typeof refresh === 'function' ) {
|
||||
await refresh();
|
||||
}
|
||||
|
||||
// Update Redux state to mark gateways as refreshed.
|
||||
const result = await refreshGateways();
|
||||
|
||||
setRefreshCompleted( true );
|
||||
return { success: true };
|
||||
} catch ( error ) {
|
||||
setRefreshError( error );
|
||||
return { success: false, error };
|
||||
} finally {
|
||||
setIsRefreshing( false );
|
||||
}
|
||||
}, [
|
||||
isRefreshing,
|
||||
isReadyToRefresh,
|
||||
reset,
|
||||
hydrate,
|
||||
refresh,
|
||||
refreshGateways,
|
||||
] );
|
||||
|
||||
// Auto-trigger refresh when conditions are met.
|
||||
useEffect( () => {
|
||||
if (
|
||||
isReadyToRefresh &&
|
||||
! gatewaysRefreshed &&
|
||||
! isRefreshing &&
|
||||
! refreshCompleted
|
||||
) {
|
||||
refreshPaymentGateways().catch( () => {
|
||||
// Silent catch to prevent unhandled promise rejections.
|
||||
} );
|
||||
}
|
||||
}, [
|
||||
isReadyToRefresh,
|
||||
gatewaysRefreshed,
|
||||
isRefreshing,
|
||||
refreshCompleted,
|
||||
refreshPaymentGateways,
|
||||
] );
|
||||
|
||||
return {
|
||||
refreshPaymentGateways,
|
||||
refreshCompleted,
|
||||
isRefreshing,
|
||||
refreshError,
|
||||
gatewaysRefreshed: gatewaysRefreshed || refreshCompleted,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePaymentGatewayRefresh;
|
|
@ -0,0 +1,89 @@
|
|||
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { OnboardingHooks, CommonHooks } from '../data';
|
||||
import { STORE_NAME as ONBOARDING_STORE_NAME } from '../data/onboarding';
|
||||
|
||||
/**
|
||||
* Custom hook for handling gateway synchronization
|
||||
*
|
||||
* @return {boolean} Whether gateway sync is completed
|
||||
*/
|
||||
export const usePaymentGatewaySync = () => {
|
||||
const { gatewaysSynced } = OnboardingHooks.useGatewaySync();
|
||||
|
||||
const onboardingDispatch = useDispatch( ONBOARDING_STORE_NAME );
|
||||
const { syncGateways } = onboardingDispatch;
|
||||
|
||||
const { isReady: onboardingIsReady, completed: onboardingCompleted } =
|
||||
OnboardingHooks.useSteps();
|
||||
const { isReady: merchantIsReady } = CommonHooks.useStore();
|
||||
|
||||
const [ isSyncing, setIsSyncing ] = useState( false );
|
||||
const [ syncCompleted, setSyncCompleted ] = useState( false );
|
||||
const [ syncError, setSyncError ] = useState( null );
|
||||
|
||||
// Use a ref to track if we've initiated a sync during this session.
|
||||
const syncAttemptedRef = useRef( false );
|
||||
|
||||
/**
|
||||
* Handles the gateway synchronization
|
||||
*
|
||||
* @return {Promise<Object>} Result of the sync operation
|
||||
*/
|
||||
const handleSync = useCallback( async () => {
|
||||
if ( isSyncing ) {
|
||||
return { success: false, skipped: true };
|
||||
}
|
||||
|
||||
setIsSyncing( true );
|
||||
setSyncError( null );
|
||||
|
||||
try {
|
||||
const result = await syncGateways();
|
||||
|
||||
if ( result.success ) {
|
||||
// Add a small delay to ensure UI updates properly.
|
||||
await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) );
|
||||
setSyncCompleted( true );
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
throw new Error( result.message || 'Failed to sync gateways' );
|
||||
} catch ( error ) {
|
||||
setSyncError( error );
|
||||
// After an error, allow retry after 5 seconds.
|
||||
setTimeout( () => {
|
||||
syncAttemptedRef.current = false;
|
||||
}, 5000 );
|
||||
|
||||
return { success: false, error };
|
||||
} finally {
|
||||
setIsSyncing( false );
|
||||
}
|
||||
}, [ isSyncing, syncGateways ] );
|
||||
|
||||
// Automatically sync when conditions are met.
|
||||
useEffect( () => {
|
||||
// Skip if required conditions aren't met.
|
||||
if ( ! onboardingIsReady || ! merchantIsReady || gatewaysSynced ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only attempt sync if not already syncing and no previous attempt.
|
||||
if ( ! isSyncing && ! syncAttemptedRef.current ) {
|
||||
syncAttemptedRef.current = true;
|
||||
handleSync();
|
||||
}
|
||||
}, [
|
||||
onboardingIsReady,
|
||||
merchantIsReady,
|
||||
onboardingCompleted,
|
||||
gatewaysSynced,
|
||||
isSyncing,
|
||||
handleSync,
|
||||
] );
|
||||
|
||||
return gatewaysSynced;
|
||||
};
|
||||
|
||||
export default usePaymentGatewaySync;
|
|
@ -74,7 +74,7 @@ class OnboardingProfile extends AbstractDataModel {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_defaults() : array {
|
||||
protected function get_defaults(): array {
|
||||
return array(
|
||||
'completed' => false,
|
||||
'step' => 0,
|
||||
|
@ -82,6 +82,8 @@ class OnboardingProfile extends AbstractDataModel {
|
|||
'accept_card_payments' => null,
|
||||
'products' => array(),
|
||||
'setup_done' => false,
|
||||
'gateways_synced' => false,
|
||||
'gateways_refreshed' => false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -203,4 +205,45 @@ class OnboardingProfile extends AbstractDataModel {
|
|||
public function set_setup_done( bool $done ) : void {
|
||||
$this->data['setup_done'] = $done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether gateways have been synced.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_gateways_synced(): bool {
|
||||
return $this->data['gateways_synced'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether gateways have been synced.
|
||||
*
|
||||
* @param bool $synced Whether gateways have been synced.
|
||||
*/
|
||||
public function set_gateways_synced( bool $synced ): void {
|
||||
$this->data['gateways_synced'] = $synced;
|
||||
|
||||
// If enabling the flag, trigger the action.
|
||||
if ( $synced ) {
|
||||
do_action( 'woocommerce_paypal_payments_sync_gateways' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether gateways have been refreshed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_gateways_refreshed(): bool {
|
||||
return $this->data['gateways_refreshed'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether gateways have been refreshed.
|
||||
*
|
||||
* @param bool $refreshed Whether gateways have been refreshed.
|
||||
*/
|
||||
public function set_gateways_refreshed( bool $refreshed ): void {
|
||||
$this->data['gateways_refreshed'] = $refreshed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,14 @@ class OnboardingRestEndpoint extends RestEndpoint {
|
|||
'products' => array(
|
||||
'js_name' => 'products',
|
||||
),
|
||||
'gateways_synced' => array(
|
||||
'js_name' => 'gatewaysSynced',
|
||||
'sanitize' => 'to_boolean',
|
||||
),
|
||||
'gateways_refreshed' => array(
|
||||
'js_name' => 'gatewaysRefreshed',
|
||||
'sanitize' => 'to_boolean',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -193,9 +193,6 @@ class SettingsDataManager {
|
|||
* @return void
|
||||
*/
|
||||
protected function apply_configuration( ConfigurationFlagsDTO $flags ) : void {
|
||||
// Apply defaults for the "Payment Methods" tab.
|
||||
$this->toggle_payment_gateways( $flags );
|
||||
|
||||
// Apply defaults for the "Settings" tab.
|
||||
$this->apply_payment_settings( $flags );
|
||||
|
||||
|
@ -206,6 +203,23 @@ class SettingsDataManager {
|
|||
$this->apply_pay_later_messaging( $flags );
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize gateway settings with merchant onboarding choices.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sync_gateway_settings() : void {
|
||||
$flags = new ConfigurationFlagsDTO();
|
||||
|
||||
$profile_data = $this->onboarding_profile->to_array();
|
||||
|
||||
$flags->is_business_seller = ! ( $profile_data['is_casual_seller'] ?? false );
|
||||
$flags->use_card_payments = $profile_data['accept_card_payments'] ?? false;
|
||||
$flags->use_subscriptions = in_array( 'SUBSCRIPTIONS', $profile_data['products'] ?? array(), true );
|
||||
|
||||
$this->toggle_payment_gateways( $flags );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables payment gateways depending on the provided
|
||||
* configuration flags.
|
||||
|
@ -250,6 +264,14 @@ class SettingsDataManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow plugins to modify payment gateway states before saving.
|
||||
*
|
||||
* @param PaymentSettings $payment_methods The payment methods object.
|
||||
* @param ConfigurationFlagsDTO $flags Configuration flags that determine which gateways to enable.
|
||||
*/
|
||||
do_action( 'woocommerce_paypal_payments_toggle_payment_gateways', $this->payment_methods, $flags );
|
||||
|
||||
$this->payment_methods->save();
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,8 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
|
||||
$onboarding_profile->set_completed( false );
|
||||
$onboarding_profile->set_step( 0 );
|
||||
$onboarding_profile->set_gateways_synced( false );
|
||||
$onboarding_profile->set_gateways_refreshed( false );
|
||||
$onboarding_profile->save();
|
||||
|
||||
// Reset dismissed and completed on click todos.
|
||||
|
@ -606,19 +608,28 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
|
||||
// Enable Fastlane after onboarding if the store is compatible.
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_apply_default_configuration',
|
||||
static function () use ( $container ) {
|
||||
$compatibility_checker = $container->get( 'axo.helpers.compatibility-checker' );
|
||||
assert( $compatibility_checker instanceof CompatibilityChecker );
|
||||
'woocommerce_paypal_payments_toggle_payment_gateways',
|
||||
function( PaymentSettings $payment_methods, ConfigurationFlagsDTO $flags ) use ( $container ) {
|
||||
if ( $flags->is_business_seller && $flags->use_card_payments ) {
|
||||
$compatibility_checker = $container->get( 'axo.helpers.compatibility-checker' );
|
||||
assert( $compatibility_checker instanceof CompatibilityChecker );
|
||||
|
||||
$payment_settings = $container->get( 'settings.data.payment' );
|
||||
assert( $payment_settings instanceof PaymentSettings );
|
||||
|
||||
if ( $compatibility_checker->is_fastlane_compatible() ) {
|
||||
$payment_settings->toggle_method_state( AxoGateway::ID, true );
|
||||
if ( $compatibility_checker->is_fastlane_compatible() ) {
|
||||
$payment_methods->toggle_method_state( AxoGateway::ID, true );
|
||||
}
|
||||
}
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
$payment_settings->save();
|
||||
// Toggle payment gateways after onboarding based on flags.
|
||||
add_action(
|
||||
'woocommerce_paypal_payments_sync_gateways',
|
||||
static function() use ( $container ) {
|
||||
$settings_data_manager = $container->get( 'settings.service.data-manager' );
|
||||
assert( $settings_data_manager instanceof SettingsDataManager );
|
||||
$settings_data_manager->sync_gateway_settings();
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -190,7 +190,12 @@ class CaptureCardPayment {
|
|||
throw new RuntimeException( $response->get_error_message() );
|
||||
}
|
||||
|
||||
return json_decode( $response['body'] );
|
||||
$decoded_response = json_decode( $response['body'] );
|
||||
if ( ! isset( $decoded_response->invoice_id ) ) {
|
||||
$decoded_response->invoice_id = $invoice_id;
|
||||
}
|
||||
|
||||
return $decoded_response;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -285,7 +285,18 @@ class CardButtonGateway extends \WC_Payment_Gateway {
|
|||
|
||||
try {
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
|
|
|
@ -518,7 +518,18 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC {
|
|||
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
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.
|
||||
* List of payment sources for which we are expected to store the payer email in the WC Order metadata.
|
||||
*/
|
||||
const PAYMENT_SOURCES_WITH_PAYER_EMAIL = array( 'paypal', 'paylater', 'venmo' );
|
||||
|
||||
|
@ -614,30 +614,19 @@ class PayPalGateway extends \WC_Payment_Gateway {
|
|||
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
try {
|
||||
$paypal_subscription_id = WC()->session->get( 'ppcp_subscription_id' ) ?? '';
|
||||
if ( $paypal_subscription_id ) {
|
||||
$order = $this->session_handler->order();
|
||||
$this->add_paypal_meta( $wc_order, $order, $this->environment );
|
||||
|
||||
$subscriptions = function_exists( 'wcs_get_subscriptions_for_order' ) ? wcs_get_subscriptions_for_order( $order_id ) : array();
|
||||
foreach ( $subscriptions as $subscription ) {
|
||||
$subscription->update_meta_data( 'ppcp_subscription', $paypal_subscription_id );
|
||||
$subscription->save();
|
||||
|
||||
$subscription->add_order_note( "PayPal subscription {$paypal_subscription_id} added." );
|
||||
}
|
||||
|
||||
$transaction_id = $this->get_paypal_order_transaction_id( $order );
|
||||
if ( $transaction_id ) {
|
||||
$this->update_transaction_id( $transaction_id, $wc_order );
|
||||
}
|
||||
|
||||
$wc_order->payment_complete();
|
||||
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
|
|
|
@ -376,7 +376,7 @@ class CardPaymentsConfiguration {
|
|||
*
|
||||
* Note: This setting is planned but not implemented yet.
|
||||
*
|
||||
* @retun bool True means, the default watermark is displayed to customers.
|
||||
* @return bool True means, the default watermark is displayed to customers.
|
||||
*/
|
||||
public function show_fastlane_watermark() : bool {
|
||||
$this->ensure_resolved_values();
|
||||
|
|
|
@ -28,7 +28,7 @@ trait OrderMetaTrait {
|
|||
* @param Environment $environment The environment.
|
||||
* @param OrderTransient|null $order_transient The order transient helper.
|
||||
*/
|
||||
protected function add_paypal_meta(
|
||||
public function add_paypal_meta(
|
||||
WC_Order $wc_order,
|
||||
Order $order,
|
||||
Environment $environment,
|
||||
|
|
|
@ -403,7 +403,7 @@ class OrderProcessor {
|
|||
$this->threed_secure->proceed_with_order( $order ),
|
||||
array(
|
||||
ThreeDSecure::NO_DECISION,
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
),
|
||||
true
|
||||
);
|
||||
|
|
|
@ -28,7 +28,7 @@ trait TransactionIdHandlingTrait {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function update_transaction_id(
|
||||
public function update_transaction_id(
|
||||
string $transaction_id,
|
||||
WC_Order $wc_order,
|
||||
LoggerInterface $logger = null
|
||||
|
@ -67,7 +67,7 @@ trait TransactionIdHandlingTrait {
|
|||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get_paypal_order_transaction_id( Order $order ): ?string {
|
||||
public function get_paypal_order_transaction_id( Order $order ): ?string {
|
||||
$purchase_unit = $order->purchase_units()[0] ?? null;
|
||||
if ( ! $purchase_unit ) {
|
||||
return null;
|
||||
|
|
|
@ -37,6 +37,19 @@ class WcSubscriptionsModule implements ServiceModule, ExtendingModule, Executabl
|
|||
use ModuleClassNameIdTrait;
|
||||
use TransactionIdHandlingTrait;
|
||||
|
||||
private const VAULT_SUPPORTS_SUBSCRIPTIONS = array(
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -56,6 +69,7 @@ class WcSubscriptionsModule implements ServiceModule, ExtendingModule, Executabl
|
|||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
$this->add_gateways_support( $c );
|
||||
|
||||
add_action(
|
||||
'woocommerce_scheduled_subscription_payment_' . PayPalGateway::ID,
|
||||
/**
|
||||
|
@ -236,31 +250,6 @@ class WcSubscriptionsModule implements ServiceModule, ExtendingModule, Executabl
|
|||
}
|
||||
);
|
||||
|
||||
// Remove `gateway_scheduled_payments` feature support for non PayPal Subscriptions at subscription level.
|
||||
add_filter(
|
||||
'woocommerce_subscription_payment_gateway_supports',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function( $is_supported, $feature, $subscription ) {
|
||||
if (
|
||||
$subscription->get_payment_method() === PayPalGateway::ID
|
||||
&& $feature === 'gateway_scheduled_payments'
|
||||
) {
|
||||
$subscription_connected = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
|
||||
if ( ! $subscription_connected ) {
|
||||
$is_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $is_supported;
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -406,94 +395,64 @@ class WcSubscriptionsModule implements ServiceModule, ExtendingModule, Executabl
|
|||
* @return void
|
||||
*/
|
||||
private function add_gateways_support( ContainerInterface $c ): void {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
if ( ! $subscriptions_helper->plugin_is_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_paypal_gateway_supports',
|
||||
function ( array $supports ) use ( $c ): array {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
|
||||
if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) {
|
||||
$supports = array(
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
'gateway_scheduled_payments',
|
||||
);
|
||||
if ( 'disable_paypal_subscriptions' === $subscriptions_mode ) {
|
||||
return $supports;
|
||||
}
|
||||
|
||||
return $supports;
|
||||
return array_merge(
|
||||
$supports,
|
||||
self::VAULT_SUPPORTS_SUBSCRIPTIONS
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_credit_card_gateway_supports',
|
||||
function ( array $supports ) use ( $c ): array {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$vaulting_enabled = $settings->has( 'vault_enabled_dcc' ) && $settings->get( 'vault_enabled_dcc' );
|
||||
|
||||
if ( $vaulting_enabled && $subscriptions_helper->plugin_is_active() ) {
|
||||
$supports = array(
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
);
|
||||
$subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
if ( 'disable_paypal_subscriptions' === $subscriptions_mode ) {
|
||||
return $supports;
|
||||
}
|
||||
|
||||
return $supports;
|
||||
$vaulting_enabled = $settings->has( 'vault_enabled_dcc' ) && $settings->get( 'vault_enabled_dcc' );
|
||||
if ( ! $vaulting_enabled ) {
|
||||
return $supports;
|
||||
}
|
||||
return array_merge(
|
||||
$supports,
|
||||
self::VAULT_SUPPORTS_SUBSCRIPTIONS
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'woocommerce_paypal_payments_card_button_gateway_supports',
|
||||
function ( array $supports ) use ( $c ): array {
|
||||
$subscriptions_helper = $c->get( 'wc-subscriptions.helper' );
|
||||
assert( $subscriptions_helper instanceof SubscriptionHelper );
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
$subscriptions_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : '';
|
||||
|
||||
if ( 'disable_paypal_subscriptions' !== $subscriptions_mode && $subscriptions_helper->plugin_is_active() ) {
|
||||
$supports = array(
|
||||
'subscriptions',
|
||||
'subscription_cancellation',
|
||||
'subscription_suspension',
|
||||
'subscription_reactivation',
|
||||
'subscription_amount_changes',
|
||||
'subscription_date_changes',
|
||||
'subscription_payment_method_change',
|
||||
'subscription_payment_method_change_customer',
|
||||
'subscription_payment_method_change_admin',
|
||||
'multiple_subscriptions',
|
||||
);
|
||||
if ( 'disable_paypal_subscriptions' === $subscriptions_mode ) {
|
||||
return $supports;
|
||||
}
|
||||
|
||||
return $supports;
|
||||
return array_merge(
|
||||
$supports,
|
||||
self::VAULT_SUPPORTS_SUBSCRIPTIONS
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -230,7 +230,18 @@ class CheckoutOrderApproved implements RequestHandler {
|
|||
}
|
||||
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
/**
|
||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||
* So you can implement your own for example on subscriptions
|
||||
*
|
||||
* - true bool controls execution of 'OrderProcessor::process()'
|
||||
* - $this \WC_Payment_Gateway
|
||||
* - $wc_order \WC_Order
|
||||
*/
|
||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||
if ( $process ) {
|
||||
$this->order_processor->process( $wc_order );
|
||||
}
|
||||
} catch ( RuntimeException $exception ) {
|
||||
return $this->failure_response(
|
||||
sprintf(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Handels the Webhook PAYMENT.CAPTURE.REFUNDED
|
||||
* Handles the Webhook PAYMENT.CAPTURE.REFUNDED
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
=== WooCommerce PayPal Payments ===
|
||||
Contributors: woocommerce, automattic, syde
|
||||
Contributors: paypal, woocommerce, automattic, syde
|
||||
Tags: woocommerce, paypal, payments, ecommerce, credit card
|
||||
Requires at least: 6.5
|
||||
Tested up to: 6.7
|
||||
|
@ -315,7 +315,7 @@ If you encounter issues with the PayPal buttons not appearing after an update, p
|
|||
* Fix - Shipping methods during callback not updated correctly #2421
|
||||
* Fix - Preserve subscription renewal processing when switching Subscriptions Mode or disabling gateway #2394
|
||||
* Fix - Remove shipping callback for Venmo express button #2374
|
||||
* Fix - Google Pay: Fix issuse with data.paymentSource being undefined #2390
|
||||
* Fix - Google Pay: Fix issue with data.paymentSource being undefined #2390
|
||||
* Fix - Loading of non-Order as a WC_Order causes warnings and potential data corruption #2343
|
||||
* Fix - Apple Pay and Google Pay buttons don't appear in PayPal Button stack on multi-step Checkout #2372
|
||||
* Fix - Apple Pay: Fix when shipping is disabled #2391
|
||||
|
|
|
@ -97,39 +97,79 @@ class PartnerReferralsDataTest extends TestCase {
|
|||
/**
|
||||
* Data provider for testing flag combinations.
|
||||
*
|
||||
* @return array[] Test cases with [has_subscriptions, has_cards, expected_changes]
|
||||
* @return array[] Test cases with [has_subscriptions, has_cards, is_acdc_eligible, expected_changes]
|
||||
*/
|
||||
public function flagCombinationsProvider() : array {
|
||||
return [
|
||||
'with subscriptions and cards' => [
|
||||
true, // With subscription?
|
||||
true, // With cards?
|
||||
'with subscriptions and cards, ACDC eligible' => [
|
||||
true, // With subscription?
|
||||
true, // With cards?
|
||||
true, // ACDC eligible?
|
||||
[
|
||||
'capabilities' => [ 'PAYPAL_WALLET_VAULTING_ADVANCED' ],
|
||||
'show_add_credit_card' => true,
|
||||
'has_vault_features' => true,
|
||||
],
|
||||
],
|
||||
'with subscriptions, no cards' => [
|
||||
'with subscriptions, no cards, ACDC eligible' => [
|
||||
true, // With subscription?
|
||||
false, // With cards?
|
||||
true, // ACDC eligible?
|
||||
[
|
||||
'capabilities' => [ 'PAYPAL_WALLET_VAULTING_ADVANCED' ],
|
||||
'show_add_credit_card' => false,
|
||||
'has_vault_features' => true,
|
||||
],
|
||||
],
|
||||
'no subscriptions, with cards' => [
|
||||
'no subscriptions, with cards, ACDC eligible' => [
|
||||
false, // With subscription?
|
||||
true, // With cards?
|
||||
true, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => true,
|
||||
'has_vault_features' => false,
|
||||
],
|
||||
],
|
||||
'no subscriptions, no cards' => [
|
||||
'no subscriptions, no cards, ACDC eligible' => [
|
||||
false, // With subscription?
|
||||
false, // With cards?
|
||||
true, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => false,
|
||||
'has_vault_features' => false,
|
||||
],
|
||||
],
|
||||
'with subscriptions and cards, ACDC is not eligible' => [
|
||||
true, // With subscription?
|
||||
true, // With cards?
|
||||
false, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => true,
|
||||
'has_vault_features' => true,
|
||||
],
|
||||
],
|
||||
'with subscriptions, no cards, ACDC is not eligible' => [
|
||||
true, // With subscription?
|
||||
false, // With cards?
|
||||
false, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => false,
|
||||
'has_vault_features' => true,
|
||||
],
|
||||
],
|
||||
'no subscriptions, with cards, ACDC is not eligible' => [
|
||||
false, // With subscription?
|
||||
true, // With cards?
|
||||
false, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => true,
|
||||
'has_vault_features' => false,
|
||||
],
|
||||
],
|
||||
'no subscriptions, no cards, ACDC is not eligible' => [
|
||||
false, // With subscription?
|
||||
false, // With cards?
|
||||
false, // ACDC eligible?
|
||||
[
|
||||
'show_add_credit_card' => false,
|
||||
'has_vault_features' => false,
|
||||
|
@ -189,7 +229,9 @@ class PartnerReferralsDataTest extends TestCase {
|
|||
*
|
||||
* @dataProvider flagCombinationsProvider
|
||||
*/
|
||||
public function testDataStructureWithFlags( bool $has_subscriptions, bool $has_cards, array $expected_changes ) : void {
|
||||
public function testDataStructureWithFlags( bool $has_subscriptions, bool $has_cards, bool $is_acdc_eligible, array $expected_changes ) : void {
|
||||
$this->dccApplies->shouldReceive('for_country_currency')->andReturn($is_acdc_eligible);
|
||||
|
||||
$result = $this->testee->data( [ 'PPCP' ], self::TOKEN, $has_subscriptions, $has_cards );
|
||||
$expected = $this->getBaseExpectedArray();
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class ThreeDSecureTest extends TestCase
|
|||
{
|
||||
$matrix = [
|
||||
'test_1' => [
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
CardAuthenticationResult::LIABILITY_SHIFT_POSSIBLE,
|
||||
CardAuthenticationResult::AUTHENTICATION_RESULT_YES,
|
||||
CardAuthenticationResult::ENROLLMENT_STATUS_YES,
|
||||
|
@ -75,7 +75,7 @@ class ThreeDSecureTest extends TestCase
|
|||
CardAuthenticationResult::ENROLLMENT_STATUS_YES,
|
||||
],
|
||||
'test_4' => [
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
CardAuthenticationResult::LIABILITY_SHIFT_POSSIBLE,
|
||||
CardAuthenticationResult::AUTHENTICATION_RESULT_ATTEMPTED,
|
||||
CardAuthenticationResult::ENROLLMENT_STATUS_YES,
|
||||
|
@ -105,13 +105,13 @@ class ThreeDSecureTest extends TestCase
|
|||
CardAuthenticationResult::ENROLLMENT_STATUS_YES,
|
||||
],
|
||||
'test_9' => [
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
CardAuthenticationResult::LIABILITY_SHIFT_NO,
|
||||
'',
|
||||
CardAuthenticationResult::ENROLLMENT_STATUS_NO,
|
||||
],
|
||||
'test_10' => [
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
CardAuthenticationResult::LIABILITY_SHIFT_NO,
|
||||
'',
|
||||
CardAuthenticationResult::ENROLLMENT_STATUS_UNAVAILABLE,
|
||||
|
@ -123,7 +123,7 @@ class ThreeDSecureTest extends TestCase
|
|||
CardAuthenticationResult::ENROLLMENT_STATUS_UNAVAILABLE,
|
||||
],
|
||||
'test_12' => [
|
||||
ThreeDSecure::PROCCEED,
|
||||
ThreeDSecure::PROCEED,
|
||||
CardAuthenticationResult::LIABILITY_SHIFT_NO,
|
||||
'',
|
||||
CardAuthenticationResult::ENROLLMENT_STATUS_BYPASS,
|
||||
|
|
|
@ -142,7 +142,7 @@ define( 'PPCP_PAYPAL_BN_CODE', 'Woo_PPCP' );
|
|||
* Add "Settings" link to Plugins screen.
|
||||
*
|
||||
* @param array $links
|
||||
* @retun array
|
||||
* @return array
|
||||
*/
|
||||
function( $links ) {
|
||||
if ( ! is_woocommerce_activated() ) {
|
||||
|
@ -169,7 +169,7 @@ define( 'PPCP_PAYPAL_BN_CODE', 'Woo_PPCP' );
|
|||
*
|
||||
* @param array $links
|
||||
* @param string $file
|
||||
* @retun array
|
||||
* @return array
|
||||
*/
|
||||
function( $links, $file ) {
|
||||
if ( plugin_basename( __FILE__ ) !== $file ) {
|
||||
|
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 96 KiB |
BIN
wordpress_org_assets/screenshot-7.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
wordpress_org_assets/screenshot-8.png
Normal file
After Width: | Height: | Size: 95 KiB |