Merge branch 'trunk' into fix/PCP-696-improve-error-message

This commit is contained in:
carmenmaymo 2023-06-11 12:07:10 +02:00
commit 027def77b4
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
126 changed files with 9521 additions and 789 deletions

View file

@ -9,6 +9,14 @@ 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\ApiClient\Factory\BillingCycleFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentPreferencesFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlanFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingOptionFactory;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
@ -209,6 +217,30 @@ return array(
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'api.endpoint.catalog-products' => static function ( ContainerInterface $container ): CatalogProducts {
return new CatalogProducts(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'api.factory.product' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'api.endpoint.billing-plans' => static function( ContainerInterface $container ): BillingPlans {
return new BillingPlans(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'api.factory.billing-cycle' ),
$container->get( 'api.factory.plan' ),
$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' );
@ -289,12 +321,19 @@ return array(
);
},
'api.factory.shipping' => static function ( ContainerInterface $container ): ShippingFactory {
$address_factory = $container->get( 'api.factory.address' );
return new ShippingFactory( $address_factory );
return new ShippingFactory(
$container->get( 'api.factory.address' ),
$container->get( 'api.factory.shipping-option' )
);
},
'api.factory.shipping-preference' => static function ( ContainerInterface $container ): ShippingPreferenceFactory {
return new ShippingPreferenceFactory();
},
'api.factory.shipping-option' => static function ( ContainerInterface $container ): ShippingOptionFactory {
return new ShippingOptionFactory(
$container->get( 'api.factory.money' )
);
},
'api.factory.amount' => static function ( ContainerInterface $container ): AmountFactory {
$item_factory = $container->get( 'api.factory.item' );
return new AmountFactory(
@ -357,6 +396,21 @@ return array(
'api.factory.fraud-processor-response' => static function ( ContainerInterface $container ): FraudProcessorResponseFactory {
return new FraudProcessorResponseFactory();
},
'api.factory.product' => static function( ContainerInterface $container ): ProductFactory {
return new ProductFactory();
},
'api.factory.billing-cycle' => static function( ContainerInterface $container ): BillingCycleFactory {
return new BillingCycleFactory( $container->get( 'api.shop.currency' ) );
},
'api.factory.payment-preferences' => static function( ContainerInterface $container ):PaymentPreferencesFactory {
return new PaymentPreferencesFactory( $container->get( 'api.shop.currency' ) );
},
'api.factory.plan' => static function( ContainerInterface $container ): PlanFactory {
return new PlanFactory(
$container->get( 'api.factory.billing-cycle' ),
$container->get( 'api.factory.payment-preferences' )
);
},
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
return new DccApplies(
$container->get( 'api.dcc-supported-country-currency-matrix' ),
@ -631,6 +685,27 @@ return array(
'SGD',
'USD',
),
'MX' => array(
'MXN',
),
'JP' => array(
'AUD',
'CAD',
'CHF',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'JPY',
'NOK',
'NZD',
'PLN',
'SEK',
'SGD',
'USD',
),
)
);
},
@ -687,6 +762,17 @@ return array(
'amex' => array( 'CAD' ),
'jcb' => array( 'CAD' ),
),
'MX' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array(),
),
'JP' => array(
'mastercard' => array(),
'visa' => array(),
'amex' => array( 'JPY' ),
'jcb' => array( 'JPY' ),
),
)
);
},

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use Exception;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
@ -120,6 +121,10 @@ class BillingAgreementsEndpoint {
*/
public function reference_transaction_enabled(): bool {
try {
if ( get_transient( 'ppcp_reference_transaction_enabled' ) === true ) {
return true;
}
$this->is_request_logging_enabled = false;
try {
@ -130,10 +135,12 @@ class BillingAgreementsEndpoint {
);
} finally {
$this->is_request_logging_enabled = true;
set_transient( 'ppcp_reference_transaction_enabled', true, 3 * MONTH_IN_SECONDS );
}
return true;
} catch ( PayPalApiException $exception ) {
} catch ( Exception $exception ) {
delete_transient( 'ppcp_reference_transaction_enabled' );
return false;
}
}

View file

@ -0,0 +1,229 @@
<?php
/**
* The Billing Plans 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\Entity\BillingCycle;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Plan;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\BillingCycleFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PlanFactory;
/**
* Class BillingPlans
*/
class BillingPlans {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* Billing cycle factory
*
* @var BillingCycleFactory
*/
private $billing_cycle_factory;
/**
* Plan factory
*
* @var PlanFactory
*/
private $plan_factory;
/**
* The logger.
*
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* BillingPlans constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
* @param PlanFactory $plan_factory Plan factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
BillingCycleFactory $billing_cycle_factory,
PlanFactory $plan_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->billing_cycle_factory = $billing_cycle_factory;
$this->plan_factory = $plan_factory;
$this->logger = $logger;
}
/**
* Creates a subscription plan.
*
* @param string $name Product name.
* @param string $product_id Product ID.
* @param array $billing_cycles Billing cycles.
* @param array $payment_preferences Payment preferences.
*
* @return Plan
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function create(
string $name,
string $product_id,
array $billing_cycles,
array $payment_preferences
): Plan {
$data = array(
'name' => $name,
'product_id' => $product_id,
'billing_cycles' => $billing_cycles,
'payment_preferences' => $payment_preferences,
);
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing/plans';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
'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 ( 201 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $this->plan_factory->from_paypal_response( $json );
}
/**
* Returns a plan,
*
* @param string $id Plan ID.
*
* @return Plan
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function plan( string $id ): Plan {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing/plans/' . $id;
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
throw new RuntimeException( 'Not able to get plan.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $this->plan_factory->from_paypal_response( $json );
}
/**
* Updates pricing.
*
* @param string $id Plan ID.
* @param BillingCycle $billing_cycle Billing cycle.
*
* @return void
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function update_pricing( string $id, BillingCycle $billing_cycle ): void {
$data = array(
'pricing_schemes' => array(
(object) array(
'billing_cycle_sequence' => 1,
'pricing_scheme' => $billing_cycle->pricing_scheme(),
),
),
);
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing/plans/' . $id . '/update-pricing-schemes';
$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( 'Could not update pricing.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 204 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
}
}

View file

@ -0,0 +1,217 @@
<?php
/**
* The Billing Subscriptions endpoint.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use Psr\Log\LoggerInterface;
use stdClass;
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' => 'Suspended 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 suspend subscription.' );
}
$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 reactivate subscription.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 204 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
}
/**
* Cancels a Subscription.
*
* @param string $id Subscription ID.
*
* @return void
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function cancel( string $id ): void {
$data = array(
'reason' => 'Cancelled by customer',
);
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id . '/cancel';
$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 cancel subscription.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 204 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
}
/**
* Returns a Subscription object from the given ID.
*
* @param string $id Subscription ID.
*
* @return stdClass
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function subscription( string $id ): stdClass {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing/subscriptions/' . $id;
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
throw new RuntimeException( 'Not able to get subscription.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $json;
}
}

View file

@ -0,0 +1,199 @@
<?php
/**
* The Catalog Products 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\Entity\Product;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ProductFactory;
/**
* Class CatalogProduct
*/
class CatalogProducts {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* Product factory.
*
* @var ProductFactory
*/
private $product_factory;
/**
* CatalogProducts constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param ProductFactory $product_factory Product factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
ProductFactory $product_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->product_factory = $product_factory;
$this->logger = $logger;
}
/**
* Creates a product.
*
* @param string $name Product name.
* @param string $description Product description.
*
* @return Product
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function create( string $name, string $description ): Product {
$data = array(
'name' => $name,
);
if ( $description ) {
$data['description'] = $description;
}
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/catalogs/products';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
'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 product.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $this->product_factory->from_paypal_response( $json );
}
/**
* Updates a Product.
*
* @param string $id Product ID.
* @param array $data Data to update.
*
* @return void
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function update( string $id, array $data ): void {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id;
$args = array(
'method' => 'PATCH',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
'body' => wp_json_encode( $data ),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
throw new RuntimeException( 'Not able to update product.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 204 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
}
/**
* Return a Product from the given ID.
*
* @param string $id Product ID.
*
* @return Product
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function product( string $id ): Product {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/catalogs/products/' . $id;
$args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
'Prefer' => 'return=representation',
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
throw new RuntimeException( 'Not able to get product.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 200 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $this->product_factory->from_paypal_response( $json );
}
}

View file

@ -103,7 +103,8 @@ class IdentityToken {
);
if (
( $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ) )
&& defined( 'PPCP_FLAG_SUBSCRIPTION' ) && PPCP_FLAG_SUBSCRIPTION
|| ( $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ) )
|| ( $this->settings->has( 'subscriptions_mode' ) && $this->settings->get( 'subscriptions_mode' ) === 'vaulting_api' )
) {
$customer_id = $this->customer_repository->customer_id_for_user( ( $user_id ) );
update_user_meta( $user_id, 'ppcp_customer_id', $customer_id );

View file

@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PatchCollection;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentMethod;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
@ -180,6 +181,8 @@ class OrderEndpoint {
* @param Payer|null $payer The payer off the order.
* @param PaymentToken|null $payment_token The payment token.
* @param PaymentMethod|null $payment_method The payment method.
* @param string $paypal_request_id The paypal request id.
* @param string $user_action The user action.
*
* @return Order
* @throws RuntimeException If the request fails.
@ -189,19 +192,28 @@ class OrderEndpoint {
string $shipping_preference,
Payer $payer = null,
PaymentToken $payment_token = null,
PaymentMethod $payment_method = null
PaymentMethod $payment_method = null,
string $paypal_request_id = '',
string $user_action = ApplicationContext::USER_ACTION_CONTINUE
): Order {
$bearer = $this->bearer->bearer();
$data = array(
'intent' => ( $this->subscription_helper->cart_contains_subscription() || $this->subscription_helper->current_product_is_subscription() ) ? 'AUTHORIZE' : $this->intent,
'purchase_units' => array_map(
static function ( PurchaseUnit $item ): array {
return $item->to_array();
static function ( PurchaseUnit $item ) use ( $shipping_preference ): array {
$data = $item->to_array();
if ( $shipping_preference !== ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE ) {
// Shipping options are not allowed to be sent when not getting the address from PayPal.
unset( $data['shipping']['options'] );
}
return $data;
},
$items
),
'application_context' => $this->application_context_repository
->current_context( $shipping_preference )->to_array(),
->current_context( $shipping_preference, $user_action )->to_array(),
);
if ( $payer && ! empty( $payer->email_address() ) ) {
$data['payer'] = $payer->to_array();
@ -512,13 +524,22 @@ class OrderEndpoint {
return $order_to_update;
}
$this->patch( $order_to_update->id(), $patches );
$new_order = $this->order( $order_to_update->id() );
return $new_order;
}
/**
* Patches an order.
*
* @param string $order_id The PayPal order ID.
* @param PatchCollection $patches The patches.
*
* @throws RuntimeException If the request fails.
*/
public function patch( string $order_id, PatchCollection $patches ): void {
$patches_array = $patches->to_array();
if ( ! isset( $patches_array[0]['value']['shipping'] ) ) {
$shipping = isset( $order_to_update->purchase_units()[0] ) && null !== $order_to_update->purchase_units()[0]->shipping() ? $order_to_update->purchase_units()[0]->shipping() : null;
if ( $shipping ) {
$patches_array[0]['value']['shipping'] = $shipping->to_array();
}
}
/**
* The filter can be used to modify the order patching request body data (the final prices, items).
@ -526,7 +547,7 @@ class OrderEndpoint {
$patches_array = apply_filters( 'ppcp_patch_order_request_body_data', $patches_array );
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_to_update->id();
$url = trailingslashit( $this->host ) . 'v2/checkout/orders/' . $order_id;
$args = array(
'method' => 'PATCH',
'headers' => array(
@ -542,11 +563,8 @@ class OrderEndpoint {
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not retrieve order.', 'woocommerce-paypal-payments' )
);
$this->logger->log(
'warning',
$error = new RuntimeException( 'Could not patch order.' );
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -562,8 +580,7 @@ class OrderEndpoint {
$json,
$status_code
);
$this->logger->log(
'warning',
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -572,9 +589,6 @@ class OrderEndpoint {
);
throw $error;
}
$new_order = $this->order( $order_to_update->id() );
return $new_order;
}
/**

View file

@ -0,0 +1,134 @@
<?php
/**
* The Billing Cycle object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class BillingCycle
*/
class BillingCycle {
/**
* Frequency.
*
* @var array
*/
private $frequency;
/**
* Sequence.
*
* @var int
*/
private $sequence;
/**
* Tenure Type.
*
* @var string
*/
private $tenure_type;
/**
* Pricing scheme.
*
* @var array
*/
private $pricing_scheme;
/**
* Total cycles.
*
* @var int
*/
private $total_cycles;
/**
* BillingCycle constructor.
*
* @param array $frequency Frequency.
* @param int $sequence Sequence.
* @param string $tenure_type Tenure type.
* @param array $pricing_scheme Pricing scheme.
* @param int $total_cycles Total cycles.
*/
public function __construct(
array $frequency,
int $sequence,
string $tenure_type,
array $pricing_scheme = array(),
int $total_cycles = 1
) {
$this->frequency = $frequency;
$this->sequence = $sequence;
$this->tenure_type = $tenure_type;
$this->pricing_scheme = $pricing_scheme;
$this->total_cycles = $total_cycles;
}
/**
* Returns frequency.
*
* @return array
*/
public function frequency(): array {
return $this->frequency;
}
/**
* Returns sequence.
*
* @return int
*/
public function sequence(): int {
return $this->sequence;
}
/**
* Returns tenure type.
*
* @return string
*/
public function tenure_type(): string {
return $this->tenure_type;
}
/**
* Returns pricing scheme.
*
* @return array
*/
public function pricing_scheme(): array {
return $this->pricing_scheme;
}
/**
* Return total cycles.
*
* @return int
*/
public function total_cycles(): int {
return $this->total_cycles;
}
/**
* Returns Billing Cycle as array.
*
* @return array
*/
public function to_array(): array {
return array(
'frequency' => $this->frequency(),
'sequence' => $this->sequence(),
'tenure_type' => $this->tenure_type(),
'pricing_scheme' => $this->pricing_scheme(),
'total_cycles' => $this->total_cycles(),
);
}
}

View file

@ -83,8 +83,8 @@ class Patch {
public function to_array(): array {
return array(
'op' => $this->op(),
'value' => $this->value(),
'path' => $this->path(),
'value' => $this->value(),
);
}

View file

@ -0,0 +1,115 @@
<?php
/**
* The Payment Preferences object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class PaymentPreferences
*/
class PaymentPreferences {
/**
* Setup fee.
*
* @var array
*/
private $setup_fee;
/**
* Auto bill outstanding.
*
* @var bool
*/
private $auto_bill_outstanding;
/**
* Setup fee failure action.
*
* @var string
*/
private $setup_fee_failure_action;
/**
* Payment failure threshold.
*
* @var int
*/
private $payment_failure_threshold;
/**
* PaymentPreferences constructor.
*
* @param array $setup_fee Setup fee.
* @param bool $auto_bill_outstanding Auto bill outstanding.
* @param string $setup_fee_failure_action Setup fee failure action.
* @param int $payment_failure_threshold payment failure threshold.
*/
public function __construct(
array $setup_fee,
bool $auto_bill_outstanding = true,
string $setup_fee_failure_action = 'CONTINUE',
int $payment_failure_threshold = 3
) {
$this->setup_fee = $setup_fee;
$this->auto_bill_outstanding = $auto_bill_outstanding;
$this->setup_fee_failure_action = $setup_fee_failure_action;
$this->payment_failure_threshold = $payment_failure_threshold;
}
/**
* Setup fee.
*
* @return array
*/
public function setup_fee(): array {
return $this->setup_fee;
}
/**
* Auto bill outstanding.
*
* @return bool
*/
public function auto_bill_outstanding(): bool {
return $this->auto_bill_outstanding;
}
/**
* Setup fee failure action.
*
* @return string
*/
public function setup_fee_failure_action(): string {
return $this->setup_fee_failure_action;
}
/**
* Payment failure threshold.
*
* @return int
*/
public function payment_failure_threshold(): int {
return $this->payment_failure_threshold;
}
/**
* Returns Payment Preferences as array.
*
* @return array
*/
public function to_array():array {
return array(
'setup_fee' => $this->setup_fee(),
'auto_bill_outstanding' => $this->auto_bill_outstanding(),
'setup_fee_failure_action' => $this->setup_fee_failure_action(),
'payment_failure_threshold' => $this->payment_failure_threshold(),
);
}
}

View file

@ -0,0 +1,154 @@
<?php
/**
* The Plan object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class Plan
*/
class Plan {
/**
* Plan ID.
*
* @var string
*/
private $id;
/**
* Plan name.
*
* @var string
*/
private $name;
/**
* Product ID.
*
* @var string
*/
private $product_id;
/**
* Billing cycles.
*
* @var array
*/
private $billing_cycles;
/**
* Payment preferences.
*
* @var PaymentPreferences
*/
private $payment_preferences;
/**
* Plan status.
*
* @var string
*/
private $status;
/**
* Plan constructor.
*
* @param string $id Plan ID.
* @param string $name Plan name.
* @param string $product_id Product ID.
* @param array $billing_cycles Billing cycles.
* @param PaymentPreferences $payment_preferences Payment preferences.
* @param string $status Plan status.
*/
public function __construct(
string $id,
string $name,
string $product_id,
array $billing_cycles,
PaymentPreferences $payment_preferences,
string $status = ''
) {
$this->id = $id;
$this->name = $name;
$this->product_id = $product_id;
$this->billing_cycles = $billing_cycles;
$this->payment_preferences = $payment_preferences;
$this->status = $status;
}
/**
* Returns Plan ID.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns Plan name.
*
* @return string
*/
public function name(): string {
return $this->name;
}
/**
* Returns Product ID.
*
* @return string
*/
public function product_id(): string {
return $this->product_id;
}
/**
* Returns Billing cycles.
*
* @return array
*/
public function billing_cycles(): array {
return $this->billing_cycles;
}
/**
* Returns Payment preferences.
*
* @return PaymentPreferences
*/
public function payment_preferences(): PaymentPreferences {
return $this->payment_preferences;
}
/**
* Returns Plan status.
*
* @return string
*/
public function status(): string {
return $this->status;
}
/**
* Returns Plan as array.
*
* @return array
*/
public function to_array():array {
return array(
'id' => $this->id(),
'name' => $this->name(),
'product_id' => $this->product_id(),
'billing_cycles' => $this->billing_cycles(),
'payment_preferences' => $this->payment_preferences(),
'status' => $this->status(),
);
}
}

View file

@ -0,0 +1,90 @@
<?php
/**
* The Product object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class Product
*/
class Product {
/**
* Product ID.
*
* @var string
*/
private $id;
/**
* Product name.
*
* @var string
*/
private $name;
/**
* Product description.
*
* @var string
*/
private $description;
/**
* Product constructor.
*
* @param string $id Product ID.
* @param string $name Product name.
* @param string $description Product description.
*/
public function __construct( string $id, string $name, string $description = '' ) {
$this->id = $id;
$this->name = $name;
$this->description = $description;
}
/**
* Returns the product ID.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* Returns the product name.
*
* @return string
*/
public function name(): string {
return $this->name;
}
/**
* Returns the product description.
*
* @return string
*/
public function description(): string {
return $this->description;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array() {
return array(
'id' => $this->id(),
'name' => $this->name(),
'description' => $this->description(),
);
}
}

View file

@ -28,15 +28,24 @@ class Shipping {
*/
private $address;
/**
* Shipping methods.
*
* @var ShippingOption[]
*/
private $options;
/**
* Shipping constructor.
*
* @param string $name The name.
* @param Address $address The address.
* @param string $name The name.
* @param Address $address The address.
* @param ShippingOption[] $options Shipping methods.
*/
public function __construct( string $name, Address $address ) {
public function __construct( string $name, Address $address, array $options = array() ) {
$this->name = $name;
$this->address = $address;
$this->options = $options;
}
/**
@ -57,17 +66,35 @@ class Shipping {
return $this->address;
}
/**
* Returns the shipping methods.
*
* @return ShippingOption[]
*/
public function options(): array {
return $this->options;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
$result = array(
'name' => array(
'full_name' => $this->name(),
),
'address' => $this->address()->to_array(),
);
if ( $this->options ) {
$result['options'] = array_map(
function ( ShippingOption $opt ): array {
return $opt->to_array();
},
$this->options
);
}
return $result;
}
}

View file

@ -0,0 +1,139 @@
<?php
/**
* The ShippingOption object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class ShippingOption
*/
class ShippingOption {
const TYPE_SHIPPING = 'SHIPPING';
const TYPE_PICKUP = 'PICKUP';
/**
* The name.
*
* @var string
*/
private $id;
/**
* The label.
*
* @var string
*/
private $label;
/**
* Whether the method is selected by default.
*
* @var bool
*/
private $selected;
/**
* The price.
*
* @var Money
*/
private $amount;
/**
* SHIPPING or PICKUP.
*
* @var string
*/
private $type;
/**
* ShippingOption constructor.
*
* @param string $id The name.
* @param string $label The label.
* @param bool $selected Whether the method is selected by default.
* @param Money $amount The price.
* @param string $type SHIPPING or PICKUP.
*/
public function __construct( string $id, string $label, bool $selected, Money $amount, string $type ) {
$this->id = $id;
$this->label = $label;
$this->selected = $selected;
$this->amount = $amount;
$this->type = $type;
}
/**
* The name.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* The label.
*
* @return string
*/
public function label(): string {
return $this->label;
}
/**
* Whether the method is selected by default.
*
* @return bool
*/
public function selected(): bool {
return $this->selected;
}
/**
* Sets whether the method is selected by default.
*
* @param bool $selected The value to be set.
*/
public function set_selected( bool $selected ): void {
$this->selected = $selected;
}
/**
* The price.
*
* @return Money
*/
public function amount(): Money {
return $this->amount;
}
/**
* SHIPPING or PICKUP.
*
* @return string
*/
public function type(): string {
return $this->type;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
return array(
'id' => $this->id,
'label' => $this->label,
'selected' => $this->selected,
'amount' => $this->amount->to_array(),
'type' => $this->type,
);
}
}

View file

@ -0,0 +1,84 @@
<?php
/**
* The Billing Cycle factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WC_Product;
use WooCommerce\PayPalCommerce\ApiClient\Entity\BillingCycle;
/**
* Class BillingCycleFactory
*/
class BillingCycleFactory {
/**
* The currency.
*
* @var string
*/
private $currency;
/**
* BillingCycleFactory constructor.
*
* @param string $currency The currency.
*/
public function __construct( string $currency ) {
$this->currency = $currency;
}
/**
* Returns a BillingCycle object from the given WC product.
*
* @param WC_Product $product WC product.
* @return BillingCycle
*/
public function from_wc_product( WC_Product $product ): BillingCycle {
return new BillingCycle(
array(
'interval_unit' => $product->get_meta( '_subscription_period' ),
'interval_count' => $product->get_meta( '_subscription_period_interval' ),
),
1,
'REGULAR',
array(
'fixed_price' => array(
'value' => $product->get_meta( '_subscription_price' ),
'currency_code' => $this->currency,
),
),
(int) $product->get_meta( '_subscription_length' )
);
}
/**
* Returns a BillingCycle object based off a PayPal response.
*
* @param stdClass $data the data.
* @return BillingCycle
*/
public function from_paypal_response( stdClass $data ): BillingCycle {
return new BillingCycle(
array(
'interval_unit' => $data->frequency->interval_unit,
'interval_count' => $data->frequency->interval_count,
),
$data->sequence,
$data->tenure_type,
array(
'fixed_price' => array(
'value' => $data->pricing_scheme->fixed_price->value,
'currency_code' => $data->pricing_scheme->fixed_price->currency_code,
),
),
$data->total_cycles
);
}
}

View file

@ -71,7 +71,15 @@ class PatchCollectionFactory {
);
$operation = $purchase_unit_from ? 'replace' : 'add';
$value = $purchase_unit_to->to_array();
$patches[] = new Patch(
if ( ! isset( $value['shipping'] ) ) {
$shipping = $purchase_unit_from && null !== $purchase_unit_from->shipping() ? $purchase_unit_from->shipping() : null;
if ( $shipping ) {
$value['shipping'] = $shipping->to_array();
}
}
$patches[] = new Patch(
$operation,
$path . "/@reference_id=='" . $purchase_unit_to->reference_id() . "'",
$value

View file

@ -0,0 +1,69 @@
<?php
/**
* The Payment Preferences factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WC_Product;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentPreferences;
/**
* Class PaymentPreferencesFactory
*/
class PaymentPreferencesFactory {
/**
* The currency.
*
* @var string
*/
private $currency;
/**
* PaymentPreferencesFactory constructor.
*
* @param string $currency The currency.
*/
public function __construct( string $currency ) {
$this->currency = $currency;
}
/**
* Returns a PaymentPreferences object from the given WC product.
*
* @param WC_Product $product WC product.
* @return PaymentPreferences
*/
public function from_wc_product( WC_Product $product ):PaymentPreferences {
return new PaymentPreferences(
array(
'value' => $product->get_meta( '_subscription_sign_up_fee' ) ?: '0',
'currency_code' => $this->currency,
)
);
}
/**
* Returns a PaymentPreferences object based off a PayPal response.
*
* @param stdClass $data The data.
* @return PaymentPreferences
*/
public function from_paypal_response( stdClass $data ) {
return new PaymentPreferences(
array(
'value' => $data->setup_fee->value,
'currency_code' => $data->setup_fee->currency_code,
),
$data->auto_bill_outstanding,
$data->setup_fee_failure_action,
$data->payment_failure_threshold
);
}
}

View file

@ -0,0 +1,96 @@
<?php
/**
* Plan Factory.
*
* @package WooCommerce\PayPalCommerce\Webhooks\Handler
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Plan;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class PlanFactory
*/
class PlanFactory {
/**
* Billing cycle factory.
*
* @var BillingCycleFactory
*/
private $billing_cycle_factory;
/**
* Payment preferences factory.
*
* @var PaymentPreferencesFactory
*/
private $payment_preferences_factory;
/**
* PlanFactory constructor.
*
* @param BillingCycleFactory $billing_cycle_factory Billing cycle factory.
* @param PaymentPreferencesFactory $payment_preferences_factory Payment preferences factory.
*/
public function __construct(
BillingCycleFactory $billing_cycle_factory,
PaymentPreferencesFactory $payment_preferences_factory
) {
$this->billing_cycle_factory = $billing_cycle_factory;
$this->payment_preferences_factory = $payment_preferences_factory;
}
/**
* Returns a Plan from PayPal response.
*
* @param stdClass $data The data.
*
* @return Plan
*
* @throws RuntimeException If it could not create Plan.
*/
public function from_paypal_response( stdClass $data ): Plan {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'No id for given plan', 'woocommerce-paypal-payments' )
);
}
if ( ! isset( $data->name ) ) {
throw new RuntimeException(
__( 'No name for plan given', 'woocommerce-paypal-payments' )
);
}
if ( ! isset( $data->product_id ) ) {
throw new RuntimeException(
__( 'No product id for given plan', 'woocommerce-paypal-payments' )
);
}
if ( ! isset( $data->billing_cycles ) ) {
throw new RuntimeException(
__( 'No billing cycles for given plan', 'woocommerce-paypal-payments' )
);
}
$billing_cycles = array();
foreach ( $data->billing_cycles as $billing_cycle ) {
$billing_cycles[] = $this->billing_cycle_factory->from_paypal_response( $billing_cycle );
}
$payment_preferences = $this->payment_preferences_factory->from_paypal_response( $data->payment_preferences );
return new Plan(
$data->id,
$data->name,
$data->product_id,
$billing_cycles,
$payment_preferences,
$data->status ?? ''
);
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* The Product factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Product;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ProductFactory
*/
class ProductFactory {
/**
* Creates a Product based off a PayPal response.
*
* @param stdClass $data The JSON object.
*
* @return Product
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( stdClass $data ): Product {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'No id for product given', 'woocommerce-paypal-payments' )
);
}
if ( ! isset( $data->name ) ) {
throw new RuntimeException(
__( 'No name for product given', 'woocommerce-paypal-payments' )
);
}
return new Product(
$data->id,
$data->name,
$data->description ?? ''
);
}
}

View file

@ -153,10 +153,11 @@ class PurchaseUnitFactory {
* Creates a PurchaseUnit based off a WooCommerce cart.
*
* @param \WC_Cart|null $cart The cart.
* @param bool $with_shipping_options Include WC shipping methods.
*
* @return PurchaseUnit
*/
public function from_wc_cart( ?\WC_Cart $cart = null ): PurchaseUnit {
public function from_wc_cart( ?\WC_Cart $cart = null, bool $with_shipping_options = false ): PurchaseUnit {
if ( ! $cart ) {
$cart = WC()->cart ?? new \WC_Cart();
}
@ -172,7 +173,7 @@ class PurchaseUnitFactory {
$shipping = null;
$customer = \WC()->customer;
if ( $this->shipping_needed( ... array_values( $items ) ) && is_a( $customer, \WC_Customer::class ) ) {
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer );
$shipping = $this->shipping_factory->from_wc_customer( \WC()->customer, $with_shipping_options );
if (
2 !== strlen( $shipping->address()->country_code() ) ||
( ! $shipping->address()->postal_code() && ! $this->country_without_postal_code( $shipping->address()->country_code() ) )

View file

@ -10,6 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
@ -24,23 +25,33 @@ class ShippingFactory {
*/
private $address_factory;
/**
* The shipping option factory.
*
* @var ShippingOptionFactory
*/
private $shipping_option_factory;
/**
* ShippingFactory constructor.
*
* @param AddressFactory $address_factory The address factory.
* @param AddressFactory $address_factory The address factory.
* @param ShippingOptionFactory $shipping_option_factory The shipping option factory.
*/
public function __construct( AddressFactory $address_factory ) {
$this->address_factory = $address_factory;
public function __construct( AddressFactory $address_factory, ShippingOptionFactory $shipping_option_factory ) {
$this->address_factory = $address_factory;
$this->shipping_option_factory = $shipping_option_factory;
}
/**
* Creates a shipping object based off a WooCommerce customer.
*
* @param \WC_Customer $customer The WooCommerce customer.
* @param bool $with_shipping_options Include WC shipping methods.
*
* @return Shipping
*/
public function from_wc_customer( \WC_Customer $customer ): Shipping {
public function from_wc_customer( \WC_Customer $customer, bool $with_shipping_options = false ): Shipping {
// Replicates the Behavior of \WC_Order::get_formatted_shipping_full_name().
$full_name = sprintf(
// translators: %1$s is the first name and %2$s is the second name. wc translation.
@ -51,7 +62,8 @@ class ShippingFactory {
$address = $this->address_factory->from_wc_customer( $customer );
return new Shipping(
$full_name,
$address
$address,
$with_shipping_options ? $this->shipping_option_factory->from_wc_cart() : array()
);
}
@ -91,9 +103,14 @@ class ShippingFactory {
);
}
$address = $this->address_factory->from_paypal_response( $data->address );
$options = array_map(
array( $this->shipping_option_factory, 'from_paypal_response' ),
$data->options ?? array()
);
return new Shipping(
$data->name->full_name,
$address
$address,
$options
);
}
}

View file

@ -0,0 +1,111 @@
<?php
/**
* The shipping options factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use stdClass;
use WC_Cart;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ShippingOptionFactory
*/
class ShippingOptionFactory {
/**
* The Money factory.
*
* @var MoneyFactory
*/
private $money_factory;
/**
* ShippingOptionFactory constructor.
*
* @param MoneyFactory $money_factory The Money factory.
*/
public function __construct( MoneyFactory $money_factory ) {
$this->money_factory = $money_factory;
}
/**
* Creates an array of ShippingOption objects for the shipping methods available in the cart.
*
* @param WC_Cart|null $cart The cart.
* @return ShippingOption[]
*/
public function from_wc_cart( ?WC_Cart $cart = null ): array {
if ( ! $cart ) {
$cart = WC()->cart ?? new WC_Cart();
}
$cart->calculate_shipping();
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods', array() );
if ( ! is_array( $chosen_shipping_methods ) ) {
$chosen_shipping_methods = array();
}
$packages = WC()->shipping()->get_packages();
$options = array();
foreach ( $packages as $package ) {
$rates = $package['rates'] ?? array();
foreach ( $rates as $rate ) {
if ( ! $rate instanceof \WC_Shipping_Rate ) {
continue;
}
$options[] = new ShippingOption(
$rate->get_id(),
$rate->get_label(),
in_array( $rate->get_id(), $chosen_shipping_methods, true ),
new Money(
(float) $rate->get_cost(),
get_woocommerce_currency()
),
ShippingOption::TYPE_SHIPPING
);
}
}
if ( ! $chosen_shipping_methods && $options ) {
$options[0]->set_selected( true );
}
return $options;
}
/**
* Creates a ShippingOption object from the PayPal JSON object.
*
* @param stdClass $data The JSON object.
*
* @return ShippingOption
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( stdClass $data ): ShippingOption {
if ( ! isset( $data->id ) ) {
throw new RuntimeException( 'No id was given for shipping option.' );
}
if ( ! isset( $data->amount ) ) {
throw new RuntimeException( 'Shipping option amount not found' );
}
$amount = $this->money_factory->from_paypal_response( $data->amount );
return new ShippingOption(
$data->id,
$data->label ?? '',
isset( $data->selected ) ? (bool) $data->selected : false,
$amount,
$data->type ?? ShippingOption::TYPE_SHIPPING
);
}
}

View file

@ -38,11 +38,13 @@ class ApplicationContextRepository {
* Returns the current application context.
*
* @param string $shipping_preferences The shipping preferences.
* @param string $user_action The user action.
*
* @return ApplicationContext
*/
public function current_context(
string $shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING
string $shipping_preferences = ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING,
string $user_action = ApplicationContext::USER_ACTION_CONTINUE
): ApplicationContext {
$brand_name = $this->settings->has( 'brand_name' ) ? $this->settings->get( 'brand_name' ) : '';
@ -55,7 +57,8 @@ class ApplicationContextRepository {
(string) $brand_name,
$locale,
(string) $landingpage,
$shipping_preferences
$shipping_preferences,
$user_action
);
return $context;
}