<?php
/**
* The subscription module.
*
* @package WooCommerce\PayPalCommerce\WcSubscriptions
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\PayPalSubscriptions;
use 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++;
}
$billing_cycles[] = ( new BillingCycle(
array(
'interval_unit' => $product->get_meta( '_subscription_period' ),
'interval_count' => $product->get_meta( '_subscription_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;
}
}