Add subscription support for variable subscriptions

This commit is contained in:
Emili Castells Guasch 2023-07-17 15:13:25 +02:00
parent 92a59346a7
commit b066fc7ccf
2 changed files with 137 additions and 110 deletions

View file

@ -1,64 +1,73 @@
document.addEventListener( document.addEventListener(
'DOMContentLoaded', 'DOMContentLoaded',
() => { () => {
if(PayPalCommerceGatewayPayPalSubscription.product_connected === 'yes') { const setupProducts = () => {
const periodInterval = document.querySelector('#_subscription_period_interval'); PayPalCommerceGatewayPayPalSubscriptionProducts?.forEach((product) => {
periodInterval.setAttribute('disabled', 'disabled'); if(product.product_connected === 'yes') {
const periodInterval = document.querySelector('#_subscription_period_interval');
periodInterval.setAttribute('disabled', 'disabled');
const subscriptionPeriod = document.querySelector('#_subscription_period'); const subscriptionPeriod = document.querySelector('#_subscription_period');
subscriptionPeriod.setAttribute('disabled', 'disabled'); subscriptionPeriod.setAttribute('disabled', 'disabled');
const subscriptionLength = document.querySelector('._subscription_length_field'); const subscriptionLength = document.querySelector('._subscription_length_field');
subscriptionLength.style.display = 'none'; subscriptionLength.style.display = 'none';
const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); const subscriptionTrial = document.querySelector('._subscription_trial_length_field');
subscriptionTrial.style.display = 'none'; subscriptionTrial.style.display = 'none';
}
const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan');
unlinkBtn?.addEventListener('click', (event)=>{
event.preventDefault();
unlinkBtn.disabled = true;
const spinner = document.getElementById('spinner-unlink-plan');
spinner.style.display = 'inline-block';
fetch(PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin',
body: JSON.stringify({
nonce: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.nonce,
plan_id: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.plan_id,
product_id: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.product_id
})
}).then(function (res) {
return res.json();
}).then(function (data) {
if (!data.success) {
unlinkBtn.disabled = false;
spinner.style.display = 'none';
console.error(data);
throw Error(data.data.message);
} }
const enableSubscription = document.getElementById('ppcp-enable-subscription'); const unlinkBtn = document.getElementById(`ppcp-unlink-sub-plan-${product.ajax.deactivate_plan.product_id}`);
const product = document.getElementById('pcpp-product'); unlinkBtn?.addEventListener('click', (event)=>{
const plan = document.getElementById('pcpp-plan'); event.preventDefault();
enableSubscription.style.display = 'none'; unlinkBtn.disabled = true;
product.style.display = 'none'; const spinner = document.getElementById('spinner-unlink-plan');
plan.style.display = 'none'; spinner.style.display = 'inline-block';
const enable_subscription_product = document.getElementById('ppcp_enable_subscription_product'); fetch(product.ajax.deactivate_plan.endpoint, {
enable_subscription_product.disabled = true; method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'same-origin',
body: JSON.stringify({
nonce: product.ajax.deactivate_plan.nonce,
plan_id: product.ajax.deactivate_plan.plan_id,
product_id: product.ajax.deactivate_plan.product_id
})
}).then(function (res) {
return res.json();
}).then(function (data) {
if (!data.success) {
unlinkBtn.disabled = false;
spinner.style.display = 'none';
console.error(data);
throw Error(data.data.message);
}
const planUnlinked = document.getElementById('pcpp-plan-unlinked'); const enableSubscription = document.getElementById('ppcp-enable-subscription');
planUnlinked.style.display = 'block'; const product = document.getElementById('pcpp-product');
const plan = document.getElementById('pcpp-plan');
enableSubscription.style.display = 'none';
product.style.display = 'none';
plan.style.display = 'none';
setTimeout(() => { const enable_subscription_product = document.getElementById('ppcp_enable_subscription_product');
location.reload(); enable_subscription_product.disabled = true;
}, 1000)
}); const planUnlinked = document.getElementById('pcpp-plan-unlinked');
planUnlinked.style.display = 'block';
setTimeout(() => {
location.reload();
}, 1000)
});
});
})
}
setupProducts();
jQuery( '#woocommerce-product-data' ).on('woocommerce_variations_loaded', () => {
setupProducts();
}); });
}); });

View file

@ -180,7 +180,7 @@ class SubscriptionModule implements ModuleInterface {
//phpcs:disable WordPress.Security.NonceVerification.Recommended //phpcs:disable WordPress.Security.NonceVerification.Recommended
$post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) ); $post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) );
$product = wc_get_product( $post_id ); $product = wc_get_product( $post_id );
if ( ! ( is_a( $product, WC_Product::class) || is_a( $product, WC_Product_Subscription_Variation::class) ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) { if ( ! ( is_a( $product, WC_Product::class ) || is_a( $product, WC_Product_Subscription_Variation::class ) ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) {
return; return;
} }
@ -193,22 +193,20 @@ class SubscriptionModule implements ModuleInterface {
true true
); );
$plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array(); $products = array( $this->set_product_config( $product ) );
$plan_id = $plan['id'] ?? ''; if ( $product->get_type() === 'variable-subscription' ) {
$products = array();
$available_variations = $product->get_available_variations();
foreach ( $available_variations as $variation ) {
$variation = wc_get_product_object( 'variation', $variation['variation_id'] );
$products[] = $this->set_product_config( $variation );
}
}
wp_localize_script( wp_localize_script(
'ppcp-paypal-subscription', 'ppcp-paypal-subscription',
'PayPalCommerceGatewayPayPalSubscription', 'PayPalCommerceGatewayPayPalSubscriptionProducts',
array( $products
'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '',
'ajax' => array(
'deactivate_plan' => array(
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
'plan_id' => $plan_id,
'product_id' => $product->get_id(),
),
),
)
); );
} }
); );
@ -465,27 +463,31 @@ class SubscriptionModule implements ModuleInterface {
return; return;
} }
$subscriptions_api_handler = $c->get('subscription.api-handler'); $subscriptions_api_handler = $c->get( 'subscription.api-handler' );
assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler );
$this->update_subscription_product_meta($product, $subscriptions_api_handler); $this->update_subscription_product_meta( $product, $subscriptions_api_handler );
}, },
12 12
); );
add_action('woocommerce_save_product_variation', function($variation_id) use($c){ add_action(
if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) ) { 'woocommerce_save_product_variation',
return; function( $variation_id ) use ( $c ) {
} $wcsnonce_save_variations = wc_clean( wp_unslash( $_POST['_wcsnonce_save_variations'] ?? '' ) );
if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $wcsnonce_save_variations ) || ! wp_verify_nonce( $wcsnonce_save_variations, 'wcs_subscription_variations' ) ) {
return;
}
$product = wc_get_product( $variation_id ); $product = wc_get_product( $variation_id );
if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) {
return; return;
} }
$subscriptions_api_handler = $c->get('subscription.api-handler'); $subscriptions_api_handler = $c->get( 'subscription.api-handler' );
assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler );
$this->update_subscription_product_meta($product, $subscriptions_api_handler); $this->update_subscription_product_meta( $product, $subscriptions_api_handler );
}); }
);
add_action( add_action(
'woocommerce_process_shop_subscription_meta', 'woocommerce_process_shop_subscription_meta',
@ -839,16 +841,7 @@ class SubscriptionModule implements ModuleInterface {
* @return void * @return void
*/ */
private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void { private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void {
if(is_a($product, WC_Product_Variable_Subscription::class)) {
$variations = $product->get_available_variations();
foreach ($variations as $variation) {
$a = 1;
// $variation['variation_id']
}
}
$enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' );
$subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' );
echo '<p class="form-field">'; echo '<p class="form-field">';
echo sprintf( echo sprintf(
@ -868,14 +861,15 @@ class SubscriptionModule implements ModuleInterface {
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 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>'; echo '</p>';
$subscription_product = $product->get_meta( 'ppcp_subscription_product' ); $subscription_product = $product->get_meta( 'ppcp_subscription_product' );
$subscription_plan = $product->get_meta( 'ppcp_subscription_plan' ); $subscription_plan = $product->get_meta( 'ppcp_subscription_plan' );
$subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' );
if ( $subscription_product && $subscription_plan ) { if ( $subscription_product && $subscription_plan ) {
if ( $enable_subscription_product !== 'yes' ) { if ( $enable_subscription_product !== 'yes' ) {
echo sprintf( echo sprintf(
// translators: %1$s and %2$s are button and wrapper html tags. // translators: %1$s and %2$s are button and wrapper html tags.
esc_html__( '%1$sUnlink PayPal Subscription Plan%2$s', 'woocommerce-paypal-payments' ), esc_html__( '%1$sUnlink PayPal Subscription Plan%2$s', 'woocommerce-paypal-payments' ),
'<p class="form-field" id="ppcp-enable-subscription"><label></label><button class="button" id="ppcp_unlink_sub_plan">', '<p class="form-field" id="ppcp-enable-subscription"><label></label><button class="button" 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" style="float: none; display:none;"></span></p>'
); );
echo sprintf( echo sprintf(
@ -912,38 +906,62 @@ class SubscriptionModule implements ModuleInterface {
/** /**
* Updates subscription product meta. * Updates subscription product meta.
* *
* @param WC_Product $product * @param WC_Product $product The product.
* @param SubscriptionsApiHandler $subscriptions_api_handler * @param SubscriptionsApiHandler $subscriptions_api_handler The subscription api handler.
* @return void * @return void
*/ */
private function update_subscription_product_meta(WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler): void private function update_subscription_product_meta( WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler ): void {
{ // phpcs:ignore WordPress.Security.NonceVerification
$enable_subscription_product = wc_clean(wp_unslash($_POST['_ppcp_enable_subscription_product'] ?? '')); $enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) );
$product->update_meta_data('_ppcp_enable_subscription_product', $enable_subscription_product); $product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product );
$product->save(); $product->save();
if (($product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation') && $enable_subscription_product === 'yes') { if ( ( $product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation' ) && $enable_subscription_product === 'yes' ) {
if ($product->meta_exists('ppcp_subscription_product') && $product->meta_exists('ppcp_subscription_plan')) { if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
$subscriptions_api_handler->update_product($product); $subscriptions_api_handler->update_product( $product );
$subscriptions_api_handler->update_plan($product); $subscriptions_api_handler->update_plan( $product );
return; return;
} }
if (!$product->meta_exists('ppcp_subscription_product')) { if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) {
$subscriptions_api_handler->create_product($product); $subscriptions_api_handler->create_product( $product );
} }
if ($product->meta_exists('ppcp_subscription_product') && !$product->meta_exists('ppcp_subscription_plan')) { if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) {
$subscription_plan_name = wc_clean(wp_unslash($_POST['_ppcp_subscription_plan_name'] ?? '')); // phpcs:ignore WordPress.Security.NonceVerification
if (!is_string($subscription_plan_name)) { $subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) );
if ( ! is_string( $subscription_plan_name ) ) {
return; return;
} }
$product->update_meta_data('_ppcp_subscription_plan_name', $subscription_plan_name); $product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name );
$product->save(); $product->save();
$subscriptions_api_handler->create_plan($subscription_plan_name, $product); $subscriptions_api_handler->create_plan( $subscription_plan_name, $product );
} }
} }
} }
/**
* 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' ) ?? '',
'ajax' => array(
'deactivate_plan' => array(
'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ),
'plan_id' => $plan_id,
'product_id' => $product->get_id(),
),
),
);
}
} }