Merge pull request #3431 from woocommerce/PCP-4655-experience-context

Use experience_context
This commit is contained in:
Emili Castells 2025-06-02 11:46:43 +02:00 committed by GitHub
commit 12df58b31c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 1184 additions and 1042 deletions

View file

@ -0,0 +1,22 @@
<?php
/**
* The factories of the API client.
*
* @package WooCommerce\PayPalCommerce\ApiClient
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ExperienceContextBuilder;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
'wcgateway.builder.experience-context' => static function ( ContainerInterface $container ): ExperienceContextBuilder {
return new ExperienceContextBuilder(
$container->get( 'wcgateway.settings' )
);
},
);

View file

@ -47,7 +47,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokenEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AddressFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AmountFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ApplicationContextFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\AuthorizationFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\CaptureFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ExchangeRateFactory;
@ -74,7 +73,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\ApiClient\Helper\PurchaseUnitSanitizer;
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
use WooCommerce\PayPalCommerce\ApiClient\Repository\CustomerRepository;
use WooCommerce\PayPalCommerce\ApiClient\Repository\OrderRepository;
use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData;
@ -254,7 +252,6 @@ return array(
assert( $settings instanceof Settings );
$intent = $settings->has( 'intent' ) && strtoupper( (string) $settings->get( 'intent' ) ) === 'AUTHORIZE' ? 'AUTHORIZE' : 'CAPTURE';
$application_context_repository = $container->get( 'api.repository.application-context' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
return new OrderEndpoint(
$container->get( 'api.host' ),
@ -263,7 +260,6 @@ return array(
$patch_collection_factory,
$intent,
$logger,
$application_context_repository,
$subscription_helper,
$container->get( 'wcgateway.is-fraudnet-enabled' ),
$container->get( 'wcgateway.fraudnet' ),
@ -315,11 +311,6 @@ return array(
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'api.repository.application-context' => static function( ContainerInterface $container ) : ApplicationContextRepository {
$settings = $container->get( 'wcgateway.settings' );
return new ApplicationContextRepository( $settings );
},
'api.repository.partner-referrals-data' => static function ( ContainerInterface $container ) : PartnerReferralsData {
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
@ -339,9 +330,6 @@ return array(
$container->get( 'api.endpoint.order' )
);
},
'api.factory.application-context' => static function ( ContainerInterface $container ) : ApplicationContextFactory {
return new ApplicationContextFactory();
},
'api.factory.payment-token' => static function ( ContainerInterface $container ) : PaymentTokenFactory {
return new PaymentTokenFactory();
},
@ -440,13 +428,9 @@ return array(
'api.factory.order' => static function ( ContainerInterface $container ): OrderFactory {
$purchase_unit_factory = $container->get( 'api.factory.purchase-unit' );
$payer_factory = $container->get( 'api.factory.payer' );
$application_context_repository = $container->get( 'api.repository.application-context' );
$application_context_factory = $container->get( 'api.factory.application-context' );
return new OrderFactory(
$purchase_unit_factory,
$payer_factory,
$application_context_repository,
$application_context_factory
$payer_factory
);
},
'api.factory.payments' => static function ( ContainerInterface $container ): PaymentsFactory {

View file

@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
use WooCommerce\PayPalCommerce\ApiClient\Helper\PartnerAttribution;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\FactoryModule;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
@ -28,7 +29,7 @@ use Psr\Log\LoggerInterface;
/**
* Class ApiModule
*/
class ApiModule implements ServiceModule, ExtendingModule, ExecutableModule {
class ApiModule implements ServiceModule, FactoryModule, ExtendingModule, ExecutableModule {
use ModuleClassNameIdTrait;
/**
@ -38,6 +39,13 @@ class ApiModule implements ServiceModule, ExtendingModule, ExecutableModule {
return require __DIR__ . '/../services.php';
}
/**
* {@inheritDoc}
*/
public function factories(): array {
return require __DIR__ . '/../factories.php';
}
/**
* {@inheritDoc}
*/

View file

@ -11,22 +11,20 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Entity\AuthorizationStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\CaptureStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext;
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\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PatchCollectionFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\ErrorResponse;
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
use WooCommerce\PayPalCommerce\WcGateway\FraudNet\FraudNet;
@ -88,13 +86,6 @@ class OrderEndpoint {
*/
private $logger;
/**
* The application context repository.
*
* @var ApplicationContextRepository
*/
private $application_context_repository;
/**
* True if FraudNet support is enabled in settings, otherwise false.
*
@ -119,17 +110,16 @@ class OrderEndpoint {
/**
* OrderEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param OrderFactory $order_factory The order factory.
* @param PatchCollectionFactory $patch_collection_factory The patch collection factory.
* @param string $intent The intent.
* @param LoggerInterface $logger The logger.
* @param ApplicationContextRepository $application_context_repository The application context repository.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
* @param FraudNet $fraudnet The FraudNet entity.
* @param string $bn_code The BN Code.
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param OrderFactory $order_factory The order factory.
* @param PatchCollectionFactory $patch_collection_factory The patch collection factory.
* @param string $intent The intent.
* @param LoggerInterface $logger The logger.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param bool $is_fraudnet_enabled true if FraudNet support is enabled in settings, otherwise false.
* @param FraudNet $fraudnet The FraudNet entity.
* @param string $bn_code The BN Code.
*/
public function __construct(
string $host,
@ -138,24 +128,22 @@ class OrderEndpoint {
PatchCollectionFactory $patch_collection_factory,
string $intent,
LoggerInterface $logger,
ApplicationContextRepository $application_context_repository,
SubscriptionHelper $subscription_helper,
bool $is_fraudnet_enabled,
FraudNet $fraudnet,
string $bn_code = ''
) {
$this->host = $host;
$this->bearer = $bearer;
$this->order_factory = $order_factory;
$this->patch_collection_factory = $patch_collection_factory;
$this->intent = $intent;
$this->logger = $logger;
$this->application_context_repository = $application_context_repository;
$this->bn_code = $bn_code;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
$this->subscription_helper = $subscription_helper;
$this->fraudnet = $fraudnet;
$this->host = $host;
$this->bearer = $bearer;
$this->order_factory = $order_factory;
$this->patch_collection_factory = $patch_collection_factory;
$this->intent = $intent;
$this->logger = $logger;
$this->bn_code = $bn_code;
$this->is_fraudnet_enabled = $is_fraudnet_enabled;
$this->subscription_helper = $subscription_helper;
$this->fraudnet = $fraudnet;
}
/**
@ -176,11 +164,8 @@ class OrderEndpoint {
* Creates an order.
*
* @param PurchaseUnit[] $items The purchase unit items for the order.
* @param string $shipping_preference One of ApplicationContext::SHIPPING_PREFERENCE_ values.
* @param string $shipping_preference One of ExperienceContext::SHIPPING_PREFERENCE_ values.
* @param Payer|null $payer The payer off the order.
* @param PaymentToken|null $payment_token The payment token.
* @param string $paypal_request_id The PayPal request id.
* @param string $user_action The user action.
* @param string $payment_method WC payment method.
* @param array $request_data Request data.
* @param PaymentSource|null $payment_source The payment source.
@ -192,21 +177,18 @@ class OrderEndpoint {
array $items,
string $shipping_preference,
Payer $payer = null,
PaymentToken $payment_token = null,
string $paypal_request_id = '',
string $user_action = ApplicationContext::USER_ACTION_CONTINUE,
string $payment_method = '',
array $request_data = array(),
PaymentSource $payment_source = null
): Order {
$bearer = $this->bearer->bearer();
$data = array(
'intent' => apply_filters( 'woocommerce_paypal_payments_order_intent', $this->intent ),
'purchase_units' => array_map(
'intent' => apply_filters( 'woocommerce_paypal_payments_order_intent', $this->intent ),
'purchase_units' => array_map(
static function ( PurchaseUnit $item ) use ( $shipping_preference ): array {
$data = $item->to_array();
if ( $shipping_preference !== ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE ) {
if ( $shipping_preference !== ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE ) {
// Shipping options are not allowed to be sent when not getting the address from PayPal.
unset( $data['shipping']['options'] );
}
@ -215,15 +197,10 @@ class OrderEndpoint {
},
$items
),
'application_context' => $this->application_context_repository
->current_context( $shipping_preference, $user_action )->to_array(),
);
if ( $payer && ! empty( $payer->email_address() ) ) {
$data['payer'] = $payer->to_array();
}
if ( $payment_token ) {
$data['payment_source']['token'] = $payment_token->to_array();
}
if ( $payment_source ) {
$data['payment_source'] = array(
$payment_source->name() => $payment_source->properties(),
@ -259,11 +236,8 @@ class OrderEndpoint {
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
$error = new RuntimeException(
__( 'Could not create order.', 'woocommerce-paypal-payments' )
);
$this->logger->log(
'warning',
$error = new RuntimeException( 'Could not create order.' );
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -332,8 +306,7 @@ class OrderEndpoint {
$error = new RuntimeException(
__( 'Could not capture order.', 'woocommerce-paypal-payments' )
);
$this->logger->log(
'warning',
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -354,8 +327,7 @@ class OrderEndpoint {
if ( strpos( $response['body'], ErrorResponse::ORDER_ALREADY_CAPTURED ) !== false ) {
return $this->order( $order->id() );
}
$this->logger->log(
'warning',
$this->logger->warning(
sprintf(
'Failed to capture order. PayPal API response: %1$s',
$error->getMessage()
@ -409,8 +381,7 @@ class OrderEndpoint {
'woocommerce-paypal-payments'
)
);
$this->logger->log(
'warning',
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -429,8 +400,7 @@ class OrderEndpoint {
$json,
$status_code
);
$this->logger->log(
'warning',
$this->logger->warning(
sprintf(
'Failed to authorize order. PayPal API response: %1$s',
$error->getMessage()
@ -489,8 +459,7 @@ class OrderEndpoint {
__( 'Could not retrieve order.', 'woocommerce-paypal-payments' ),
404
);
$this->logger->log(
'warning',
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,
@ -505,8 +474,7 @@ class OrderEndpoint {
$json,
$status_code
);
$this->logger->log(
'warning',
$this->logger->warning(
$error->getMessage(),
array(
'args' => $args,

View file

@ -1,252 +0,0 @@
<?php
/**
* The application context object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class ApplicationContext
*/
class ApplicationContext {
const LANDING_PAGE_LOGIN = 'LOGIN';
const LANDING_PAGE_BILLING = 'BILLING';
const LANDING_PAGE_NO_PREFERENCE = 'NO_PREFERENCE';
const VALID_LANDING_PAGE_VALUES = array(
self::LANDING_PAGE_LOGIN,
self::LANDING_PAGE_BILLING,
self::LANDING_PAGE_NO_PREFERENCE,
);
const SHIPPING_PREFERENCE_GET_FROM_FILE = 'GET_FROM_FILE';
const SHIPPING_PREFERENCE_NO_SHIPPING = 'NO_SHIPPING';
const SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS = 'SET_PROVIDED_ADDRESS';
const VALID_SHIPPING_PREFERENCE_VALUES = array(
self::SHIPPING_PREFERENCE_GET_FROM_FILE,
self::SHIPPING_PREFERENCE_NO_SHIPPING,
self::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS,
);
const USER_ACTION_CONTINUE = 'CONTINUE';
const USER_ACTION_PAY_NOW = 'PAY_NOW';
const VALID_USER_ACTION_VALUES = array(
self::USER_ACTION_CONTINUE,
self::USER_ACTION_PAY_NOW,
);
const PAYMENT_METHOD_UNRESTRICTED = 'UNRESTRICTED';
const PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED = 'IMMEDIATE_PAYMENT_REQUIRED';
/**
* The brand name.
*
* @var string
*/
private $brand_name;
/**
* The locale.
*
* @var string
*/
private $locale;
/**
* The landing page.
*
* @var string
*/
private $landing_page;
/**
* The shipping preference.
*
* @var string
*/
private $shipping_preference;
/**
* The user action.
*
* @var string
*/
private $user_action;
/**
* The return url.
*
* @var string
*/
private $return_url;
/**
* The cancel url.
*
* @var string
*/
private $cancel_url;
/**
* The payment method preference.
*
* @var string
*/
private $payment_method_preference;
/**
* ApplicationContext constructor.
*
* @param string $return_url The return URL.
* @param string $cancel_url The cancel URL.
* @param string $brand_name The brand name.
* @param string $locale The locale.
* @param string $landing_page The landing page.
* @param string $shipping_preference The shipping preference.
* @param string $user_action The user action.
* @param string $payment_method_preference The payment method preference.
*
* @throws RuntimeException When values are not valid.
*/
public function __construct(
string $return_url = '',
string $cancel_url = '',
string $brand_name = '',
string $locale = '',
string $landing_page = self::LANDING_PAGE_NO_PREFERENCE,
string $shipping_preference = self::SHIPPING_PREFERENCE_NO_SHIPPING,
string $user_action = self::USER_ACTION_CONTINUE,
string $payment_method_preference = self::PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED
) {
if ( ! in_array( $landing_page, self::VALID_LANDING_PAGE_VALUES, true ) ) {
throw new RuntimeException( 'Landingpage not correct' );
}
if ( ! in_array( $shipping_preference, self::VALID_SHIPPING_PREFERENCE_VALUES, true ) ) {
throw new RuntimeException( 'Shipping preference not correct' );
}
if ( ! in_array( $user_action, self::VALID_USER_ACTION_VALUES, true ) ) {
throw new RuntimeException( 'User action preference not correct' );
}
$this->return_url = $return_url;
$this->cancel_url = $cancel_url;
$this->brand_name = $brand_name;
$this->locale = $locale;
$this->landing_page = $landing_page;
$this->shipping_preference = $shipping_preference;
$this->user_action = $user_action;
$this->payment_method_preference = $payment_method_preference;
}
/**
* Returns the brand name.
*
* @return string
*/
public function brand_name(): string {
return $this->brand_name;
}
/**
* Returns the locale.
*
* @return string
*/
public function locale(): string {
return $this->locale;
}
/**
* Returns the landing page.
*
* @return string
*/
public function landing_page(): string {
return $this->landing_page;
}
/**
* Returns the shipping preference.
*
* @return string
*/
public function shipping_preference(): string {
return $this->shipping_preference;
}
/**
* Returns the user action.
*
* @return string
*/
public function user_action(): string {
return $this->user_action;
}
/**
* Returns the return URL.
*
* @return string
*/
public function return_url(): string {
return $this->return_url;
}
/**
* Returns the cancel URL.
*
* @return string
*/
public function cancel_url(): string {
return $this->cancel_url;
}
/**
* Returns the payment method preference.
*/
public function payment_method_preference(): string {
return $this->payment_method_preference;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$data = array();
if ( $this->user_action() ) {
$data['user_action'] = $this->user_action();
}
if ( $this->shipping_preference() ) {
$data['shipping_preference'] = $this->shipping_preference();
}
if ( $this->landing_page() ) {
$data['landing_page'] = $this->landing_page();
}
if ( $this->locale() ) {
$data['locale'] = $this->locale();
}
if ( $this->brand_name() ) {
$data['brand_name'] = $this->brand_name();
}
if ( $this->return_url() ) {
$data['return_url'] = $this->return_url();
}
if ( $this->cancel_url() ) {
$data['cancel_url'] = $this->cancel_url();
}
if ( $this->payment_method_preference ) {
$data['payment_method'] = array(
'payee_preferred' => $this->payment_method_preference,
);
}
return $data;
}
}

View file

@ -0,0 +1,244 @@
<?php
/**
* The experience_context object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
use ReflectionClass;
/**
* Class ExperienceContext
*/
class ExperienceContext {
public const LANDING_PAGE_LOGIN = 'LOGIN';
public const LANDING_PAGE_GUEST_CHECKOUT = 'GUEST_CHECKOUT';
public const LANDING_PAGE_NO_PREFERENCE = 'NO_PREFERENCE';
public const SHIPPING_PREFERENCE_GET_FROM_FILE = 'GET_FROM_FILE';
public const SHIPPING_PREFERENCE_NO_SHIPPING = 'NO_SHIPPING';
public const SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS = 'SET_PROVIDED_ADDRESS';
public const USER_ACTION_CONTINUE = 'CONTINUE';
public const USER_ACTION_PAY_NOW = 'PAY_NOW';
public const PAYMENT_METHOD_UNRESTRICTED = 'UNRESTRICTED';
public const PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED = 'IMMEDIATE_PAYMENT_REQUIRED';
/**
* The return url.
*/
private ?string $return_url = null;
/**
* The cancel url.
*/
private ?string $cancel_url = null;
/**
* The brand name.
*/
private ?string $brand_name = null;
/**
* The locale.
*/
private ?string $locale = null;
/**
* The landing page.
*/
private ?string $landing_page = null;
/**
* The shipping preference.
*/
private ?string $shipping_preference = null;
/**
* The user action.
*/
private ?string $user_action = null;
/**
* The payment method preference.
*/
private ?string $payment_method_preference = null;
/**
* Returns the return URL.
*/
public function return_url(): ?string {
return $this->return_url;
}
/**
* Sets the return URL.
*
* @param string|null $new_value The value to set.
*/
public function with_return_url( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->return_url = $new_value;
return $obj;
}
/**
* Returns the cancel URL.
*/
public function cancel_url(): ?string {
return $this->cancel_url;
}
/**
* Sets the cancel URL.
*
* @param string|null $new_value The value to set.
*/
public function with_cancel_url( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->cancel_url = $new_value;
return $obj;
}
/**
* Returns the brand name.
*
* @return string
*/
public function brand_name(): ?string {
return $this->brand_name;
}
/**
* Sets the brand name.
*
* @param string|null $new_value The value to set.
*/
public function with_brand_name( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->brand_name = $new_value;
return $obj;
}
/**
* Returns the locale.
*/
public function locale(): ?string {
return $this->locale;
}
/**
* Sets the locale.
*
* @param string|null $new_value The value to set.
*/
public function with_locale( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->locale = $new_value;
return $obj;
}
/**
* Returns the landing page.
*/
public function landing_page(): ?string {
return $this->landing_page;
}
/**
* Sets the landing page.
*
* @param string|null $new_value The value to set.
*/
public function with_landing_page( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->landing_page = $new_value;
return $obj;
}
/**
* Returns the shipping preference.
*/
public function shipping_preference(): ?string {
return $this->shipping_preference;
}
/**
* Sets the shipping preference.
*
* @param string|null $new_value The value to set.
*/
public function with_shipping_preference( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->shipping_preference = $new_value;
return $obj;
}
/**
* Returns the user action.
*/
public function user_action(): ?string {
return $this->user_action;
}
/**
* Sets the user action.
*
* @param string|null $new_value The value to set.
*/
public function with_user_action( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->user_action = $new_value;
return $obj;
}
/**
* Returns the payment method preference.
*/
public function payment_method_preference(): ?string {
return $this->payment_method_preference;
}
/**
* Sets the payment method preference.
*
* @param string|null $new_value The value to set.
*/
public function with_payment_method_preference( ?string $new_value ): ExperienceContext {
$obj = clone $this;
$obj->payment_method_preference = $new_value;
return $obj;
}
/**
* Returns the object as array.
*/
public function to_array(): array {
$data = array();
$class = new ReflectionClass( $this );
foreach ( $class->getProperties() as $prop ) {
$value = $this->{$prop->getName()};
if ( $value === null ) {
continue;
}
$data[ $prop->getName() ] = $value;
}
return $data;
}
}

View file

@ -64,13 +64,6 @@ class Order {
*/
private $update_time;
/**
* The application context.
*
* @var ApplicationContext|null
*/
private $application_context;
/**
* The payment source.
*
@ -83,21 +76,19 @@ class Order {
*
* @see https://developer.paypal.com/docs/api/orders/v2/#orders-create-response
*
* @param string $id The ID.
* @param PurchaseUnit[] $purchase_units The purchase units.
* @param OrderStatus $order_status The order status.
* @param ApplicationContext|null $application_context The application context.
* @param PaymentSource|null $payment_source The payment source.
* @param Payer|null $payer The payer.
* @param string $intent The intent.
* @param \DateTime|null $create_time The create time.
* @param \DateTime|null $update_time The update time.
* @param string $id The ID.
* @param PurchaseUnit[] $purchase_units The purchase units.
* @param OrderStatus $order_status The order status.
* @param PaymentSource|null $payment_source The payment source.
* @param Payer|null $payer The payer.
* @param string $intent The intent.
* @param \DateTime|null $create_time The create time.
* @param \DateTime|null $update_time The update time.
*/
public function __construct(
string $id,
array $purchase_units,
OrderStatus $order_status,
ApplicationContext $application_context = null,
PaymentSource $payment_source = null,
Payer $payer = null,
string $intent = 'CAPTURE',
@ -105,15 +96,14 @@ class Order {
\DateTime $update_time = null
) {
$this->id = $id;
$this->application_context = $application_context;
$this->payer = $payer;
$this->order_status = $order_status;
$this->intent = ( 'CAPTURE' === $intent ) ? 'CAPTURE' : 'AUTHORIZE';
$this->purchase_units = $purchase_units;
$this->create_time = $create_time;
$this->update_time = $update_time;
$this->payment_source = $payment_source;
$this->id = $id;
$this->payer = $payer;
$this->order_status = $order_status;
$this->intent = ( 'CAPTURE' === $intent ) ? 'CAPTURE' : 'AUTHORIZE';
$this->purchase_units = $purchase_units;
$this->create_time = $create_time;
$this->update_time = $update_time;
$this->payment_source = $payment_source;
}
/**
@ -179,16 +169,6 @@ class Order {
return $this->order_status;
}
/**
* Returns the application context.
*
* @return ApplicationContext|null
*/
public function application_context() {
return $this->application_context;
}
/**
* Returns the payment source.
*
@ -225,9 +205,6 @@ class Order {
if ( $this->update_time() ) {
$order['update_time'] = $this->update_time()->format( 'Y-m-d\TH:i:sO' );
}
if ( $this->application_context() ) {
$order['application_context'] = $this->application_context()->to_array();
}
return $order;
}

View file

@ -99,6 +99,16 @@ class PaymentToken {
);
}
/**
* Returns the PaymentSource object.
*/
public function to_payment_source(): PaymentSource {
return new PaymentSource(
'token',
(object) $this->to_array()
);
}
/**
* Returns a list of valid token types.
* Can be modified through the `woocommerce_paypal_payments_valid_payment_token_types` filter.

View file

@ -1,44 +0,0 @@
<?php
/**
* The ApplicationContext factory.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
/**
* Class ApplicationContextFactory
*/
class ApplicationContextFactory {
/**
* Returns an Application Context based off a PayPal Response.
*
* @param \stdClass $data The JSON object.
*
* @return ApplicationContext
*/
public function from_paypal_response( \stdClass $data ): ApplicationContext {
return new ApplicationContext(
isset( $data->return_url ) ?
$data->return_url : '',
isset( $data->cancel_url ) ?
$data->cancel_url : '',
isset( $data->brand_name ) ?
$data->brand_name : '',
isset( $data->locale ) ?
$data->locale : '',
isset( $data->landing_page ) ?
$data->landing_page : ApplicationContext::LANDING_PAGE_NO_PREFERENCE,
isset( $data->shipping_preference ) ?
$data->shipping_preference : ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE,
isset( $data->user_action ) ?
$data->user_action : ApplicationContext::USER_ACTION_CONTINUE
);
}
}

View file

@ -0,0 +1,193 @@
<?php
/**
* The ExperienceContext builder.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WC_AJAX;
use WC_Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
/**
* Class ExperienceContextBuilder
*/
class ExperienceContextBuilder {
/**
* The object being built.
*/
private ExperienceContext $experience_context;
/**
* The Settings.
*/
private ContainerInterface $settings;
/**
* ExperienceContextBuilder constructor.
*
* @param ContainerInterface $settings The settings.
*/
public function __construct( ContainerInterface $settings ) {
$this->experience_context = new ExperienceContext();
$this->settings = $settings;
}
/**
* Uses the default config for the PayPal buttons.
*
* @param string $shipping_preference One of the ExperienceContext::SHIPPING_PREFERENCE_* values.
* @param string $user_action One of the ExperienceContext::USER_ACTION_* values.
*/
public function with_default_paypal_config(
string $shipping_preference = ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING,
string $user_action = ExperienceContext::USER_ACTION_CONTINUE
): ExperienceContextBuilder {
$builder = clone $this;
$builder = $builder
->with_current_locale()
->with_current_brand_name()
->with_current_landing_page()
->with_current_payment_method_preference()
->with_endpoint_return_urls();
$builder->experience_context = $builder->experience_context
->with_shipping_preference( $shipping_preference )
->with_user_action( $user_action );
return $builder;
}
/**
* Uses the ReturnUrlEndpoint return URL.
*/
public function with_endpoint_return_urls(): ExperienceContextBuilder {
$builder = clone $this;
$builder->experience_context = $builder->experience_context
->with_return_url( home_url( WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ) )
->with_cancel_url( wc_get_checkout_url() );
return $builder;
}
/**
* Uses the WC order return URLs.
*
* @param WC_Order $wc_order The WC order.
*/
public function with_order_return_urls( WC_Order $wc_order ): ExperienceContextBuilder {
$builder = clone $this;
$url = $wc_order->get_checkout_order_received_url();
$builder->experience_context = $builder->experience_context
->with_return_url( $url )
->with_cancel_url( add_query_arg( 'cancelled', 'true', $url ) );
return $builder;
}
/**
* Uses the current brand name from the settings.
*/
public function with_current_brand_name(): ExperienceContextBuilder {
$builder = clone $this;
$brand_name = $this->settings->has( 'brand_name' ) ? (string) $this->settings->get( 'brand_name' ) : '';
if ( empty( $brand_name ) ) {
$brand_name = null;
}
$builder->experience_context = $builder->experience_context
->with_brand_name( $brand_name );
return $builder;
}
/**
* Uses the current user locale.
*/
public function with_current_locale(): ExperienceContextBuilder {
$builder = clone $this;
$builder->experience_context = $builder->experience_context
->with_locale( $this->locale_to_bcp47( get_user_locale() ) );
return $builder;
}
/**
* Uses the current landing page from the settings.
*/
public function with_current_landing_page(): ExperienceContextBuilder {
$builder = clone $this;
$landing_page = $this->settings->has( 'landing_page' ) ?
(string) $this->settings->get( 'landing_page' )
: ExperienceContext::LANDING_PAGE_NO_PREFERENCE;
if ( empty( $landing_page ) ) {
$landing_page = ExperienceContext::LANDING_PAGE_NO_PREFERENCE;
}
$builder->experience_context = $builder->experience_context
->with_landing_page( $landing_page );
return $builder;
}
/**
* Uses the payment method preference from the settings.
*/
public function with_current_payment_method_preference(): ExperienceContextBuilder {
$builder = clone $this;
$builder->experience_context = $builder->experience_context
->with_payment_method_preference(
$this->settings->has( 'payee_preferred' ) && $this->settings->get( 'payee_preferred' ) ?
ExperienceContext::PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED
: ExperienceContext::PAYMENT_METHOD_UNRESTRICTED
);
return $builder;
}
/**
* Returns the ExperienceContext.
*/
public function build(): ExperienceContext {
return $this->experience_context;
}
/**
* Returns BCP-47 code supported by PayPal, for example de-DE-formal becomes de-DE.
*
* @param string $locale The locale, e.g. from get_user_locale.
*/
protected function locale_to_bcp47( string $locale ): string {
$locale = str_replace( '_', '-', $locale );
if ( preg_match( '/^[a-z]{2}(?:-[A-Z][a-z]{3})?(?:-(?:[A-Z]{2}))?$/', $locale ) ) {
return $locale;
}
$parts = explode( '-', $locale );
if ( count( $parts ) === 3 ) {
$ret = substr( $locale, 0, (int) strrpos( $locale, '-' ) );
if ( false !== $ret ) {
return $ret;
}
}
return 'en';
}
}

View file

@ -14,7 +14,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
/**
* Class OrderFactory
@ -35,39 +34,19 @@ class OrderFactory {
*/
private $payer_factory;
/**
* The ApplicationContext repository.
*
* @var ApplicationContextRepository
*/
private $application_context_repository;
/**
* The ApplicationContext factory.
*
* @var ApplicationContextFactory
*/
private $application_context_factory;
/**
* OrderFactory constructor.
*
* @param PurchaseUnitFactory $purchase_unit_factory The PurchaseUnit factory.
* @param PayerFactory $payer_factory The Payer factory.
* @param ApplicationContextRepository $application_context_repository The Application Context repository.
* @param ApplicationContextFactory $application_context_factory The Application Context factory.
* @param PurchaseUnitFactory $purchase_unit_factory The PurchaseUnit factory.
* @param PayerFactory $payer_factory The Payer factory.
*/
public function __construct(
PurchaseUnitFactory $purchase_unit_factory,
PayerFactory $payer_factory,
ApplicationContextRepository $application_context_repository,
ApplicationContextFactory $application_context_factory
PayerFactory $payer_factory
) {
$this->purchase_unit_factory = $purchase_unit_factory;
$this->payer_factory = $payer_factory;
$this->application_context_repository = $application_context_repository;
$this->application_context_factory = $application_context_factory;
$this->purchase_unit_factory = $purchase_unit_factory;
$this->payer_factory = $payer_factory;
}
/**
@ -85,7 +64,6 @@ class OrderFactory {
$order->id(),
$purchase_units,
$order->status(),
$order->application_context(),
$order->payment_source(),
$order->payer(),
$order->intent(),
@ -131,18 +109,15 @@ class OrderFactory {
$order_data->purchase_units
);
$create_time = ( isset( $order_data->create_time ) ) ?
$create_time = ( isset( $order_data->create_time ) ) ?
\DateTime::createFromFormat( 'Y-m-d\TH:i:sO', $order_data->create_time )
: null;
$update_time = ( isset( $order_data->update_time ) ) ?
$update_time = ( isset( $order_data->update_time ) ) ?
\DateTime::createFromFormat( 'Y-m-d\TH:i:sO', $order_data->update_time )
: null;
$payer = ( isset( $order_data->payer ) ) ?
$payer = ( isset( $order_data->payer ) ) ?
$this->payer_factory->from_paypal_response( $order_data->payer )
: null;
$application_context = ( isset( $order_data->application_context ) ) ?
$this->application_context_factory->from_paypal_response( $order_data->application_context )
: null;
$payment_source = null;
if ( isset( $order_data->payment_source ) ) {
@ -165,7 +140,6 @@ class OrderFactory {
$order_data->id,
$purchase_units,
new OrderStatus( $order_data->status ),
$application_context,
$payment_source,
$payer,
$order_data->intent,

View file

@ -10,7 +10,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use WC_Cart;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit;
/**
@ -35,7 +35,7 @@ class ShippingPreferenceFactory {
): string {
$contains_physical_goods = $purchase_unit->contains_physical_goods();
if ( ! $contains_physical_goods ) {
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
return ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING;
}
$has_shipping = null !== $purchase_unit->shipping();
@ -45,20 +45,20 @@ class ShippingPreferenceFactory {
if ( $shipping_address_is_fixed ) {
// Checkout + no address given? Probably something weird happened, like no form validation?
if ( ! $has_shipping ) {
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
return ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING;
}
return ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
return ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
}
if ( 'card' === $funding_source ) {
if ( ! $has_shipping ) {
return ApplicationContext::SHIPPING_PREFERENCE_NO_SHIPPING;
return ExperienceContext::SHIPPING_PREFERENCE_NO_SHIPPING;
}
// Looks like GET_FROM_FILE does not work for the vaulted card button.
return ApplicationContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
return ExperienceContext::SHIPPING_PREFERENCE_SET_PROVIDED_ADDRESS;
}
return ApplicationContext::SHIPPING_PREFERENCE_GET_FROM_FILE;
return ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE;
}
}

View file

@ -1,91 +0,0 @@
<?php
/**
* Returns the current application context.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Repository
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Repository;
use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext;
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
/**
* Class ApplicationContextRepository
*/
class ApplicationContextRepository {
/**
* The Settings.
*
* @var ContainerInterface
*/
private $settings;
/**
* ApplicationContextRepository constructor.
*
* @param ContainerInterface $settings The settings.
*/
public function __construct( ContainerInterface $settings ) {
$this->settings = $settings;
}
/**
* 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 $user_action = ApplicationContext::USER_ACTION_CONTINUE
): ApplicationContext {
$brand_name = $this->settings->has( 'brand_name' ) ? $this->settings->get( 'brand_name' ) : '';
$locale = $this->valid_bcp47_code();
$landingpage = $this->settings->has( 'landing_page' ) ?
$this->settings->get( 'landing_page' ) : ApplicationContext::LANDING_PAGE_NO_PREFERENCE;
$payment_preference = $this->settings->has( 'payee_preferred' ) && $this->settings->get( 'payee_preferred' ) ?
ApplicationContext::PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED : ApplicationContext::PAYMENT_METHOD_UNRESTRICTED;
$context = new ApplicationContext(
home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ),
(string) wc_get_checkout_url(),
(string) $brand_name,
$locale,
(string) $landingpage,
$shipping_preferences,
$user_action,
$payment_preference
);
return $context;
}
/**
* Returns a PayPal-supported BCP-47 code, for example de-DE-formal becomes de-DE.
*
* @return string
*/
protected function valid_bcp47_code() {
$locale = str_replace( '_', '-', get_user_locale() );
if ( preg_match( '/^[a-z]{2}(?:-[A-Z][a-z]{3})?(?:-(?:[A-Z]{2}))?$/', $locale ) ) {
return $locale;
}
$parts = explode( '-', $locale );
if ( count( $parts ) === 3 ) {
$ret = substr( $locale, 0, strrpos( $locale, '-' ) );
if ( false !== $ret ) {
return $ret;
}
}
return 'en';
}
}