Initial commit.
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The PayPalSubscriptions module extensions.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
return array();
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The PayPalSubscriptions module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
return static function (): \WooCommerce\PayPalCommerce\PayPalSubscriptions\PayPalSubscriptionsModule {
|
||||
return new \WooCommerce\PayPalCommerce\PayPalSubscriptions\PayPalSubscriptionsModule();
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The PayPalSubscriptions module services.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
return array('paypal-subscriptions.deactivate-plan-endpoint' => static function (ContainerInterface $container): \WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint {
|
||||
return new \WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint($container->get('button.request-data'), $container->get('api.endpoint.billing-plans'));
|
||||
}, 'paypal-subscriptions.api-handler' => static function (ContainerInterface $container): \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler {
|
||||
return new \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler($container->get('api.endpoint.catalog-products'), $container->get('api.factory.product'), $container->get('api.endpoint.billing-plans'), $container->get('api.factory.billing-cycle'), $container->get('api.factory.payment-preferences'), $container->get('api.shop.currency.getter'), $container->get('woocommerce.logger.woocommerce'));
|
||||
}, 'paypal-subscriptions.module.url' => static function (ContainerInterface $container): string {
|
||||
return plugins_url('/modules/ppcp-paypal-subscriptions/', $container->get('ppcp.path-to-plugin-main-file'));
|
||||
}, 'paypal-subscriptions.renewal-handler' => static function (ContainerInterface $container): \WooCommerce\PayPalCommerce\PayPalSubscriptions\RenewalHandler {
|
||||
return new \WooCommerce\PayPalCommerce\PayPalSubscriptions\RenewalHandler($container->get('woocommerce.logger.woocommerce'));
|
||||
}, 'paypal-subscriptions.status' => static function (ContainerInterface $container): \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionStatus {
|
||||
return new \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionStatus($container->get('api.endpoint.billing-subscriptions'), $container->get('woocommerce.logger.woocommerce'));
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The deactivate Subscription Plan Endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use Exception;
|
||||
use WC_Product;
|
||||
use WC_Subscriptions_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
/**
|
||||
* Class DeactivatePlanEndpoint
|
||||
*/
|
||||
class DeactivatePlanEndpoint
|
||||
{
|
||||
const ENDPOINT = 'ppc-deactivate-plan';
|
||||
/**
|
||||
* The request data.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
/**
|
||||
* The billing plans.
|
||||
*
|
||||
* @var BillingPlans
|
||||
*/
|
||||
private $billing_plans;
|
||||
/**
|
||||
* DeactivatePlanEndpoint constructor.
|
||||
*
|
||||
* @param RequestData $request_data The request data.
|
||||
* @param BillingPlans $billing_plans The billing plans.
|
||||
*/
|
||||
public function __construct(RequestData $request_data, BillingPlans $billing_plans)
|
||||
{
|
||||
$this->request_data = $request_data;
|
||||
$this->billing_plans = $billing_plans;
|
||||
}
|
||||
/**
|
||||
* Handles the request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle_request(): void
|
||||
{
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_send_json_error('Not admin.', 403);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$data = $this->request_data->read_request(self::ENDPOINT);
|
||||
$plan_id = $data['plan_id'] ?? '';
|
||||
if ($plan_id) {
|
||||
$this->billing_plans->deactivate_plan($plan_id);
|
||||
}
|
||||
$product_id = $data['product_id'] ?? '';
|
||||
if ($product_id) {
|
||||
$product = wc_get_product($product_id);
|
||||
if (is_a($product, WC_Product::class) && WC_Subscriptions_Product::is_subscription($product)) {
|
||||
$product->delete_meta_data('_ppcp_enable_subscription_product');
|
||||
$product->delete_meta_data('_ppcp_subscription_plan_name');
|
||||
$product->delete_meta_data('ppcp_subscription_product');
|
||||
$product->delete_meta_data('ppcp_subscription_plan');
|
||||
$product->save();
|
||||
}
|
||||
}
|
||||
wp_send_json_success(array('product_id' => (string) $product_id));
|
||||
} catch (Exception $error) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The PayPalSubscriptions module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\PayPalSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use ActionScheduler_Store;
|
||||
use WC_Order;
|
||||
use WC_Product;
|
||||
use WC_Product_Subscription_Variation;
|
||||
use WC_Subscription;
|
||||
use WC_Subscriptions_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
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;
|
||||
/**
|
||||
* Class SavedPaymentCheckerModule
|
||||
*/
|
||||
class PayPalSubscriptionsModule implements ServiceModule, ExtendingModule, ExecutableModule
|
||||
{
|
||||
use ModuleClassNameIdTrait;
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function services(): array
|
||||
{
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function extensions(): array
|
||||
{
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
/**
|
||||
* {@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_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',
|
||||
/**
|
||||
* WC_Payment_Gateway $gateway type removed.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function (bool $process, $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',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($product_id) use ($c) {
|
||||
$settings = $c->get('wcgateway.settings');
|
||||
assert($settings instanceof Settings);
|
||||
try {
|
||||
$subscriptions_mode = $settings->get('subscriptions_mode');
|
||||
} catch (NotFoundException $exception) {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$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;
|
||||
}
|
||||
$product = wc_get_product($product_id);
|
||||
if (!is_a($product, WC_Product::class)) {
|
||||
return;
|
||||
}
|
||||
$subscriptions_api_handler = $c->get('paypal-subscriptions.api-handler');
|
||||
assert($subscriptions_api_handler instanceof \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler);
|
||||
$this->update_subscription_product_meta($product, $subscriptions_api_handler);
|
||||
},
|
||||
12
|
||||
);
|
||||
add_filter(
|
||||
'woocommerce_add_to_cart_validation',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function ($passed_validation, $product_id) use ($c) {
|
||||
if (WC()->cart->is_empty() || wcs_is_manual_renewal_enabled()) {
|
||||
return $passed_validation;
|
||||
}
|
||||
$product = wc_get_product($product_id);
|
||||
if (!is_a($product, WC_Product::class)) {
|
||||
wc_add_notice(__('Cannot add this product to cart (invalid product).', 'woocommerce-paypal-payments'), 'error');
|
||||
return \false;
|
||||
}
|
||||
$settings = $c->get('wcgateway.settings');
|
||||
assert($settings instanceof Settings);
|
||||
$subscriptions_mode = $settings->has('subscriptions_mode') ? $settings->get('subscriptions_mode') : '';
|
||||
$is_paypal_subscription = static function ($product) use ($subscriptions_mode): bool {
|
||||
return $product && in_array($product->get_type(), array('subscription', 'variable-subscription'), \true) && 'subscriptions_api' === $subscriptions_mode && $product->get_meta('_ppcp_enable_subscription_product', \true) === 'yes';
|
||||
};
|
||||
if ($is_paypal_subscription($product)) {
|
||||
if (!$product->get_sold_individually()) {
|
||||
$product->set_sold_individually(\true);
|
||||
$product->save();
|
||||
}
|
||||
wc_add_notice(__('You cannot add a PayPal Subscription product to a cart with other items.', 'woocommerce-paypal-payments'), 'error');
|
||||
return \false;
|
||||
}
|
||||
foreach (WC()->cart->get_cart() as $cart_item) {
|
||||
$cart_product = wc_get_product($cart_item['product_id']);
|
||||
if ($is_paypal_subscription($cart_product)) {
|
||||
wc_add_notice(__('You can only have one PayPal Subscription product in your cart.', 'woocommerce-paypal-payments'), 'error');
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return $passed_validation;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
add_action(
|
||||
'woocommerce_save_product_variation',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($variation_id) use ($c) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$wcsnonce_save_variations = wc_clean(wp_unslash($_POST['_wcsnonce_save_variations'] ?? ''));
|
||||
if (!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')) {
|
||||
return;
|
||||
}
|
||||
$product = wc_get_product($variation_id);
|
||||
if (!is_a($product, WC_Product_Subscription_Variation::class)) {
|
||||
return;
|
||||
}
|
||||
$subscriptions_api_handler = $c->get('paypal-subscriptions.api-handler');
|
||||
assert($subscriptions_api_handler instanceof \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler);
|
||||
$this->update_subscription_product_meta($product, $subscriptions_api_handler);
|
||||
},
|
||||
30
|
||||
);
|
||||
/**
|
||||
* Executed when updating WC Subscription.
|
||||
*/
|
||||
add_action(
|
||||
'woocommerce_process_shop_subscription_meta',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($id) use ($c) {
|
||||
$subscription = wcs_get_subscription($id);
|
||||
if ($subscription === \false) {
|
||||
return;
|
||||
}
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if (!$subscription_id) {
|
||||
return;
|
||||
}
|
||||
$subscription_status = $c->get('paypal-subscriptions.status');
|
||||
assert($subscription_status instanceof \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionStatus);
|
||||
$subscription_status->update_status($subscription->get_status(), $subscription_id);
|
||||
},
|
||||
20
|
||||
);
|
||||
/**
|
||||
* Update subscription status from WC Subscriptions list page action link.
|
||||
*/
|
||||
add_action('woocommerce_subscription_status_updated', function (WC_Subscription $subscription) use ($c) {
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if (!$subscription_id) {
|
||||
return;
|
||||
}
|
||||
$subscription_status = $c->get('paypal-subscriptions.status');
|
||||
assert($subscription_status instanceof \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionStatus);
|
||||
$subscription_status->update_status($subscription->get_status(), $subscription_id);
|
||||
});
|
||||
add_action(
|
||||
'woocommerce_subscription_before_actions',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($subscription) use ($c) {
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if ($subscription_id) {
|
||||
$environment = $c->get('settings.environment');
|
||||
$host = $environment->current_environment_is(Environment::SANDBOX) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||
?>
|
||||
<tr>
|
||||
<td><?php
|
||||
esc_html_e('PayPal Subscription', 'woocommerce-paypal-payments');
|
||||
?></td>
|
||||
<td>
|
||||
<a href="<?php
|
||||
echo esc_url($host . "/myaccount/autopay/connect/{$subscription_id}");
|
||||
?>" id="ppcp-subscription-id" target="_blank"><?php
|
||||
echo esc_html($subscription_id);
|
||||
?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
);
|
||||
add_filter(
|
||||
'woocommerce_order_data_store_cpt_get_orders_query',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($query, $query_vars): array {
|
||||
if (!empty($query_vars['ppcp_subscription'])) {
|
||||
$query['meta_query'][] = array('key' => 'ppcp_subscription', 'value' => esc_attr($query_vars['ppcp_subscription']));
|
||||
}
|
||||
return $query;
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
add_action(
|
||||
'woocommerce_customer_changed_subscription_to_cancelled',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($subscription) use ($c) {
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if ($subscription_id) {
|
||||
$subscriptions_endpoint = $c->get('api.endpoint.billing-subscriptions');
|
||||
assert($subscriptions_endpoint instanceof BillingSubscriptions);
|
||||
try {
|
||||
$subscriptions_endpoint->suspend($subscription_id);
|
||||
} 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 suspend subscription product on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
add_action(
|
||||
'woocommerce_customer_changed_subscription_to_active',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($subscription) use ($c) {
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if ($subscription_id) {
|
||||
$subscriptions_endpoint = $c->get('api.endpoint.billing-subscriptions');
|
||||
assert($subscriptions_endpoint instanceof BillingSubscriptions);
|
||||
try {
|
||||
$subscriptions_endpoint->activate($subscription_id);
|
||||
} 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 active subscription product on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
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);
|
||||
try {
|
||||
$subscriptions_mode = $settings->get('subscriptions_mode');
|
||||
if ($subscriptions_mode === 'subscriptions_api') {
|
||||
/**
|
||||
* Needed for getting global post object.
|
||||
*
|
||||
* @psalm-suppress InvalidGlobal
|
||||
*/
|
||||
global $post;
|
||||
$product = wc_get_product($post->ID);
|
||||
if (!is_a($product, WC_Product::class)) {
|
||||
return;
|
||||
}
|
||||
$environment = $c->get('settings.environment');
|
||||
echo '<div class="options_group subscription_pricing show_if_subscription hidden">';
|
||||
$this->render_paypal_subscription_fields($product, $environment);
|
||||
echo '</div>';
|
||||
}
|
||||
} catch (NotFoundException $exception) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
add_action(
|
||||
'woocommerce_variation_options_pricing',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @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);
|
||||
try {
|
||||
$subscriptions_mode = $settings->get('subscriptions_mode');
|
||||
if ($subscriptions_mode === 'subscriptions_api') {
|
||||
$product = wc_get_product($variation->ID);
|
||||
if (!is_a($product, WC_Product_Subscription_Variation::class)) {
|
||||
return;
|
||||
}
|
||||
$environment = $c->get('settings.environment');
|
||||
$this->render_paypal_subscription_fields($product, $environment);
|
||||
}
|
||||
} catch (NotFoundException $exception) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function ($hook) use ($c) {
|
||||
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 (!in_array($hook, array('post.php', 'post-new.php'), \true) || $subscription_mode !== 'subscriptions_api') {
|
||||
return;
|
||||
}
|
||||
$module_url = $c->get('paypal-subscriptions.module.url');
|
||||
wp_enqueue_script('ppcp-paypal-subscription', untrailingslashit($module_url) . '/assets/js/paypal-subscription.js', array('jquery'), $c->get('ppcp.asset-version'), \true);
|
||||
wp_set_script_translations('ppcp-paypal-subscription', 'woocommerce-paypal-payments');
|
||||
$product = wc_get_product();
|
||||
if (!$product) {
|
||||
return;
|
||||
}
|
||||
wp_localize_script('ppcp-paypal-subscription', 'PayPalCommerceGatewayPayPalSubscriptionProducts', array('ajax' => array('deactivate_plan' => array('endpoint' => \WC_AJAX::get_endpoint(\WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint::ENDPOINT), 'nonce' => wp_create_nonce(\WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint::ENDPOINT))), 'product_id' => $product->get_id(), 'i18n' => array('prices_must_be_above_zero' => __('Prices must be above zero for PayPal Subscriptions!', 'woocommerce-paypal-payments'), 'not_allowed_period_interval' => __('Not allowed period interval combination for PayPal Subscriptions!', 'woocommerce-paypal-payments'))));
|
||||
}
|
||||
);
|
||||
add_action('wc_ajax_' . \WooCommerce\PayPalCommerce\PayPalSubscriptions\DeactivatePlanEndpoint::ENDPOINT, function () use ($c) {
|
||||
$c->get('paypal-subscriptions.deactivate-plan-endpoint')->handle_request();
|
||||
});
|
||||
add_action(
|
||||
'add_meta_boxes',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
function (string $post_type, $post_or_order_object) use ($c) {
|
||||
if (!function_exists('wcs_get_subscription')) {
|
||||
return;
|
||||
}
|
||||
$order = $post_or_order_object instanceof WP_Post ? wc_get_order($post_or_order_object->ID) : $post_or_order_object;
|
||||
if (!is_a($order, WC_Order::class)) {
|
||||
return;
|
||||
}
|
||||
$subscription = wcs_get_subscription($order->get_id());
|
||||
if (!is_a($subscription, WC_Subscription::class)) {
|
||||
return;
|
||||
}
|
||||
$subscription_id = $subscription->get_meta('ppcp_subscription') ?? '';
|
||||
if (!$subscription_id) {
|
||||
return;
|
||||
}
|
||||
$screen_id = wc_get_page_screen_id('shop_subscription');
|
||||
remove_meta_box('woocommerce-subscription-schedule', $screen_id, 'side');
|
||||
$host = $c->get('api.paypal-website-url');
|
||||
add_meta_box('ppcp_paypal_subscription', __('PayPal Subscription', 'woocommerce-paypal-payments'), function () use ($subscription_id, $host) {
|
||||
$url = trailingslashit($host) . 'billing/subscriptions/' . $subscription_id;
|
||||
echo '<p>' . esc_html__('This subscription is linked to a PayPal Subscription, Cancel it to unlink.', 'woocommerce-paypal-payments') . '</p>';
|
||||
echo '<p><strong>' . esc_html__('Subscription:', 'woocommerce-paypal-payments') . '</strong> <a href="' . esc_url($url) . '" target="_blank">' . esc_attr($subscription_id) . '</a></p>';
|
||||
}, $post_type, 'side');
|
||||
},
|
||||
30,
|
||||
2
|
||||
);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Updates subscription product meta.
|
||||
*
|
||||
* @param WC_Product $product The product.
|
||||
* @param SubscriptionsApiHandler $subscriptions_api_handler The subscription api handler.
|
||||
* @return void
|
||||
*
|
||||
* @psalm-suppress PossiblyInvalidCast
|
||||
*/
|
||||
private function update_subscription_product_meta(WC_Product $product, \WooCommerce\PayPalCommerce\PayPalSubscriptions\SubscriptionsApiHandler $subscriptions_api_handler): void
|
||||
{
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$enable_subscription_product = wc_string_to_bool((string) wc_clean(wp_unslash($_POST['_ppcp_enable_subscription_product'] ?? '')));
|
||||
$product->update_meta_data('_ppcp_enable_subscription_product', wc_bool_to_string($enable_subscription_product));
|
||||
if (!$enable_subscription_product) {
|
||||
$product->save();
|
||||
return;
|
||||
}
|
||||
if (!$product->get_sold_individually()) {
|
||||
$product->set_sold_individually(\true);
|
||||
}
|
||||
$product->save();
|
||||
if ($product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation') {
|
||||
if ($product->meta_exists('ppcp_subscription_product') && $product->meta_exists('ppcp_subscription_plan')) {
|
||||
$subscriptions_api_handler->update_product($product);
|
||||
$subscriptions_api_handler->update_plan($product);
|
||||
return;
|
||||
}
|
||||
if (!$product->meta_exists('ppcp_subscription_product')) {
|
||||
$subscriptions_api_handler->create_product($product);
|
||||
}
|
||||
if ($product->meta_exists('ppcp_subscription_product') && !$product->meta_exists('ppcp_subscription_plan')) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification
|
||||
$subscription_plan_name = wc_clean(wp_unslash($_POST['_ppcp_subscription_plan_name'] ?? ''));
|
||||
if (!is_string($subscription_plan_name)) {
|
||||
return;
|
||||
}
|
||||
$product->update_meta_data('_ppcp_subscription_plan_name', $subscription_plan_name);
|
||||
$product->save();
|
||||
$subscriptions_api_handler->create_plan($subscription_plan_name, $product);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Render PayPal Subscriptions fields.
|
||||
*
|
||||
* @param WC_Product $product WC Product.
|
||||
* @param Environment $environment The environment.
|
||||
* @return void
|
||||
*/
|
||||
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>'
|
||||
);
|
||||
$plan_id = $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'),
|
||||
'<span class="description">',
|
||||
'</span>'
|
||||
);
|
||||
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>';
|
||||
if ($subscription_product || $subscription_plan) {
|
||||
$display_unlink_p = 'display:none;';
|
||||
if ($enable_subscription_product !== 'yes') {
|
||||
$display_unlink_p = '';
|
||||
}
|
||||
echo sprintf(
|
||||
// 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-' . 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.
|
||||
esc_html__('%1$sPlan unlinked successfully ✔️%2$s', 'woocommerce-paypal-payments'),
|
||||
'<p class="form-field pcpp-plan-unlinked" id="pcpp-plan-unlinked-' . esc_attr((string) $product->get_id()) . '" style="display: none;">',
|
||||
'</p>'
|
||||
);
|
||||
$host = $environment->current_environment_is(Environment::SANDBOX) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com';
|
||||
if ($subscription_product) {
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s are wrapper html tags.
|
||||
esc_html__('%1$sProduct%2$s', 'woocommerce-paypal-payments'),
|
||||
'<p class="form-field pcpp-product" id="pcpp-product-' . esc_attr((string) $product->get_id()) . '"><label style="' . esc_attr($style) . '">',
|
||||
'</label><a href="' . esc_url($host . '/billing/plans/products/' . $subscription_product['id']) . '" target="_blank">' . esc_attr($subscription_product['id']) . '</a></p>'
|
||||
);
|
||||
}
|
||||
if ($subscription_plan) {
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s are wrapper html tags.
|
||||
esc_html__('%1$sPlan%2$s', 'woocommerce-paypal-payments'),
|
||||
'<p class="form-field pcpp-plan" id="pcpp-plan-' . esc_attr((string) $product->get_id()) . '"><label style="' . esc_attr($style) . '">',
|
||||
'</label><a href="' . esc_url($host . '/billing/plans/' . $subscription_plan['id']) . '" target="_blank">' . esc_attr($subscription_plan['id']) . '</a></p>'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$display_plan_name_p = '';
|
||||
if ($enable_subscription_product !== 'yes') {
|
||||
$display_plan_name_p = 'display:none;';
|
||||
}
|
||||
echo sprintf(
|
||||
// translators: %1$s and %2$s are wrapper html tags.
|
||||
esc_html__('%1$sPlan Name%2$s', 'woocommerce-paypal-payments'),
|
||||
'<p class="form-field ppcp_subscription_plan_name_p" id="ppcp_subscription_plan_name_p-' . esc_attr((string) $product->get_id()) . '" style="' . esc_attr($display_plan_name_p) . '"><label for="_ppcp_subscription_plan_name-' . esc_attr((string) $product->get_id()) . '">',
|
||||
'</label><input type="text" class="short ppcp_subscription_plan_name" id="ppcp_subscription_plan_name-' . esc_attr((string) $product->get_id()) . '" name="_ppcp_subscription_plan_name" value="' . esc_attr($subscription_plan_name) . '"></p>'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Subscriptions renewal handler.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
|
||||
use WC_Data_Exception;
|
||||
use WC_Order;
|
||||
use WC_Subscription;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait;
|
||||
/**
|
||||
* Class RenewalHandler
|
||||
*/
|
||||
class RenewalHandler
|
||||
{
|
||||
use TransactionIdHandlingTrait;
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* RenewalHandler constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
/**
|
||||
* Process subscription renewal.
|
||||
*
|
||||
* @param WC_Subscription[] $subscriptions WC Subscriptions.
|
||||
* @param string $transaction_id PayPal transaction ID.
|
||||
* @return void
|
||||
* @throws WC_Data_Exception If something goes wrong while setting payment method.
|
||||
*/
|
||||
public function process(array $subscriptions, string $transaction_id): void
|
||||
{
|
||||
foreach ($subscriptions as $subscription) {
|
||||
if ($this->is_for_renewal_order($subscription)) {
|
||||
$subscription->update_status('on-hold');
|
||||
$renewal_order = wcs_create_renewal_order($subscription);
|
||||
if (is_a($renewal_order, WC_Order::class)) {
|
||||
$this->logger->info(sprintf('Processing renewal order #%s for subscription #%s', $renewal_order->get_id(), $subscription->get_id()));
|
||||
$renewal_order->set_payment_method($subscription->get_payment_method());
|
||||
$renewal_order->payment_complete();
|
||||
$this->update_transaction_id($transaction_id, $renewal_order, $this->logger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$parent_order = wc_get_order($subscription->get_parent());
|
||||
if (is_a($parent_order, WC_Order::class)) {
|
||||
$this->logger->info(sprintf('Processing parent order #%s for subscription #%s', $parent_order->get_id(), $subscription->get_id()));
|
||||
$subscription->update_meta_data('_ppcp_is_subscription_renewal', 'true');
|
||||
$subscription->save_meta_data();
|
||||
$this->update_transaction_id($transaction_id, $parent_order, $this->logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Checks whether subscription order is for renewal or not.
|
||||
*
|
||||
* @param WC_Subscription $subscription WC Subscription.
|
||||
* @return bool
|
||||
*/
|
||||
private function is_for_renewal_order(WC_Subscription $subscription): bool
|
||||
{
|
||||
$subscription_renewal_meta = $subscription->get_meta('_ppcp_is_subscription_renewal') ?? '';
|
||||
if ($subscription_renewal_meta === 'true') {
|
||||
return \true;
|
||||
}
|
||||
if (time() >= $subscription->get_time('start') && time() - $subscription->get_time('start') <= 8 * HOUR_IN_SECONDS) {
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handles PayPal subscription status.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
/**
|
||||
* Class SubscriptionStatus
|
||||
*/
|
||||
class SubscriptionStatus
|
||||
{
|
||||
/**
|
||||
* Billing subscriptions endpoint.
|
||||
*
|
||||
* @var BillingSubscriptions
|
||||
*/
|
||||
private $subscriptions_endpoint;
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* SubscriptionStatus constructor.
|
||||
*
|
||||
* @param BillingSubscriptions $subscriptions_endpoint Billing subscriptions endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(BillingSubscriptions $subscriptions_endpoint, LoggerInterface $logger)
|
||||
{
|
||||
$this->subscriptions_endpoint = $subscriptions_endpoint;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
/**
|
||||
* Updates PayPal subscription status from the given WC Subscription status.
|
||||
*
|
||||
* @param string $subscription_status The WC Subscription status.
|
||||
* @param string $subscription_id The PayPal Subscription ID.
|
||||
* @return void
|
||||
*/
|
||||
public function update_status(string $subscription_status, string $subscription_id): void
|
||||
{
|
||||
if ($subscription_status === 'cancelled') {
|
||||
try {
|
||||
$current_subscription = $this->subscriptions_endpoint->subscription($subscription_id);
|
||||
if ($current_subscription->status === 'CANCELLED') {
|
||||
return;
|
||||
}
|
||||
$this->logger->info(sprintf('Canceling PayPal subscription #%s.', $subscription_id));
|
||||
$this->subscriptions_endpoint->cancel($subscription_id);
|
||||
} catch (RuntimeException $exception) {
|
||||
$this->logger->error(sprintf('Could not cancel PayPal subscription #%s. %s', $subscription_id, $this->get_error($exception)));
|
||||
}
|
||||
}
|
||||
if ($subscription_status === 'on-hold' || $subscription_status === 'pending-cancel') {
|
||||
try {
|
||||
$this->logger->info(sprintf('Suspending PayPal subscription #%s.', $subscription_id));
|
||||
$this->subscriptions_endpoint->suspend($subscription_id);
|
||||
} catch (RuntimeException $exception) {
|
||||
$this->logger->error(sprintf('Could not suspend PayPal subscription #%s. %s', $subscription_id, $this->get_error($exception)));
|
||||
}
|
||||
}
|
||||
if ($subscription_status === 'active') {
|
||||
try {
|
||||
$current_subscription = $this->subscriptions_endpoint->subscription($subscription_id);
|
||||
if ($current_subscription->status === 'SUSPENDED') {
|
||||
$this->logger->info(sprintf('Activating suspended PayPal subscription #%s.', $subscription_id));
|
||||
$this->subscriptions_endpoint->activate($subscription_id);
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
$this->logger->error(sprintf('Could not reactivate PayPal subscription #%s. %s', $subscription_id, $this->get_error($exception)));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get status by subscription ID.
|
||||
*
|
||||
* @param string $subscription_id Subscription ID to get status for.
|
||||
* @return string Current subscription status.
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
*/
|
||||
public function get_status(string $subscription_id): string
|
||||
{
|
||||
static $subscription_status = null;
|
||||
if (null === $subscription_status) {
|
||||
$subscription = $this->subscriptions_endpoint->subscription($subscription_id);
|
||||
if (!isset($subscription->status)) {
|
||||
throw new RuntimeException('Status not found in subscription data');
|
||||
}
|
||||
$subscription_status = (string) $subscription->status;
|
||||
}
|
||||
return $subscription_status;
|
||||
}
|
||||
/**
|
||||
* Get error from exception.
|
||||
*
|
||||
* @param RuntimeException $exception The exception.
|
||||
* @return string
|
||||
*/
|
||||
private function get_error(RuntimeException $exception): string
|
||||
{
|
||||
$error = $exception->getMessage();
|
||||
if (is_a($exception, PayPalApiException::class)) {
|
||||
$error = $exception->get_details($error);
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The subscription module.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcSubscriptions
|
||||
*/
|
||||
declare (strict_types=1);
|
||||
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
|
||||
use WC_Product;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\BillingCycle;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\ItemTrait;
|
||||
/**
|
||||
* Class SubscriptionsApiHandler
|
||||
*/
|
||||
class SubscriptionsApiHandler
|
||||
{
|
||||
use ItemTrait;
|
||||
/**
|
||||
* Catalog products.
|
||||
*
|
||||
* @var CatalogProducts
|
||||
*/
|
||||
private $products_endpoint;
|
||||
/**
|
||||
* Product factory.
|
||||
*
|
||||
* @var ProductFactory
|
||||
*/
|
||||
private $product_factory;
|
||||
/**
|
||||
* Billing plans.
|
||||
*
|
||||
* @var BillingPlans
|
||||
*/
|
||||
private $billing_plans_endpoint;
|
||||
/**
|
||||
* Billing cycle factory.
|
||||
*
|
||||
* @var BillingCycleFactory
|
||||
*/
|
||||
private $billing_cycle_factory;
|
||||
/**
|
||||
* Payment preferences factory.
|
||||
*
|
||||
* @var PaymentPreferencesFactory
|
||||
*/
|
||||
private $payment_preferences_factory;
|
||||
/**
|
||||
* The currency.
|
||||
*
|
||||
* @var CurrencyGetter
|
||||
*/
|
||||
private CurrencyGetter $currency;
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
/**
|
||||
* SubscriptionsApiHandler constructor.
|
||||
*
|
||||
* @param CatalogProducts $products_endpoint Products endpoint.
|
||||
* @param ProductFactory $product_factory Product factory.
|
||||
* @param BillingPlans $billing_plans_endpoint Billing plans endpoint.
|
||||
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
|
||||
* @param PaymentPreferencesFactory $payment_preferences_factory Payment preferences factory.
|
||||
* @param CurrencyGetter $currency The currency.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(CatalogProducts $products_endpoint, ProductFactory $product_factory, BillingPlans $billing_plans_endpoint, BillingCycleFactory $billing_cycle_factory, PaymentPreferencesFactory $payment_preferences_factory, CurrencyGetter $currency, LoggerInterface $logger)
|
||||
{
|
||||
$this->products_endpoint = $products_endpoint;
|
||||
$this->product_factory = $product_factory;
|
||||
$this->billing_plans_endpoint = $billing_plans_endpoint;
|
||||
$this->billing_cycle_factory = $billing_cycle_factory;
|
||||
$this->payment_preferences_factory = $payment_preferences_factory;
|
||||
$this->currency = $currency;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
/**
|
||||
* Creates a Catalog Product and adds it as WC product meta.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function create_product(WC_Product $product)
|
||||
{
|
||||
try {
|
||||
$subscription_product = $this->products_endpoint->create($this->prepare_item_string($product->get_title()), $this->prepare_item_string($product->get_description()));
|
||||
$product->update_meta_data('ppcp_subscription_product', $subscription_product->to_array());
|
||||
$product->save();
|
||||
} catch (RuntimeException $exception) {
|
||||
$error = $exception->getMessage();
|
||||
if (is_a($exception, PayPalApiException::class)) {
|
||||
$error = $exception->get_details($error);
|
||||
}
|
||||
$this->logger->error('Could not create catalog product on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a subscription plan.
|
||||
*
|
||||
* @param string $plan_name The plan name.
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function create_plan(string $plan_name, WC_Product $product): void
|
||||
{
|
||||
try {
|
||||
$subscription_plan = $this->billing_plans_endpoint->create($plan_name ?: $product->get_title(), $product->get_meta('ppcp_subscription_product')['id'] ?? '', $this->billing_cycles($product), $this->payment_preferences_factory->from_wc_product($product)->to_array());
|
||||
$product->update_meta_data('ppcp_subscription_plan', $subscription_plan->to_array());
|
||||
$product->save();
|
||||
} catch (RuntimeException $exception) {
|
||||
$error = $exception->getMessage();
|
||||
if (is_a($exception, PayPalApiException::class)) {
|
||||
$error = $exception->get_details($error);
|
||||
}
|
||||
$this->logger->error('Could not create subscription plan on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Updates a product.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function update_product(WC_Product $product): void
|
||||
{
|
||||
try {
|
||||
$catalog_product_id = $product->get_meta('ppcp_subscription_product')['id'] ?? '';
|
||||
if ($catalog_product_id) {
|
||||
$catalog_product = $this->products_endpoint->product($catalog_product_id);
|
||||
$catalog_product_name = $catalog_product->name() ?: '';
|
||||
$catalog_product_description = $catalog_product->description() ?: '';
|
||||
$wc_product_description = $this->prepare_item_string($product->get_description()) ?: $this->prepare_item_string($product->get_title());
|
||||
if ($catalog_product_name !== $product->get_title() || $catalog_product_description !== $wc_product_description) {
|
||||
$data = array();
|
||||
if ($catalog_product_name !== $product->get_title()) {
|
||||
$data[] = (object) array('op' => 'replace', 'path' => '/name', 'value' => $product->get_title());
|
||||
}
|
||||
if ($catalog_product_description !== $wc_product_description) {
|
||||
$data[] = (object) array('op' => 'replace', 'path' => '/description', 'value' => $wc_product_description);
|
||||
}
|
||||
$this->products_endpoint->update($catalog_product_id, $data);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
$error = $exception->getMessage();
|
||||
if (is_a($exception, PayPalApiException::class)) {
|
||||
$error = $exception->get_details($error);
|
||||
}
|
||||
$this->logger->error('Could not update catalog product on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Updates a plan.
|
||||
*
|
||||
* @param WC_Product $product The WC product.
|
||||
* @return void
|
||||
*/
|
||||
public function update_plan(WC_Product $product): void
|
||||
{
|
||||
try {
|
||||
$subscription_plan_id = $product->get_meta('ppcp_subscription_plan')['id'] ?? '';
|
||||
if ($subscription_plan_id) {
|
||||
$subscription_plan = $this->billing_plans_endpoint->plan($subscription_plan_id);
|
||||
$price = $subscription_plan->billing_cycles()[0]->pricing_scheme()['fixed_price']['value'] ?? '';
|
||||
if ($price && round((float) $price, 2) !== round((float) $product->get_price(), 2)) {
|
||||
$this->billing_plans_endpoint->update_pricing($subscription_plan_id, $this->billing_cycle_factory->from_wc_product($product));
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
$error = $exception->getMessage();
|
||||
if (is_a($exception, PayPalApiException::class)) {
|
||||
$error = $exception->get_details($error);
|
||||
}
|
||||
$this->logger->error('Could not update subscription plan on PayPal. ' . $error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns billing cycles based on WC Subscription product.
|
||||
*
|
||||
* @param WC_Product $product The WC Subscription product.
|
||||
* @return array
|
||||
*/
|
||||
private function billing_cycles(WC_Product $product): array
|
||||
{
|
||||
$billing_cycles = array();
|
||||
$sequence = 1;
|
||||
$trial_length = $product->get_meta('_subscription_trial_length') ?? '';
|
||||
if ($trial_length) {
|
||||
$billing_cycles[] = (new BillingCycle(array('interval_unit' => $product->get_meta('_subscription_trial_period'), 'interval_count' => $product->get_meta('_subscription_trial_length')), $sequence, 'TRIAL', array('fixed_price' => array('value' => '0', 'currency_code' => $this->currency->get())), 1))->to_array();
|
||||
++$sequence;
|
||||
}
|
||||
$interval = $product->get_meta('_subscription_period');
|
||||
$period_interval = (int) $product->get_meta('_subscription_period_interval');
|
||||
$update = \false;
|
||||
if ($interval === 'day' && $period_interval > 365) {
|
||||
$period_interval = 365;
|
||||
$update = \true;
|
||||
} elseif ($interval === 'week' && $period_interval > 52) {
|
||||
$period_interval = 52;
|
||||
$update = \true;
|
||||
} elseif ($interval === 'month' && $period_interval > 12) {
|
||||
$period_interval = 12;
|
||||
$update = \true;
|
||||
} elseif ($interval === 'year' && $period_interval > 1) {
|
||||
$period_interval = 1;
|
||||
$update = \true;
|
||||
}
|
||||
if ($update) {
|
||||
$product->add_meta_data('_subscription_period_interval', (string) $period_interval, \true);
|
||||
$product->save();
|
||||
$this->logger->warning(sprintf('Subscription plan on PayPal is to high for %1$s interval change it to maximum of %2$d', $interval, $period_interval));
|
||||
}
|
||||
$billing_cycles[] = (new BillingCycle(array('interval_unit' => $interval, 'interval_count' => $period_interval), $sequence, 'REGULAR', array('fixed_price' => array('value' => $product->get_meta('_subscription_price') ?: $product->get_price(), 'currency_code' => $this->currency->get())), (int) $product->get_meta('_subscription_length')))->to_array();
|
||||
return $billing_cycles;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user