Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PayPal Subscriptions cancel and suspend from Subscriptions list page does not work (3719) #2632

Open
wants to merge 8 commits into
base: trunk
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public function activate( string $id ): void {
*/
public function cancel( string $id ): void {
$data = array(
'reason' => 'Cancelled by customer',
'reason' => sprintf( 'Cancelled by %s.', is_admin() ? 'merchant' : 'customer' ),
);

$bearer = $this->bearer->bearer();
Expand Down
6 changes: 6 additions & 0 deletions modules/ppcp-paypal-subscriptions/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@
dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php'
);
},
'paypal-subscriptions.status' => static function ( ContainerInterface $container ): SubscriptionStatus {
return new SubscriptionStatus(
$container->get( 'api.endpoint.billing-subscriptions' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
);
71 changes: 25 additions & 46 deletions modules/ppcp-paypal-subscriptions/src/PayPalSubscriptionsModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,72 +189,51 @@ function( $variation_id ) use ( $c ) {
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, $post ) use ( $c ) {
function( $id ) use ( $c ) {
$subscription = wcs_get_subscription( $id );
if ( ! is_a( $subscription, WC_Subscription::class ) ) {
if ( $subscription === false ) {
return;
}

$subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? '';
if ( ! $subscription_id ) {
return;
}
$subscriptions_endpoint = $c->get( 'api.endpoint.billing-subscriptions' );
assert( $subscriptions_endpoint instanceof BillingSubscriptions );

if ( $subscription->get_status() === 'cancelled' ) {
try {
$subscriptions_endpoint->cancel( $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 cancel subscription product on PayPal. ' . $error );
}
}
$subscription_status = $c->get( 'paypal-subscriptions.status' );
assert( $subscription_status instanceof SubscriptionStatus );

if ( $subscription->get_status() === 'pending-cancel' ) {
try {
$subscriptions_endpoint->suspend( $subscription_id );
} catch ( RuntimeException $exception ) {
$error = $exception->getMessage();
if ( is_a( $exception, PayPalApiException::class ) ) {
$error = $exception->get_details( $error );
}
$subscription_status->update_status( $subscription->get_status(), $subscription_id );
},
20
);

$logger = $c->get( 'woocommerce.logger.woocommerce' );
$logger->error( 'Could not suspend subscription product on PayPal. ' . $error );
}
/**
* 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;
}

if ( $subscription->get_status() === 'active' ) {
try {
$current_subscription = $subscriptions_endpoint->subscription( $subscription_id );
if ( $current_subscription->status === 'SUSPENDED' ) {
$subscriptions_endpoint->activate( $subscription_id );
}
} catch ( RuntimeException $exception ) {
$error = $exception->getMessage();
if ( is_a( $exception, PayPalApiException::class ) ) {
$error = $exception->get_details( $error );
}
$subscription_status = $c->get( 'paypal-subscriptions.status' );
assert( $subscription_status instanceof SubscriptionStatus );

$logger = $c->get( 'woocommerce.logger.woocommerce' );
$logger->error( 'Could not reactivate subscription product on PayPal. ' . $error );
}
}
},
20,
2
$subscription_status->update_status( $subscription->get_status(), $subscription_id );
}
);

add_filter(
Expand Down
144 changes: 144 additions & 0 deletions modules/ppcp-paypal-subscriptions/src/SubscriptionStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php
/**
* Handles PayPal subscription status.
*
* @package WooCommerce\PayPalCommerce\WcSubscriptions
*/

declare(strict_types=1);

namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;

use 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 === 'pending-cancel' || $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' ) {
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 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;
}
}
Loading