mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Merge trunk
This commit is contained in:
commit
8f6f5c0c35
30 changed files with 1045 additions and 43 deletions
|
@ -17,3 +17,7 @@ indent_style = space
|
|||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.php]
|
||||
ij_php_variable_naming_style = snake_case
|
||||
ij_php_getters_setters_naming_style = snake_case
|
||||
|
|
|
@ -16,7 +16,8 @@ return array(
|
|||
|
||||
'wcgateway.builder.experience-context' => static function ( ContainerInterface $container ): ExperienceContextBuilder {
|
||||
return new ExperienceContextBuilder(
|
||||
$container->get( 'wcgateway.settings' )
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
$container->get( 'wcgateway.shipping.callback.factory.url' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
44
modules/ppcp-api-client/src/Entity/CallbackConfig.php
Normal file
44
modules/ppcp-api-client/src/Entity/CallbackConfig.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
|
||||
|
||||
/**
|
||||
* The config for experience_context.order_update_callback_config.
|
||||
*/
|
||||
class CallbackConfig {
|
||||
public const EVENT_SHIPPING_ADDRESS = 'SHIPPING_ADDRESS';
|
||||
public const EVENT_SHIPPING_OPTIONS = 'SHIPPING_OPTIONS';
|
||||
|
||||
/**
|
||||
* The events.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private array $events;
|
||||
|
||||
/**
|
||||
* The URL that will be called when the events occur.
|
||||
*/
|
||||
private string $url;
|
||||
|
||||
/**
|
||||
* @param string[] $events The events.
|
||||
* @param string $url The URL that will be called when the events occur.
|
||||
*/
|
||||
public function __construct( array $events, string $url ) {
|
||||
$this->events = $events;
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'callback_events' => $this->events,
|
||||
'callback_url' => $this->url,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -79,6 +79,11 @@ class ExperienceContext {
|
|||
*/
|
||||
private ?string $contact_preference = null;
|
||||
|
||||
/**
|
||||
* The callback config.
|
||||
*/
|
||||
private ?CallbackConfig $order_update_callback_config = null;
|
||||
|
||||
/**
|
||||
* Returns the return URL.
|
||||
*/
|
||||
|
@ -255,6 +260,25 @@ class ExperienceContext {
|
|||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback config.
|
||||
*/
|
||||
public function order_update_callback_config(): ?CallbackConfig {
|
||||
return $this->order_update_callback_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callback config.
|
||||
*
|
||||
* @param CallbackConfig|null $new_value The value to set.
|
||||
*/
|
||||
public function with_order_update_callback_config( ?CallbackConfig $new_value ): ExperienceContext {
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->order_update_callback_config = $new_value;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object as array.
|
||||
*/
|
||||
|
@ -267,6 +291,9 @@ class ExperienceContext {
|
|||
if ( $value === null ) {
|
||||
continue;
|
||||
}
|
||||
if ( is_object( $value ) && method_exists( $value, 'to_array' ) ) {
|
||||
$value = $value->to_array();
|
||||
}
|
||||
$data[ $prop->getName() ] = $value;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
|
|||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencyGetter;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\CartTotals;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
|
@ -111,6 +112,24 @@ class AmountFactory {
|
|||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Amount object based off a WooCommerce cart object from the Store API.
|
||||
*/
|
||||
public function from_store_api_cart( CartTotals $cart_totals ): Amount {
|
||||
return new Amount(
|
||||
$cart_totals->total_price()->to_paypal(),
|
||||
new AmountBreakdown(
|
||||
$cart_totals->total_items()->to_paypal(),
|
||||
$cart_totals->total_shipping()->to_paypal(),
|
||||
$cart_totals->total_tax()->to_paypal(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$cart_totals->total_discount()->to_paypal(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Amount object based off a WooCommerce order.
|
||||
*
|
||||
|
|
|
@ -11,9 +11,11 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
|
|||
|
||||
use WC_AJAX;
|
||||
use WC_Order;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\CallbackConfig;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ExperienceContext;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Shipping\ShippingCallbackUrlFactory;
|
||||
|
||||
/**
|
||||
* Class ExperienceContextBuilder
|
||||
|
@ -30,15 +32,16 @@ class ExperienceContextBuilder {
|
|||
*/
|
||||
private ContainerInterface $settings;
|
||||
|
||||
/**
|
||||
* ExperienceContextBuilder constructor.
|
||||
*
|
||||
* @param ContainerInterface $settings The settings.
|
||||
*/
|
||||
public function __construct( ContainerInterface $settings ) {
|
||||
private ShippingCallbackUrlFactory $shipping_callback_url_factory;
|
||||
|
||||
public function __construct(
|
||||
ContainerInterface $settings,
|
||||
ShippingCallbackUrlFactory $shipping_callback_url_factory
|
||||
) {
|
||||
$this->experience_context = new ExperienceContext();
|
||||
|
||||
$this->settings = $settings;
|
||||
$this->settings = $settings;
|
||||
$this->shipping_callback_url_factory = $shipping_callback_url_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,6 +164,23 @@ class ExperienceContextBuilder {
|
|||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the server-side shipping callback configuration.
|
||||
*/
|
||||
public function with_shipping_callback(): ExperienceContextBuilder {
|
||||
$builder = clone $this;
|
||||
|
||||
$builder->experience_context = $builder->experience_context
|
||||
->with_order_update_callback_config(
|
||||
new CallbackConfig(
|
||||
array( CallbackConfig::EVENT_SHIPPING_ADDRESS, CallbackConfig::EVENT_SHIPPING_OPTIONS ),
|
||||
$this->shipping_callback_url_factory->create()
|
||||
)
|
||||
);
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a custom contact preference to the experience context.
|
||||
*
|
||||
|
|
|
@ -352,6 +352,10 @@ export const PayPalComponent = ( {
|
|||
);
|
||||
|
||||
const getOnShippingOptionsChange = ( fundingSource ) => {
|
||||
if ( config.scriptData.server_side_shipping_callback.enabled ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( fundingSource === 'venmo' ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -364,6 +368,10 @@ export const PayPalComponent = ( {
|
|||
};
|
||||
|
||||
const getOnShippingAddressChange = ( fundingSource ) => {
|
||||
if ( config.scriptData.server_side_shipping_callback.enabled ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( fundingSource === 'venmo' ) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -144,7 +144,10 @@ class Renderer {
|
|||
};
|
||||
|
||||
// Check the condition and add the handler if needed
|
||||
if ( this.shouldEnableShippingCallback() ) {
|
||||
if (
|
||||
this.shouldEnableShippingCallback() &&
|
||||
! this.defaultSettings.server_side_shipping_callback.enabled
|
||||
) {
|
||||
options.onShippingOptionsChange = ( data, actions ) => {
|
||||
const shippingOptionsChange =
|
||||
! this.isVenmoButtonClickedWhenVaultingIsEnabled(
|
||||
|
|
|
@ -167,6 +167,7 @@ return array(
|
|||
$container->get( 'api.endpoint.payment-tokens' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||
$container->get( 'button.handle-shipping-in-paypal' ),
|
||||
$container->get( 'wcgateway.server-side-shipping-callback-enabled' ),
|
||||
$container->get( 'button.helper.disabled-funding-sources' ),
|
||||
$container->get( 'wcgateway.configuration.card-configuration' ),
|
||||
$container->get( 'api.helper.partner-attribution' )
|
||||
|
@ -239,6 +240,7 @@ return array(
|
|||
$container->get( 'button.early-wc-checkout-validation-enabled' ),
|
||||
$container->get( 'button.pay-now-contexts' ),
|
||||
$container->get( 'button.handle-shipping-in-paypal' ),
|
||||
$container->get( 'wcgateway.server-side-shipping-callback-enabled' ),
|
||||
$container->get( 'wcgateway.funding-sources-without-redirect' ),
|
||||
$logger
|
||||
);
|
||||
|
|
|
@ -253,6 +253,11 @@ class SmartButton implements SmartButtonInterface {
|
|||
*/
|
||||
protected PartnerAttribution $partner_attribution;
|
||||
|
||||
/**
|
||||
* Whether the server-side shipping callback is enabled (feature flag).
|
||||
*/
|
||||
private bool $server_side_shipping_callback_enabled;
|
||||
|
||||
/**
|
||||
* SmartButton constructor.
|
||||
*
|
||||
|
@ -279,6 +284,7 @@ class SmartButton implements SmartButtonInterface {
|
|||
* @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
* @param bool $should_handle_shipping_in_paypal Whether the shipping should be handled in PayPal.
|
||||
* @param bool $server_side_shipping_callback_enabled Whether the server-side shipping callback is enabled (feature flag).
|
||||
* @param DisabledFundingSources $disabled_funding_sources List of funding sources to be disabled.
|
||||
* @param CardPaymentsConfiguration $dcc_configuration The DCC Gateway Configuration.
|
||||
* @param PartnerAttribution $partner_attribution The PayPal Partner Attribution Helper.
|
||||
|
@ -307,36 +313,38 @@ class SmartButton implements SmartButtonInterface {
|
|||
PaymentTokensEndpoint $payment_tokens_endpoint,
|
||||
LoggerInterface $logger,
|
||||
bool $should_handle_shipping_in_paypal,
|
||||
bool $server_side_shipping_callback_enabled,
|
||||
DisabledFundingSources $disabled_funding_sources,
|
||||
CardPaymentsConfiguration $dcc_configuration,
|
||||
PartnerAttribution $partner_attribution
|
||||
) {
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->client_id = $client_id;
|
||||
$this->request_data = $request_data;
|
||||
$this->dcc_applies = $dcc_applies;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
$this->messages_apply = $messages_apply;
|
||||
$this->environment = $environment;
|
||||
$this->payment_token_repository = $payment_token_repository;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->all_funding_sources = $all_funding_sources;
|
||||
$this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled;
|
||||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->funding_sources_without_redirect = $funding_sources_without_redirect;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->logger = $logger;
|
||||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
$this->should_handle_shipping_in_paypal = $should_handle_shipping_in_paypal;
|
||||
$this->disabled_funding_sources = $disabled_funding_sources;
|
||||
$this->dcc_configuration = $dcc_configuration;
|
||||
$this->partner_attribution = $partner_attribution;
|
||||
$this->module_url = $module_url;
|
||||
$this->version = $version;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->settings = $settings;
|
||||
$this->payer_factory = $payer_factory;
|
||||
$this->client_id = $client_id;
|
||||
$this->request_data = $request_data;
|
||||
$this->dcc_applies = $dcc_applies;
|
||||
$this->subscription_helper = $subscription_helper;
|
||||
$this->messages_apply = $messages_apply;
|
||||
$this->environment = $environment;
|
||||
$this->payment_token_repository = $payment_token_repository;
|
||||
$this->settings_status = $settings_status;
|
||||
$this->currency = $currency;
|
||||
$this->all_funding_sources = $all_funding_sources;
|
||||
$this->basic_checkout_validation_enabled = $basic_checkout_validation_enabled;
|
||||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->funding_sources_without_redirect = $funding_sources_without_redirect;
|
||||
$this->vault_v3_enabled = $vault_v3_enabled;
|
||||
$this->logger = $logger;
|
||||
$this->payment_tokens_endpoint = $payment_tokens_endpoint;
|
||||
$this->should_handle_shipping_in_paypal = $should_handle_shipping_in_paypal;
|
||||
$this->server_side_shipping_callback_enabled = $server_side_shipping_callback_enabled;
|
||||
$this->disabled_funding_sources = $disabled_funding_sources;
|
||||
$this->dcc_configuration = $dcc_configuration;
|
||||
$this->partner_attribution = $partner_attribution;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1341,6 +1349,9 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
|
|||
'has_wc_card_payment_tokens' => $this->user_has_wc_card_payment_tokens( get_current_user_id() ),
|
||||
),
|
||||
'should_handle_shipping_in_paypal' => $this->should_handle_shipping_in_paypal && ! $this->is_checkout(),
|
||||
'server_side_shipping_callback' => array(
|
||||
'enabled' => $this->server_side_shipping_callback_enabled,
|
||||
),
|
||||
'needShipping' => $this->need_shipping(),
|
||||
'vaultingEnabled' => $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ),
|
||||
'productType' => null,
|
||||
|
|
|
@ -163,6 +163,11 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
*/
|
||||
private $handle_shipping_in_paypal;
|
||||
|
||||
/**
|
||||
* Whether the server-side shipping callback is enabled (feature flag).
|
||||
*/
|
||||
private bool $server_side_shipping_callback_enabled;
|
||||
|
||||
/**
|
||||
* The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back.
|
||||
*
|
||||
|
@ -202,6 +207,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
* @param bool $early_validation_enabled Whether to execute WC validation of the checkout form.
|
||||
* @param string[] $pay_now_contexts The contexts that should have the Pay Now button.
|
||||
* @param bool $handle_shipping_in_paypal If true, the shipping methods are sent to PayPal allowing the customer to select it inside the popup.
|
||||
* @param bool $server_side_shipping_callback_enabled Whether the server-side shipping callback is enabled (feature flag).
|
||||
* @param string[] $funding_sources_without_redirect The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
|
@ -221,6 +227,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
bool $early_validation_enabled,
|
||||
array $pay_now_contexts,
|
||||
bool $handle_shipping_in_paypal,
|
||||
bool $server_side_shipping_callback_enabled,
|
||||
array $funding_sources_without_redirect,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
|
@ -240,6 +247,7 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$this->early_validation_enabled = $early_validation_enabled;
|
||||
$this->pay_now_contexts = $pay_now_contexts;
|
||||
$this->handle_shipping_in_paypal = $handle_shipping_in_paypal;
|
||||
$this->server_side_shipping_callback_enabled = $server_side_shipping_callback_enabled;
|
||||
$this->funding_sources_without_redirect = $funding_sources_without_redirect;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
@ -460,13 +468,19 @@ class CreateOrderEndpoint implements EndpointInterface {
|
|||
$payment_source_key
|
||||
);
|
||||
|
||||
$experience_context = $this->experience_context_builder
|
||||
->with_default_paypal_config( $shipping_preference, $action )
|
||||
->with_contact_preference( $contact_preference );
|
||||
|
||||
if ( $this->server_side_shipping_callback_enabled
|
||||
&& $shipping_preference === ExperienceContext::SHIPPING_PREFERENCE_GET_FROM_FILE ) {
|
||||
$experience_context = $experience_context->with_shipping_callback();
|
||||
}
|
||||
|
||||
$payment_source = new PaymentSource(
|
||||
$payment_source_key,
|
||||
(object) array(
|
||||
'experience_context' => $this->experience_context_builder
|
||||
->with_default_paypal_config( $shipping_preference, $action )
|
||||
->with_contact_preference( $contact_preference )
|
||||
->build()->to_array(),
|
||||
'experience_context' => $experience_context->build()->to_array(),
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
|
|||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers;
|
||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ShippingCallbackEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
|
@ -38,6 +39,12 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Factory\SimpleRedirect
|
|||
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrar;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Registrar\TaskRegistrarInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\WcTasks\Tasks\SimpleRedirectTask;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Shipping\ShippingCallbackUrlFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Endpoint\CartEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\CartFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\CartTotalsFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\MoneyFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\ShippingRatesFactory;
|
||||
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Admin\FeesRenderer;
|
||||
|
@ -2206,4 +2213,54 @@ return array(
|
|||
|
||||
return $prefix . '-';
|
||||
},
|
||||
|
||||
'wcgateway.store-api.endpoint.cart' => static function( ContainerInterface $container ) : CartEndpoint {
|
||||
return new CartEndpoint(
|
||||
$container->get( 'wcgateway.store-api.factory.cart' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'wcgateway.store-api.factory.cart' => static function( ContainerInterface $container ) : CartFactory {
|
||||
return new CartFactory(
|
||||
$container->get( 'wcgateway.store-api.factory.cart-totals' ),
|
||||
$container->get( 'wcgateway.store-api.factory.shipping-rates' )
|
||||
);
|
||||
},
|
||||
'wcgateway.store-api.factory.cart-totals' => static function( ContainerInterface $container ) : CartTotalsFactory {
|
||||
return new CartTotalsFactory(
|
||||
$container->get( 'wcgateway.store-api.factory.money' )
|
||||
);
|
||||
},
|
||||
'wcgateway.store-api.factory.shipping-rates' => static function( ContainerInterface $container ) : ShippingRatesFactory {
|
||||
return new ShippingRatesFactory(
|
||||
$container->get( 'wcgateway.store-api.factory.money' )
|
||||
);
|
||||
},
|
||||
'wcgateway.store-api.factory.money' => static function( ContainerInterface $container ) : MoneyFactory {
|
||||
return new MoneyFactory();
|
||||
},
|
||||
|
||||
'wcgateway.shipping.callback.endpoint' => static function( ContainerInterface $container ) : ShippingCallbackEndpoint {
|
||||
return new ShippingCallbackEndpoint(
|
||||
$container->get( 'wcgateway.store-api.endpoint.cart' ),
|
||||
$container->get( 'api.factory.amount' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'wcgateway.shipping.callback.factory.url' => static function( ContainerInterface $container ) : ShippingCallbackUrlFactory {
|
||||
return new ShippingCallbackUrlFactory(
|
||||
$container->get( 'wcgateway.store-api.endpoint.cart' ),
|
||||
$container->get( 'wcgateway.shipping.callback.endpoint' )
|
||||
);
|
||||
},
|
||||
|
||||
'wcgateway.server-side-shipping-callback-enabled' => static function( ContainerInterface $container ) : bool {
|
||||
return apply_filters(
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
'woocommerce.feature-flags.woocommerce_paypal_payments.server_side_shipping_callback_enabled',
|
||||
getenv( 'PCP_SERVER_SIDE_SHIPPING_CALLBACK_ENABLED' ) === '1'
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Endpoint;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\AmountFactory;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Endpoint\CartEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\ShippingRate;
|
||||
use WP_REST_Response;
|
||||
|
||||
/**
|
||||
* Handles the shipping callback.
|
||||
*/
|
||||
class ShippingCallbackEndpoint {
|
||||
|
||||
private const NAMESPACE = 'paypal/v1';
|
||||
private const ROUTE = 'shipping-callback';
|
||||
|
||||
private CartEndpoint $cart_endpoint;
|
||||
|
||||
private AmountFactory $amount_factory;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct( CartEndpoint $cart_endpoint, AmountFactory $amount_factory, LoggerInterface $logger ) {
|
||||
$this->cart_endpoint = $cart_endpoint;
|
||||
$this->amount_factory = $amount_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the endpoint.
|
||||
*/
|
||||
public function register(): bool {
|
||||
return (bool) register_rest_route(
|
||||
self::NAMESPACE,
|
||||
self::ROUTE,
|
||||
array(
|
||||
'methods' => array(
|
||||
'POST',
|
||||
),
|
||||
'callback' => array(
|
||||
$this,
|
||||
'handle_request',
|
||||
),
|
||||
'permission_callback' => array(
|
||||
$this,
|
||||
'verify_request',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function verify_request( \WP_REST_Request $request ): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handle_request( \WP_REST_Request $request ): WP_REST_Response {
|
||||
$cart_token = (string) $request->get_param( 'cart_token' );
|
||||
|
||||
$request_data = $request->get_params();
|
||||
|
||||
$this->logger->debug( 'Shipping callback received: ' . $request->get_body() );
|
||||
|
||||
$request_id = $request_data['id'];
|
||||
$pu_id = $request_data['purchase_units'][0]['reference_id'];
|
||||
|
||||
$address = $this->convert_address_to_wc( $request_data['shipping_address'] );
|
||||
|
||||
$cart_response = $this->cart_endpoint->update_customer(
|
||||
$cart_token,
|
||||
array(
|
||||
'shipping_address' => $address,
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $cart_response->cart()->shipping_rates() ) ) {
|
||||
$this->logger->debug( 'Shipping callback response: ADDRESS_ERROR (no shipping rates found).' );
|
||||
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'name' => 'UNPROCESSABLE_ENTITY',
|
||||
'details' => array(
|
||||
array(
|
||||
'issue' => 'ADDRESS_ERROR',
|
||||
),
|
||||
),
|
||||
),
|
||||
422
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $request_data['shipping_option'] ) ) {
|
||||
$selected_shipping_method_id = $request_data['shipping_option']['id'];
|
||||
|
||||
$cart_response = $this->cart_endpoint->select_shipping_rate( $cart_token, 0, $selected_shipping_method_id );
|
||||
}
|
||||
|
||||
$cart = $cart_response->cart();
|
||||
|
||||
$amount = $this->amount_factory->from_store_api_cart( $cart->totals() );
|
||||
|
||||
$shipping_options = array_map(
|
||||
function ( ShippingRate $rate ): ShippingOption {
|
||||
return $rate->to_paypal();
|
||||
},
|
||||
$cart->shipping_rates()
|
||||
);
|
||||
|
||||
$response = array(
|
||||
'id' => $request_id,
|
||||
'purchase_units' => array(
|
||||
array(
|
||||
'reference_id' => $pu_id,
|
||||
'amount' => $amount->to_array(),
|
||||
'shipping_options' => array_map(
|
||||
function ( ShippingOption $shipping_option ): array {
|
||||
return $shipping_option->to_array();
|
||||
},
|
||||
$shipping_options
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$this->logger->debug( 'Shipping callback response: ' . (string) wp_json_encode( $response ) );
|
||||
|
||||
return new WP_REST_Response(
|
||||
$response,
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the endpoint.
|
||||
*/
|
||||
public function url(): string {
|
||||
$url = rest_url( self::NAMESPACE . '/' . self::ROUTE );
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
private function convert_address_to_wc( array $address ): array {
|
||||
return array(
|
||||
'country' => $address['country_code'] ?? '',
|
||||
'state' => $address['admin_area_1'] ?? '',
|
||||
'city' => $address['admin_area_2'] ?? '',
|
||||
'postcode' => $address['postal_code'] ?? '',
|
||||
'address_line_1' => '',
|
||||
'address_line_2' => '',
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Shipping;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ShippingCallbackEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Endpoint\CartEndpoint;
|
||||
|
||||
/**
|
||||
* URL generation for the server-side shipping callback.
|
||||
*/
|
||||
class ShippingCallbackUrlFactory {
|
||||
private CartEndpoint $cart_endpoint;
|
||||
|
||||
private ShippingCallbackEndpoint $shipping_callback_endpoint;
|
||||
|
||||
public function __construct( CartEndpoint $cart_endpoint, ShippingCallbackEndpoint $shipping_callback_endpoint ) {
|
||||
$this->cart_endpoint = $cart_endpoint;
|
||||
$this->shipping_callback_endpoint = $shipping_callback_endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the callback URL.
|
||||
*/
|
||||
public function create() : string {
|
||||
$cart_response = $this->cart_endpoint->get_cart();
|
||||
|
||||
$url = $this->shipping_callback_endpoint->url();
|
||||
$url = add_query_arg( 'cart_token', $cart_response->cart_token(), $url );
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
143
modules/ppcp-wc-gateway/src/StoreApi/Endpoint/CartEndpoint.php
Normal file
143
modules/ppcp-wc-gateway/src/StoreApi/Endpoint/CartEndpoint.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\CartResponse;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\CartFactory;
|
||||
|
||||
/**
|
||||
* The wrapper for the WC Store API cart requests.
|
||||
*/
|
||||
class CartEndpoint {
|
||||
use RequestTrait;
|
||||
|
||||
private CartFactory $cart_factory;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* Unused (for trait).
|
||||
*/
|
||||
private string $host = '';
|
||||
|
||||
public function __construct( CartFactory $cart_factory, LoggerInterface $logger ) {
|
||||
$this->cart_factory = $cart_factory;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cart of the current user (based on the current cookies).
|
||||
*
|
||||
* @throws Exception When request fails.
|
||||
*/
|
||||
public function get_cart(): CartResponse {
|
||||
return $this->perform_cart_request(
|
||||
'cart',
|
||||
array(
|
||||
'method' => 'GET',
|
||||
'cookies' => $_COOKIE,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the customer address to the specified data and returns the cart.
|
||||
*
|
||||
* @see https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/cart#update-customer
|
||||
* @param string $cart_token The cart token from the get_cart response.
|
||||
* @param array<string, mixed> $fields The address fields to set and their new values.
|
||||
* @throws Exception When request fails.
|
||||
*/
|
||||
public function update_customer( string $cart_token, array $fields ): CartResponse {
|
||||
return $this->perform_cart_request(
|
||||
'cart/update-customer',
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'cart-token' => $cart_token,
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode( $fields, JSON_FORCE_OBJECT ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shipping rate and returns the cart.
|
||||
*
|
||||
* @param string $cart_token The cart token from the get_cart response.
|
||||
* @param int $package_id The package number, normally should be 0.
|
||||
* @param string $rate_id The rate id, like "flat_rate:1".
|
||||
* @throws Exception When request fails.
|
||||
*/
|
||||
public function select_shipping_rate( string $cart_token, int $package_id, string $rate_id ): CartResponse {
|
||||
return $this->perform_cart_request(
|
||||
'cart/select-shipping-rate',
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'cart-token' => $cart_token,
|
||||
'Content-Type' => 'application/json',
|
||||
),
|
||||
'body' => wp_json_encode(
|
||||
array(
|
||||
'package_id' => $package_id,
|
||||
'rate_id' => $rate_id,
|
||||
),
|
||||
JSON_FORCE_OBJECT
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function perform_cart_request( string $path, array $args ): CartResponse {
|
||||
$response = $this->request( $this->cart_endpoint_url( $path ), $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error = new Exception( "$path request failed: " . $response->get_error_message() );
|
||||
$this->logger->warning(
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$json = json_decode( $response['body'], true );
|
||||
|
||||
$error_code = $json['code'] ?? null;
|
||||
$error_message = $json['message'] ?? null;
|
||||
if ( $error_code ) {
|
||||
$error = new Exception( "$path request return error: $error_code - $error_message" );
|
||||
$this->logger->warning(
|
||||
$error->getMessage(),
|
||||
array(
|
||||
'args' => $args,
|
||||
'response' => $response,
|
||||
)
|
||||
);
|
||||
throw $error;
|
||||
}
|
||||
|
||||
$cart = $this->cart_factory->from_response( $json );
|
||||
|
||||
$cart_token = $response['headers']['cart-token'] ?? '';
|
||||
|
||||
return new CartResponse( $cart, $cart_token );
|
||||
}
|
||||
|
||||
protected function cart_endpoint_url( string $path ): string {
|
||||
return $this->base_api_url() . $path;
|
||||
}
|
||||
|
||||
protected function base_api_url(): string {
|
||||
return rest_url( '/wc/store/v1/' );
|
||||
}
|
||||
}
|
36
modules/ppcp-wc-gateway/src/StoreApi/Entity/Cart.php
Normal file
36
modules/ppcp-wc-gateway/src/StoreApi/Entity/Cart.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory\ShippingRate;
|
||||
|
||||
/**
|
||||
* Cart object for the Store API.
|
||||
*/
|
||||
class Cart {
|
||||
private CartTotals $totals;
|
||||
|
||||
/**
|
||||
* @var ShippingRate[]
|
||||
*/
|
||||
private array $shipping_rates;
|
||||
|
||||
/**
|
||||
* @param CartTotals $totals
|
||||
* @param ShippingRate[] $shipping_rates
|
||||
*/
|
||||
public function __construct( CartTotals $totals, array $shipping_rates ) {
|
||||
$this->totals = $totals;
|
||||
$this->shipping_rates = $shipping_rates;
|
||||
}
|
||||
|
||||
public function totals(): CartTotals {
|
||||
return $this->totals;
|
||||
}
|
||||
|
||||
public function shipping_rates(): array {
|
||||
return $this->shipping_rates;
|
||||
}
|
||||
}
|
30
modules/ppcp-wc-gateway/src/StoreApi/Entity/CartResponse.php
Normal file
30
modules/ppcp-wc-gateway/src/StoreApi/Entity/CartResponse.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity;
|
||||
|
||||
/**
|
||||
* CartResponse for the Store API.
|
||||
*/
|
||||
class CartResponse {
|
||||
private Cart $cart;
|
||||
|
||||
private string $cart_token;
|
||||
|
||||
public function __construct( Cart $cart, string $cart_token ) {
|
||||
$this->cart = $cart;
|
||||
$this->cart_token = $cart_token;
|
||||
}
|
||||
|
||||
public function cart(): Cart {
|
||||
return $this->cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* The token required for the API requests (except Get Cart).
|
||||
*/
|
||||
public function cart_token(): string {
|
||||
return $this->cart_token;
|
||||
}
|
||||
}
|
96
modules/ppcp-wc-gateway/src/StoreApi/Entity/CartTotals.php
Normal file
96
modules/ppcp-wc-gateway/src/StoreApi/Entity/CartTotals.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity;
|
||||
|
||||
/**
|
||||
* CartTotals object for the Store API.
|
||||
*/
|
||||
class CartTotals {
|
||||
private Money $total_items;
|
||||
|
||||
private Money $total_items_tax;
|
||||
|
||||
private Money $total_fees;
|
||||
|
||||
private Money $total_fees_tax;
|
||||
|
||||
private Money $total_discount;
|
||||
|
||||
private Money $total_discount_tax;
|
||||
|
||||
private Money $total_shipping;
|
||||
|
||||
private Money $total_shipping_tax;
|
||||
|
||||
private Money $total_price;
|
||||
|
||||
private Money $total_tax;
|
||||
|
||||
public function __construct(
|
||||
Money $total_items,
|
||||
Money $total_items_tax,
|
||||
Money $total_fees,
|
||||
Money $total_fees_tax,
|
||||
Money $total_discount,
|
||||
Money $total_discount_tax,
|
||||
Money $total_shipping,
|
||||
Money $total_shipping_tax,
|
||||
Money $total_price,
|
||||
Money $total_tax
|
||||
) {
|
||||
$this->total_items = $total_items;
|
||||
$this->total_items_tax = $total_items_tax;
|
||||
$this->total_fees = $total_fees;
|
||||
$this->total_fees_tax = $total_fees_tax;
|
||||
$this->total_discount = $total_discount;
|
||||
$this->total_discount_tax = $total_discount_tax;
|
||||
$this->total_shipping = $total_shipping;
|
||||
$this->total_shipping_tax = $total_shipping_tax;
|
||||
$this->total_price = $total_price;
|
||||
$this->total_tax = $total_tax;
|
||||
}
|
||||
|
||||
public function total_items(): Money {
|
||||
return $this->total_items;
|
||||
}
|
||||
|
||||
public function total_items_tax(): Money {
|
||||
return $this->total_items_tax;
|
||||
}
|
||||
|
||||
public function total_fees(): Money {
|
||||
return $this->total_fees;
|
||||
}
|
||||
|
||||
public function total_fees_tax(): Money {
|
||||
return $this->total_fees_tax;
|
||||
}
|
||||
|
||||
public function total_discount(): Money {
|
||||
return $this->total_discount;
|
||||
}
|
||||
|
||||
public function total_discount_tax(): Money {
|
||||
return $this->total_discount_tax;
|
||||
}
|
||||
|
||||
public function total_shipping(): Money {
|
||||
return $this->total_shipping;
|
||||
}
|
||||
|
||||
public function total_shipping_tax(): Money {
|
||||
return $this->total_shipping_tax;
|
||||
}
|
||||
|
||||
public function total_price(): Money {
|
||||
return $this->total_price;
|
||||
}
|
||||
|
||||
public function total_tax(): Money {
|
||||
return $this->total_tax;
|
||||
}
|
||||
|
||||
|
||||
}
|
59
modules/ppcp-wc-gateway/src/StoreApi/Entity/Money.php
Normal file
59
modules/ppcp-wc-gateway/src/StoreApi/Entity/Money.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity;
|
||||
|
||||
/**
|
||||
* Money value for the Store API.
|
||||
*/
|
||||
class Money {
|
||||
private string $value;
|
||||
|
||||
private string $currency_code;
|
||||
|
||||
private int $currency_minor_unit;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param string $currency_code
|
||||
* @param int $currency_minor_unit
|
||||
*/
|
||||
public function __construct( string $value, string $currency_code, int $currency_minor_unit ) {
|
||||
$this->value = $value;
|
||||
$this->currency_code = $currency_code;
|
||||
$this->currency_minor_unit = $currency_minor_unit;
|
||||
}
|
||||
|
||||
public function value(): string {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function currency_code(): string {
|
||||
return $this->currency_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of digits after ".". For most currencies it is 2.
|
||||
*/
|
||||
public function currency_minor_unit(): int {
|
||||
return $this->currency_minor_unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to float, e.g. value=123, currency_minor_unit=2 --> 1.23.
|
||||
*/
|
||||
public function to_float(): float {
|
||||
return round( ( (int) $this->value ) / 10 ** $this->currency_minor_unit, $this->currency_minor_unit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Money object for the PayPal API.
|
||||
*/
|
||||
public function to_paypal(): \WooCommerce\PayPalCommerce\ApiClient\Entity\Money {
|
||||
return new \WooCommerce\PayPalCommerce\ApiClient\Entity\Money(
|
||||
$this->to_float(),
|
||||
$this->currency_code
|
||||
);
|
||||
}
|
||||
}
|
31
modules/ppcp-wc-gateway/src/StoreApi/Factory/CartFactory.php
Normal file
31
modules/ppcp-wc-gateway/src/StoreApi/Factory/CartFactory.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\Cart;
|
||||
|
||||
/**
|
||||
* Factory for the Store API cart.
|
||||
*/
|
||||
class CartFactory {
|
||||
private CartTotalsFactory $cart_totals_factory;
|
||||
|
||||
private ShippingRatesFactory $shipping_rates_factory;
|
||||
|
||||
public function __construct(
|
||||
CartTotalsFactory $cart_totals_factory,
|
||||
ShippingRatesFactory $shipping_rate_factory
|
||||
) {
|
||||
$this->cart_totals_factory = $cart_totals_factory;
|
||||
$this->shipping_rates_factory = $shipping_rate_factory;
|
||||
}
|
||||
|
||||
public function from_response( array $obj ): Cart {
|
||||
return new Cart(
|
||||
$this->cart_totals_factory->from_response_obj( (array) ( $obj['totals'] ?? array() ) ),
|
||||
$this->shipping_rates_factory->from_response_obj( (array) ( $obj['shipping_rates'] ?? array() ) )
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\CartTotals;
|
||||
|
||||
/**
|
||||
* Factory for the Store API cart totals.
|
||||
*/
|
||||
class CartTotalsFactory {
|
||||
private MoneyFactory $money_factory;
|
||||
|
||||
public function __construct( MoneyFactory $money_factory ) {
|
||||
$this->money_factory = $money_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the 'totals' object from the cart response.
|
||||
*/
|
||||
public function from_response_obj( array $obj ): CartTotals {
|
||||
return new CartTotals(
|
||||
$this->money_factory->from_response_values( $obj, 'total_items' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_items_tax' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_fees' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_fees_tax' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_discount' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_discount_tax' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_shipping' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_shipping_tax' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_price' ),
|
||||
$this->money_factory->from_response_values( $obj, 'total_tax' )
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\Money;
|
||||
|
||||
/**
|
||||
* Factory for the Store API money values.
|
||||
*/
|
||||
class MoneyFactory {
|
||||
/**
|
||||
* Parses the specified money property from a Store API response object
|
||||
* containing fields like currency_minor_unit.
|
||||
*
|
||||
* @param array $obj The object.
|
||||
* @param string $property_name The property name with the money value.
|
||||
*/
|
||||
public function from_response_values( array $obj, string $property_name ): Money {
|
||||
$value = (string) $obj[ $property_name ];
|
||||
$currency_code = (string) ( $obj['currency_code'] ?? '' );
|
||||
$currency_minor_unit = (int) ( $obj['currency_minor_unit'] ?? 2 );
|
||||
|
||||
return new Money( $value, $currency_code, $currency_minor_unit );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\ShippingOption;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\StoreApi\Entity\Money;
|
||||
|
||||
/**
|
||||
* ShippingRate object for the Store API.
|
||||
*/
|
||||
class ShippingRate {
|
||||
private string $rate_id;
|
||||
|
||||
private string $name;
|
||||
|
||||
private bool $selected;
|
||||
|
||||
private Money $price;
|
||||
|
||||
private Money $taxes;
|
||||
|
||||
public function __construct(
|
||||
string $rate_id,
|
||||
string $name,
|
||||
bool $selected,
|
||||
Money $price,
|
||||
Money $taxes
|
||||
) {
|
||||
$this->rate_id = $rate_id;
|
||||
$this->name = $name;
|
||||
$this->selected = $selected;
|
||||
$this->price = $price;
|
||||
$this->taxes = $taxes;
|
||||
}
|
||||
|
||||
public function rate_id(): string {
|
||||
return $this->rate_id;
|
||||
}
|
||||
|
||||
public function name(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function selected(): bool {
|
||||
return $this->selected;
|
||||
}
|
||||
|
||||
public function price(): Money {
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
public function taxes(): Money {
|
||||
return $this->taxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ShippingOption object for the PayPal API.
|
||||
*/
|
||||
public function to_paypal(): ShippingOption {
|
||||
return new ShippingOption(
|
||||
$this->rate_id,
|
||||
$this->name,
|
||||
$this->selected,
|
||||
$this->price->to_paypal(),
|
||||
ShippingOption::TYPE_SHIPPING
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\StoreApi\Factory;
|
||||
|
||||
/**
|
||||
* Factory for the Store API shipping rates.
|
||||
*/
|
||||
class ShippingRatesFactory {
|
||||
private MoneyFactory $money_factory;
|
||||
|
||||
public function __construct( MoneyFactory $money_factory ) {
|
||||
$this->money_factory = $money_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts shipping rates from the 'shipping_rates' object in the cart response.
|
||||
*/
|
||||
public function from_response_obj( array $obj ): array {
|
||||
$rates = array();
|
||||
foreach ( $obj as $package ) {
|
||||
foreach ( $package['shipping_rates'] as $item ) {
|
||||
$rates[] = $this->parse_shipping_rate( $item );
|
||||
}
|
||||
}
|
||||
return $rates;
|
||||
}
|
||||
|
||||
private function parse_shipping_rate( array $obj ): ShippingRate {
|
||||
return new ShippingRate(
|
||||
$obj['rate_id'],
|
||||
$obj['name'],
|
||||
$obj['selected'],
|
||||
$this->money_factory->from_response_values( $obj, 'price' ),
|
||||
$this->money_factory->from_response_values( $obj, 'taxes' )
|
||||
);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameI
|
|||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Assets\VoidButtonAssets;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\RefreshFeatureStatusEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ShippingCallbackEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\VoidOrderEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Helper\InstallmentsProductStatus;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Notice\SendOnlyCountryNotice;
|
||||
|
@ -603,6 +604,16 @@ class WCGatewayModule implements ServiceModule, ExtendingModule, ExecutableModul
|
|||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'rest_api_init',
|
||||
static function () use ( $c ) {
|
||||
$endpoint = $c->get( 'wcgateway.shipping.callback.endpoint' );
|
||||
assert( $endpoint instanceof ShippingCallbackEndpoint );
|
||||
|
||||
$endpoint->register();
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
"ddev:pw-tests": "ddev yarn playwright test",
|
||||
"ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests",
|
||||
"ddev:lint": "yarn ddev:phpcs && yarn ddev:psalm",
|
||||
"ddev:phpcs": "ddev exec phpcs --parallel=8 -s",
|
||||
"ddev:phpcs": "ddev exec phpcs --parallel=8 -s --runtime-set ignore_warnings_on_exit 1",
|
||||
"ddev:psalm": "ddev exec psalm --show-info=false --threads=8 --diff",
|
||||
"ddev:fix-lint": "ddev exec phpcbf",
|
||||
"ddev:xdebug-on": "ddev xdebug",
|
||||
|
|
|
@ -39,11 +39,22 @@
|
|||
<properties>
|
||||
<property name="skipIfInheritdoc" value="true" />
|
||||
</properties>
|
||||
<exclude name="Squiz.Commenting.FunctionComment.MissingParamTag" />
|
||||
<exclude name="Squiz.Commenting.FunctionComment.MissingParamComment" />
|
||||
<exclude name="Squiz.Commenting.FunctionComment.Missing" />
|
||||
</rule>
|
||||
|
||||
<rule ref="Squiz.Commenting.VariableComment">
|
||||
<exclude name="Squiz.Commenting.VariableComment.MissingVar" />
|
||||
<exclude name="Squiz.Commenting.VariableComment.Missing" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.FileComment">
|
||||
<exclude name="Squiz.Commenting.FileComment.Missing" />
|
||||
<exclude name="Squiz.Commenting.FileComment.MissingPackageTag" />
|
||||
</rule>
|
||||
<rule ref="Generic.Commenting.DocComment">
|
||||
<exclude name="Generic.Commenting.DocComment.MissingShort" />
|
||||
</rule>
|
||||
|
||||
|
||||
<arg name="extensions" value="php"/>
|
||||
<file>api</file>
|
||||
|
|
|
@ -20,7 +20,11 @@ class ExperienceContextTest extends TestCase
|
|||
->with_shipping_preference('NO_SHIPPING')
|
||||
->with_user_action('CONTINUE')
|
||||
->with_payment_method_preference('UNRESTRICTED')
|
||||
->with_contact_preference('NO_CONTACT_INFO');
|
||||
->with_contact_preference('NO_CONTACT_INFO')
|
||||
->with_order_update_callback_config(new CallbackConfig(
|
||||
[CallbackConfig::EVENT_SHIPPING_ADDRESS, CallbackConfig::EVENT_SHIPPING_OPTIONS],
|
||||
'example.com/callback',
|
||||
));
|
||||
|
||||
$this->assertEmpty($empty->to_array());
|
||||
|
||||
|
@ -34,6 +38,10 @@ class ExperienceContextTest extends TestCase
|
|||
'user_action' => 'CONTINUE',
|
||||
'payment_method_preference' => 'UNRESTRICTED',
|
||||
'contact_preference' => 'NO_CONTACT_INFO',
|
||||
'order_update_callback_config' => [
|
||||
'callback_events' => [CallbackConfig::EVENT_SHIPPING_ADDRESS, CallbackConfig::EVENT_SHIPPING_OPTIONS],
|
||||
'callback_url' => 'example.com/callback',
|
||||
]
|
||||
], $result->to_array());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\ExperienceContextBuilder;
|
|||
use WooCommerce\PayPalCommerce\TestCase;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Endpoint\ReturnUrlEndpoint;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Shipping\ShippingCallbackUrlFactory;
|
||||
use function Brain\Monkey\Functions\expect;
|
||||
use Mockery;
|
||||
|
||||
|
@ -15,6 +16,8 @@ class ExperienceContextBuilderTest extends TestCase
|
|||
{
|
||||
private $settings;
|
||||
|
||||
private $shipping_callback_url_factory;
|
||||
|
||||
private $sut;
|
||||
|
||||
public function setUp(): void
|
||||
|
@ -22,8 +25,9 @@ class ExperienceContextBuilderTest extends TestCase
|
|||
parent::setUp();
|
||||
|
||||
$this->settings = Mockery::mock(Settings::class);
|
||||
$this->shipping_callback_url_factory = Mockery::mock(ShippingCallbackUrlFactory::class);
|
||||
|
||||
$this->sut = new ExperienceContextBuilder($this->settings);
|
||||
$this->sut = new ExperienceContextBuilder($this->settings, $this->shipping_callback_url_factory);
|
||||
}
|
||||
|
||||
public function testOrderReturnUrls()
|
||||
|
|
|
@ -175,6 +175,7 @@ class CreateOrderEndpointTest extends TestCase
|
|||
false,
|
||||
['checkout'],
|
||||
false,
|
||||
false,
|
||||
['paypal'],
|
||||
new NullLogger()
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue