mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Allow customer suspend and activate subscription
This commit is contained in:
parent
6400e14874
commit
5a230a7669
5 changed files with 214 additions and 19 deletions
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
|
@ -222,6 +223,13 @@ return array(
|
|||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.endpoint.billing-subscriptions' => static function( ContainerInterface $container ): BillingSubscriptions {
|
||||
return new BillingSubscriptions(
|
||||
$container->get( 'api.host' ),
|
||||
$container->get( 'api.bearer' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
|
||||
|
||||
$settings = $container->get( 'wcgateway.settings' );
|
||||
|
|
137
modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php
Normal file
137
modules/ppcp-api-client/src/Endpoint/BillingSubscriptions.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
/**
|
||||
* The Billing Subscriptions endpoint.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Class BillingSubscriptions
|
||||
*/
|
||||
class BillingSubscriptions {
|
||||
|
||||
use RequestTrait;
|
||||
|
||||
/**
|
||||
* The host.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* The bearer.
|
||||
*
|
||||
* @var Bearer
|
||||
*/
|
||||
private $bearer;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* BillingSubscriptions constructor.
|
||||
*
|
||||
* @param string $host The host.
|
||||
* @param Bearer $bearer The bearer.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct( string $host, Bearer $bearer, LoggerInterface $logger ) {
|
||||
$this->host = $host;
|
||||
$this->bearer = $bearer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends a subscription.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function suspend( string $id ):void {
|
||||
$data = array(
|
||||
'reason' => 'Cancelled by customer',
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/suspend';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create plan.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates a subscription.
|
||||
*
|
||||
* @param string $id Subscription ID.
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException If the request fails.
|
||||
* @throws PayPalApiException If the request fails.
|
||||
*/
|
||||
public function activate( string $id ): void {
|
||||
$data = array(
|
||||
'reason' => 'Reactivated by customer',
|
||||
);
|
||||
|
||||
$bearer = $this->bearer->bearer();
|
||||
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/activate';
|
||||
$args = array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $bearer->token(),
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $data ),
|
||||
);
|
||||
|
||||
$response = $this->request( $url, $args );
|
||||
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
|
||||
throw new RuntimeException( 'Not able to create plan.' );
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'] );
|
||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 204 !== $status_code ) {
|
||||
throw new PayPalApiException(
|
||||
$json,
|
||||
$status_code
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Subscription;
|
||||
|
||||
use WC_Product_Subscription;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
|
@ -164,22 +165,22 @@ class SubscriptionModule implements ModuleInterface {
|
|||
$billing_plans_endpoint = $c->get( 'api.endpoint.billing-plans' );
|
||||
assert( $billing_plans_endpoint instanceof BillingPlans );
|
||||
|
||||
if($product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' )) {
|
||||
if($product->get_meta( '_subscription_price' ) === $product->get_meta('ppcp_subscription_plan')->billing_cycles[0]->pricing_scheme->fixed_price->value) {
|
||||
if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) {
|
||||
if ( $product->get_meta( '_subscription_price' ) === $product->get_meta( 'ppcp_subscription_plan' )->billing_cycles[0]->pricing_scheme->fixed_price->value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$billing_cycles = array(
|
||||
'pricing_scheme' => array(
|
||||
'fixed_price' => array(
|
||||
'value' => $product->get_meta( '_subscription_price' ),
|
||||
'value' => $product->get_meta( '_subscription_price' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
$billing_plans_endpoint->update_pricing(
|
||||
$product->get_meta('ppcp_subscription_plan')->id,
|
||||
$product->get_meta( 'ppcp_subscription_plan' )->id,
|
||||
$billing_cycles
|
||||
);
|
||||
} catch ( RuntimeException $exception ) {
|
||||
|
@ -267,22 +268,25 @@ class SubscriptionModule implements ModuleInterface {
|
|||
|
||||
add_action(
|
||||
'add_meta_boxes',
|
||||
function( string $post_type ) use($c) {
|
||||
function( string $post_type ) use ( $c ) {
|
||||
if ( $post_type === 'product' ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) );
|
||||
$product = wc_get_product( $post_id );
|
||||
if (is_a($product, WC_Product_Subscription::class)) {
|
||||
$settings = $c->get('wcgateway.settings');
|
||||
assert($settings instanceof Settings);
|
||||
if ($settings->get('subscriptions_mode') && $settings->get('subscriptions_mode') === 'subscriptions_api') {
|
||||
$subscription_product = $product->get_meta('ppcp_subscription_product');
|
||||
$subscription_plan = $product->get_meta('ppcp_subscription_plan');
|
||||
add_meta_box('ppcp_subscription', __('PayPal Subscription', 'woocommerce-paypal-payments'),
|
||||
function () use ($subscription_product, $subscription_plan) {
|
||||
$product = wc_get_product( $post_id );
|
||||
if ( is_a( $product, WC_Product_Subscription::class ) ) {
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
if ( $settings->get( 'subscriptions_mode' ) && $settings->get( 'subscriptions_mode' ) === 'subscriptions_api' ) {
|
||||
$subscription_product = $product->get_meta( 'ppcp_subscription_product' );
|
||||
$subscription_plan = $product->get_meta( 'ppcp_subscription_plan' );
|
||||
add_meta_box(
|
||||
'ppcp_subscription',
|
||||
__( 'PayPal Subscription', 'woocommerce-paypal-payments' ),
|
||||
function () use ( $subscription_product, $subscription_plan ) {
|
||||
echo '<label><input type="checkbox" name="ppcp_connect_subscriptions_api" checked="checked">Connect to Subscriptions API</label>';
|
||||
if ($subscription_product && $subscription_plan) {
|
||||
echo '<p>Product ID: ' . esc_attr($subscription_product->id) . '</p>';
|
||||
echo '<p>Plan ID: ' . esc_attr($subscription_plan->id) . '</p>';
|
||||
if ( $subscription_product && $subscription_plan ) {
|
||||
echo '<p>Product ID: ' . esc_attr( $subscription_product->id ) . '</p>';
|
||||
echo '<p>Plan ID: ' . esc_attr( $subscription_plan->id ) . '</p>';
|
||||
}
|
||||
},
|
||||
$post_type,
|
||||
|
@ -310,6 +314,52 @@ class SubscriptionModule implements ModuleInterface {
|
|||
10,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_customer_changed_subscription_to_cancelled',
|
||||
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',
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@ require('dotenv').config({ path: '.env.e2e' });
|
|||
|
||||
const config = {
|
||||
testDir: './tests/playwright',
|
||||
timeout: 30000,
|
||||
timeout: 40000,
|
||||
use: {
|
||||
baseURL: process.env.BASEURL,
|
||||
},
|
||||
|
|
|
@ -109,7 +109,7 @@ test.describe('Subscriptions Customer', () => {
|
|||
await popup.fill('#password', CUSTOMER_PASSWORD);
|
||||
await popup.locator('#btnLogin').click();
|
||||
await popup.locator('text=Continue').click();
|
||||
await popup.locator('text=Agree and Subscribe').click();
|
||||
await popup.locator('text=Agree & Subscribe').click();
|
||||
|
||||
await page.waitForTimeout(10000);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue