Merge branch 'trunk' into PCP-2722-add-block-checkout-compatibility-to-advanced-card-processing

This commit is contained in:
Emili Castells Guasch 2024-05-17 17:21:12 +02:00
commit 4b3cd53f73
21 changed files with 665 additions and 51 deletions

View file

@ -60,7 +60,8 @@ class CartActionHandler {
funding_source: window.ppcpFundingSource,
bn_code:bnCode,
payer,
context:this.config.context
context:this.config.context,
payment_source: data.paymentSource
}),
}).then(function(res) {
return res.json();

View file

@ -0,0 +1,126 @@
import {paypalAddressToWc} from "../../../../../ppcp-blocks/resources/js/Helper/Address.js";
import {convertKeysToSnakeCase} from "../../../../../ppcp-blocks/resources/js/Helper/Helper.js";
/**
* Handles the shipping option change in PayPal.
*
* @param data
* @param actions
* @param config
* @returns {Promise<void>}
*/
export const handleShippingOptionsChange = async (data, actions, config) => {
try {
const shippingOptionId = data.selectedShippingOption?.id;
if (shippingOptionId) {
await fetch(config.ajax.update_customer_shipping.shipping_options.endpoint, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WC-Store-API-Nonce': config.ajax.update_customer_shipping.wp_rest_nonce,
},
body: JSON.stringify({
rate_id: shippingOptionId,
})
})
.then(response => {
return response.json();
})
.then(cardData => {
const shippingMethods = document.querySelectorAll('.shipping_method');
shippingMethods.forEach(function(method) {
if (method.value === shippingOptionId) {
method.checked = true;
}
});
})
}
const res = await fetch(config.ajax.update_shipping.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: config.ajax.update_shipping.nonce,
order_id: data.orderID,
})
});
const json = await res.json();
if (!json.success) {
throw new Error(json.data.message);
}
} catch (e) {
console.error(e);
actions.reject();
}
};
/**
* Handles the shipping address change in PayPal.
*
* @param data
* @param actions
* @param config
* @returns {Promise<void>}
*/
export const handleShippingAddressChange = async (data, actions, config) => {
try {
const address = paypalAddressToWc(convertKeysToSnakeCase(data.shippingAddress));
// Retrieve current cart contents
await fetch(config.ajax.update_customer_shipping.shipping_address.cart_endpoint)
.then(response => {
return response.json();
})
.then(cartData => {
// Update shipping address in the cart data
cartData.shipping_address.address_1 = address.address_1;
cartData.shipping_address.address_2 = address.address_2;
cartData.shipping_address.city = address.city;
cartData.shipping_address.state = address.state;
cartData.shipping_address.postcode = address.postcode;
cartData.shipping_address.country = address.country;
// Send update request
return fetch(config.ajax.update_customer_shipping.shipping_address.update_customer_endpoint, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WC-Store-API-Nonce': config.ajax.update_customer_shipping.wp_rest_nonce,
},
body: JSON.stringify({
shipping_address: cartData.shipping_address,
})
}).then(function (res) {
return res.json();
}).then(function (customerData) {
jQuery(".cart_totals .shop_table").load(location.href + " " + ".cart_totals .shop_table" + ">*", "");
})
})
const res = await fetch(config.ajax.update_shipping.endpoint, {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
nonce: config.ajax.update_shipping.nonce,
order_id: data.orderID,
})
});
const json = await res.json();
if (!json.success) {
throw new Error(json.data.message);
}
} catch (e) {
console.error(e);
actions.reject();
}
};

View file

@ -10,6 +10,7 @@ const onApprove = (context, errorHandler) => {
nonce: context.config.ajax.approve_order.nonce,
order_id:data.orderID,
funding_source: window.ppcpFundingSource,
should_create_wc_order: !context.config.vaultingEnabled || data.paymentSource !== 'venmo'
})
}).then((res)=>{
return res.json();
@ -20,7 +21,11 @@ const onApprove = (context, errorHandler) => {
errorHandler.genericError();
});
}
location.href = context.config.redirect;
let orderReceivedUrl = data.data?.order_received_url
location.href = orderReceivedUrl ? orderReceivedUrl : context.config.redirect;
});
}

View file

@ -3,6 +3,10 @@ import {loadScript} from "@paypal/paypal-js";
import {keysToCamelCase} from "../Helper/Utils";
import widgetBuilder from "./WidgetBuilder";
import {normalizeStyleForFundingSource} from "../Helper/Style";
import {
handleShippingOptionsChange,
handleShippingAddressChange,
} from "../Helper/ShippingHandler.js";
class Renderer {
constructor(creditCardRenderer, defaultSettings, onSmartButtonClick, onSmartButtonsInit) {
@ -64,6 +68,15 @@ class Renderer {
}
}
shouldHandleShippingInPaypal = (venmoButtonClicked) => {
if (!this.defaultSettings.should_handle_shipping_in_paypal) {
console.log('no')
return false;
}
return !venmoButtonClicked || !this.defaultSettings.vaultingEnabled;
}
renderButtons(wrapper, style, contextConfig, hasEnabledSeparateGateways, fundingSource = null) {
if (! document.querySelector(wrapper) || this.isAlreadyRendered(wrapper, fundingSource, hasEnabledSeparateGateways) ) {
// Try to render registered buttons again in case they were removed from the DOM by an external source.
@ -75,17 +88,24 @@ class Renderer {
contextConfig.fundingSource = fundingSource;
}
let venmoButtonClicked = false;
const buttonsOptions = () => {
return {
style,
...contextConfig,
onClick: this.onSmartButtonClick,
onClick: (data, actions) => {
venmoButtonClicked = data.fundingSource === 'venmo'
this.onSmartButtonClick
},
onInit: (data, actions) => {
if (this.onSmartButtonsInit) {
this.onSmartButtonsInit(data, actions);
}
this.handleOnButtonsInit(wrapper, data, actions);
},
onShippingOptionsChange: (data, actions) => this.shouldHandleShippingInPaypal(venmoButtonClicked) ? handleShippingOptionsChange(data, actions, this.defaultSettings) : null,
onShippingAddressChange: (data, actions) => this.shouldHandleShippingInPaypal(venmoButtonClicked) ? handleShippingAddressChange(data, actions, this.defaultSettings) : null,
}
}

View file

@ -16,6 +16,7 @@ use WooCommerce\PayPalCommerce\Button\Helper\CartProductsHelper;
use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver;
use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
use WooCommerce\PayPalCommerce\Button\Helper\WooCommerceOrderCreator;
use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator;
use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
@ -147,7 +148,8 @@ return array(
$container->get( 'wcgateway.funding-sources-without-redirect' ),
$container->get( 'vaulting.vault-v3-enabled' ),
$container->get( 'api.endpoint.payment-tokens' ),
$container->get( 'woocommerce.logger.woocommerce' )
$container->get( 'woocommerce.logger.woocommerce' ),
$container->get( 'button.handle-shipping-in-paypal' )
);
},
'button.url' => static function ( ContainerInterface $container ): string {
@ -157,7 +159,13 @@ return array(
);
},
'button.pay-now-contexts' => static function ( ContainerInterface $container ): array {
return array( 'checkout', 'pay-now' );
$defaults = array( 'checkout', 'pay-now' );
if ( $container->get( 'button.handle-shipping-in-paypal' ) ) {
return array_merge( $defaults, array( 'cart', 'product', 'mini-cart' ) );
}
return $defaults;
},
'button.request-data' => static function ( ContainerInterface $container ): RequestData {
return new RequestData();
@ -221,14 +229,18 @@ return array(
return new EarlyOrderHandler( $state, $order_processor, $session_handler );
},
'button.endpoint.approve-order' => static function ( ContainerInterface $container ): ApproveOrderEndpoint {
$request_data = $container->get( 'button.request-data' );
$order_endpoint = $container->get( 'api.endpoint.order' );
$session_handler = $container->get( 'session.handler' );
$three_d_secure = $container->get( 'button.helper.three-d-secure' );
$settings = $container->get( 'wcgateway.settings' );
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
$order_helper = $container->get( 'api.order-helper' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$request_data = $container->get( 'button.request-data' );
$order_endpoint = $container->get( 'api.endpoint.order' );
$session_handler = $container->get( 'session.handler' );
$three_d_secure = $container->get( 'button.helper.three-d-secure' );
$settings = $container->get( 'wcgateway.settings' );
$dcc_applies = $container->get( 'api.helpers.dccapplies' );
$order_helper = $container->get( 'api.order-helper' );
$final_review_enabled = $container->get( 'blocks.settings.final_review_enabled' );
$wc_order_creator = $container->get( 'button.helper.wc-order-creator' );
$gateway = $container->get( 'wcgateway.paypal-gateway' );
$subscription_helper = $container->get( 'wc-subscriptions.helper' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new ApproveOrderEndpoint(
$request_data,
$order_endpoint,
@ -237,6 +249,10 @@ return array(
$settings,
$dcc_applies,
$order_helper,
$final_review_enabled,
$gateway,
$wc_order_creator,
$subscription_helper,
$logger
);
},
@ -342,6 +358,10 @@ return array(
* May result in slower popup performance, additional loading.
*/
'button.handle-shipping-in-paypal' => static function ( ContainerInterface $container ): bool {
return false;
return ! $container->get( 'blocks.settings.final_review_enabled' );
},
'button.helper.wc-order-creator' => static function ( ContainerInterface $container ): WooCommerceOrderCreator {
return new WooCommerceOrderCreator( $container->get( 'wcgateway.funding-source.renderer' ), $container->get( 'session.handler' ) );
},
);

View file

@ -19,6 +19,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint;
use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint;
@ -217,6 +218,13 @@ class SmartButton implements SmartButtonInterface {
*/
private $logger;
/**
* Whether the shipping should be handled in PayPal.
*
* @var bool
*/
private $should_handle_shipping_in_paypal;
/**
* SmartButton constructor.
*
@ -242,6 +250,7 @@ class SmartButton implements SmartButtonInterface {
* @param bool $vault_v3_enabled Whether Vault v3 module is enabled.
* @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.
*/
public function __construct(
string $module_url,
@ -265,7 +274,8 @@ class SmartButton implements SmartButtonInterface {
array $funding_sources_without_redirect,
bool $vault_v3_enabled,
PaymentTokensEndpoint $payment_tokens_endpoint,
LoggerInterface $logger
LoggerInterface $logger,
bool $should_handle_shipping_in_paypal
) {
$this->module_url = $module_url;
@ -290,6 +300,7 @@ class SmartButton implements SmartButtonInterface {
$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;
}
/**
@ -1133,6 +1144,21 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentTokenForGuest::ENDPOINT ),
'nonce' => wp_create_nonce( CreatePaymentTokenForGuest::nonce() ),
),
'update_shipping' => array(
'endpoint' => \WC_AJAX::get_endpoint( UpdateShippingEndpoint::ENDPOINT ),
'nonce' => wp_create_nonce( UpdateShippingEndpoint::nonce() ),
),
'update_customer_shipping' => array(
'shipping_options' => array(
'endpoint' => '/wp-json/wc/store/cart/select-shipping-rate',
),
'shipping_address' => array(
'cart_endpoint' => '/wp-json/wc/store/cart/',
'update_customer_endpoint' => '/wp-json/wc/store/v1/cart/update-customer/',
),
'wp_rest_nonce' => wp_create_nonce( 'wc_store_api' ),
'update_shipping_method' => \WC_AJAX::get_endpoint( 'update_shipping_method' ),
),
),
'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(),
'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(),
@ -1253,6 +1279,8 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
'user' => array(
'is_logged' => is_user_logged_in(),
),
'should_handle_shipping_in_paypal' => $this->should_handle_shipping_in_paypal && ! $this->is_checkout(),
'vaultingEnabled' => $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' ),
);
if ( 'pay-now' === $this->context() ) {
@ -1333,7 +1361,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages
'integration-date' => PAYPAL_INTEGRATION_DATE,
'components' => implode( ',', $this->components() ),
'vault' => ( $this->can_save_vault_token() || $this->subscription_helper->need_subscription_intent( $subscription_mode ) ) ? 'true' : 'false',
'commit' => in_array( $context, $this->pay_now_contexts, true ) ? 'true' : 'false',
'commit' => 'false',
'intent' => $intent,
);

View file

@ -14,20 +14,25 @@ use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper;
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
use WooCommerce\PayPalCommerce\Button\Helper\WooCommerceOrderCreator;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
/**
* Class ApproveOrderEndpoint
*/
class ApproveOrderEndpoint implements EndpointInterface {
use ContextTrait;
const ENDPOINT = 'ppc-approve-order';
/**
@ -79,6 +84,34 @@ class ApproveOrderEndpoint implements EndpointInterface {
*/
protected $order_helper;
/**
* Whether the final review is enabled.
*
* @var bool
*/
protected $final_review_enabled;
/**
* The WC gateway.
*
* @var PayPalGateway
*/
protected $gateway;
/**
* The WooCommerce order creator.
*
* @var WooCommerceOrderCreator
*/
protected $wc_order_creator;
/**
* The Subscription Helper.
*
* @var SubscriptionHelper
*/
protected $subscription_helper;
/**
* The logger.
*
@ -89,14 +122,18 @@ class ApproveOrderEndpoint implements EndpointInterface {
/**
* ApproveOrderEndpoint constructor.
*
* @param RequestData $request_data The request data helper.
* @param OrderEndpoint $order_endpoint The order endpoint.
* @param SessionHandler $session_handler The session handler.
* @param ThreeDSecure $three_d_secure The 3d secure helper object.
* @param Settings $settings The settings.
* @param DccApplies $dcc_applies The DCC applies object.
* @param OrderHelper $order_helper The order helper.
* @param LoggerInterface $logger The logger.
* @param RequestData $request_data The request data helper.
* @param OrderEndpoint $order_endpoint The order endpoint.
* @param SessionHandler $session_handler The session handler.
* @param ThreeDSecure $three_d_secure The 3d secure helper object.
* @param Settings $settings The settings.
* @param DccApplies $dcc_applies The DCC applies object.
* @param OrderHelper $order_helper The order helper.
* @param bool $final_review_enabled Whether the final review is enabled.
* @param PayPalGateway $gateway The WC gateway.
* @param WooCommerceOrderCreator $wc_order_creator The WooCommerce order creator.
* @param SubscriptionHelper $subscription_helper The subscription helper.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
RequestData $request_data,
@ -106,17 +143,25 @@ class ApproveOrderEndpoint implements EndpointInterface {
Settings $settings,
DccApplies $dcc_applies,
OrderHelper $order_helper,
bool $final_review_enabled,
PayPalGateway $gateway,
WooCommerceOrderCreator $wc_order_creator,
SubscriptionHelper $subscription_helper,
LoggerInterface $logger
) {
$this->request_data = $request_data;
$this->api_endpoint = $order_endpoint;
$this->session_handler = $session_handler;
$this->threed_secure = $three_d_secure;
$this->settings = $settings;
$this->dcc_applies = $dcc_applies;
$this->order_helper = $order_helper;
$this->logger = $logger;
$this->request_data = $request_data;
$this->api_endpoint = $order_endpoint;
$this->session_handler = $session_handler;
$this->threed_secure = $three_d_secure;
$this->settings = $settings;
$this->dcc_applies = $dcc_applies;
$this->order_helper = $order_helper;
$this->final_review_enabled = $final_review_enabled;
$this->gateway = $gateway;
$this->wc_order_creator = $wc_order_creator;
$this->subscription_helper = $subscription_helper;
$this->logger = $logger;
}
/**
@ -182,6 +227,7 @@ class ApproveOrderEndpoint implements EndpointInterface {
);
}
$this->session_handler->replace_order( $order );
wp_send_json_success();
}
@ -200,6 +246,19 @@ class ApproveOrderEndpoint implements EndpointInterface {
$this->session_handler->replace_funding_source( $funding_source );
$this->session_handler->replace_order( $order );
if ( ! $this->subscription_helper->plugin_is_active() && apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false ) ) {
$this->toggle_final_review_enabled_setting();
}
$should_create_wc_order = $data['should_create_wc_order'] ?? false;
if ( ! $this->final_review_enabled && ! $this->is_checkout() && $should_create_wc_order ) {
$wc_order = $this->wc_order_creator->create_from_paypal_order( $order, WC()->cart );
$this->gateway->process_payment( $wc_order->get_id() );
$order_received_url = $wc_order->get_checkout_order_received_url();
wp_send_json_success( array( 'order_received_url' => $order_received_url ) );
}
wp_send_json_success();
return true;
} catch ( Exception $error ) {
@ -216,4 +275,15 @@ class ApproveOrderEndpoint implements EndpointInterface {
return false;
}
}
/**
* Will toggle the "final confirmation" checkbox.
*
* @return void
*/
protected function toggle_final_review_enabled_setting(): void {
$final_review_enabled_setting = $this->settings->has( 'blocks_final_review_enabled' ) && $this->settings->get( 'blocks_final_review_enabled' );
$final_review_enabled_setting ? $this->settings->set( 'blocks_final_review_enabled', false ) : $this->settings->set( 'blocks_final_review_enabled', true );
$this->settings->persist();
}
}

View file

@ -246,6 +246,7 @@ class CreateOrderEndpoint implements EndpointInterface {
$this->parsed_request_data = $data;
$payment_method = $data['payment_method'] ?? '';
$funding_source = $data['funding_source'] ?? '';
$payment_source = $data['payment_source'] ?? '';
$wc_order = null;
if ( 'pay-now' === $data['context'] ) {
$wc_order = wc_get_order( (int) $data['order_id'] );
@ -261,7 +262,7 @@ class CreateOrderEndpoint implements EndpointInterface {
}
$this->purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order );
} else {
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart( null, $this->handle_shipping_in_paypal );
$this->purchase_unit = $this->purchase_unit_factory->from_wc_cart( null, $this->should_handle_shipping_in_paypal( $payment_source ) );
// Do not allow completion by webhooks when started via non-checkout buttons,
// it is needed only for some APMs in checkout.
@ -610,4 +611,20 @@ class CreateOrderEndpoint implements EndpointInterface {
'custom_id' => $order->purchase_units()[0]->custom_id(),
);
}
/**
* Checks if the shipping should be handled in PayPal popup.
*
* @param string $payment_source The payment source.
* @return bool true if the shipping should be handled in PayPal popup, otherwise false.
*/
protected function should_handle_shipping_in_paypal( string $payment_source ): bool {
$is_vaulting_enabled = $this->settings->has( 'vault_enabled' ) && $this->settings->get( 'vault_enabled' );
if ( ! $this->handle_shipping_in_paypal ) {
return false;
}
return ! $is_vaulting_enabled || $payment_source !== 'venmo';
}
}

View file

@ -0,0 +1,228 @@
<?php
/**
* Can create WC orders.
*
* @package WooCommerce\PayPalCommerce\Button\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Helper;
use RuntimeException;
use WC_Cart;
use WC_Order;
use WC_Order_Item_Product;
use WC_Order_Item_Shipping;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Payer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Shipping;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\FundingSource\FundingSourceRenderer;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
/**
* Class WooCommerceOrderCreator
*/
class WooCommerceOrderCreator {
/**
* The funding source renderer.
*
* @var FundingSourceRenderer
*/
protected $funding_source_renderer;
/**
* The Session handler.
*
* @var SessionHandler
*/
protected $session_handler;
/**
* WooCommerceOrderCreator constructor.
*
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
* @param SessionHandler $session_handler The session handler.
*/
public function __construct(
FundingSourceRenderer $funding_source_renderer,
SessionHandler $session_handler
) {
$this->funding_source_renderer = $funding_source_renderer;
$this->session_handler = $session_handler;
}
/**
* Creates WC order based on given PayPal order.
*
* @param Order $order The PayPal order.
* @param WC_Cart $wc_cart The Cart.
* @return WC_Order The WC order.
* @throws RuntimeException If problem creating.
*/
public function create_from_paypal_order( Order $order, WC_Cart $wc_cart ): WC_Order {
$wc_order = wc_create_order();
if ( ! $wc_order instanceof WC_Order ) {
throw new RuntimeException( 'Problem creating WC order.' );
}
$this->configure_line_items( $wc_order, $wc_cart );
$this->configure_shipping( $wc_order, $order->payer(), $order->purchase_units()[0]->shipping() );
$this->configure_payment_source( $wc_order );
$this->configure_customer( $wc_order );
$this->configure_coupons( $wc_order, $wc_cart->get_applied_coupons() );
$wc_order->calculate_totals();
$wc_order->save();
return $wc_order;
}
/**
* Configures the line items.
*
* @param WC_Order $wc_order The WC order.
* @param WC_Cart $wc_cart The Cart.
* @return void
*/
protected function configure_line_items( WC_Order $wc_order, WC_Cart $wc_cart ): void {
$cart_contents = $wc_cart->get_cart();
foreach ( $cart_contents as $cart_item ) {
$product_id = $cart_item['product_id'] ?? 0;
$variation_id = $cart_item['variation_id'] ?? 0;
$quantity = $cart_item['quantity'] ?? 0;
$variation_attributes = $cart_item['variation'];
$item = new WC_Order_Item_Product();
$item->set_product_id( $product_id );
$item->set_quantity( $quantity );
if ( $variation_id ) {
$item->set_variation_id( $variation_id );
$item->set_variation( $variation_attributes );
}
$product = wc_get_product( $variation_id ?: $product_id );
if ( ! $product ) {
return;
}
$item->set_name( $product->get_name() );
$item->set_subtotal( $product->get_price() * $quantity );
$item->set_total( $product->get_price() * $quantity );
$wc_order->add_item( $item );
}
}
/**
* Configures the shipping & billing addresses for WC order from given payer.
*
* @param WC_Order $wc_order The WC order.
* @param Payer|null $payer The payer.
* @param Shipping|null $shipping The shipping.
* @return void
*/
protected function configure_shipping( WC_Order $wc_order, ?Payer $payer, ?Shipping $shipping ): void {
$shipping_address = null;
$billing_address = null;
$shipping_options = null;
if ( $payer ) {
$address = $payer->address();
$payer_name = $payer->name();
$billing_address = array(
'first_name' => $payer_name ? $payer_name->given_name() : '',
'last_name' => $payer_name ? $payer_name->surname() : '',
'address_1' => $address ? $address->address_line_1() : '',
'address_2' => $address ? $address->address_line_2() : '',
'city' => $address ? $address->admin_area_2() : '',
'state' => $address ? $address->admin_area_1() : '',
'postcode' => $address ? $address->postal_code() : '',
'country' => $address ? $address->country_code() : '',
);
}
if ( $shipping ) {
$address = $shipping->address();
$shipping_address = array(
'first_name' => $shipping->name(),
'last_name' => '',
'address_1' => $address->address_line_1(),
'address_2' => $address->address_line_2(),
'city' => $address->admin_area_2(),
'state' => $address->admin_area_1(),
'postcode' => $address->postal_code(),
'country' => $address->country_code(),
);
$shipping_options = $shipping->options()[0] ?? '';
}
if ( $shipping_address ) {
$wc_order->set_shipping_address( $shipping_address );
}
if ( $billing_address || $shipping_address ) {
$wc_order->set_billing_address( $billing_address ?: $shipping_address );
}
if ( $shipping_options ) {
$shipping = new WC_Order_Item_Shipping();
$shipping->set_method_title( $shipping_options->label() );
$shipping->set_method_id( $shipping_options->id() );
$shipping->set_total( $shipping_options->amount()->value_str() );
$wc_order->add_item( $shipping );
}
}
/**
* Configures the payment source.
*
* @param WC_Order $wc_order The WC order.
* @return void
*/
protected function configure_payment_source( WC_Order $wc_order ): void {
$funding_source = $this->session_handler->funding_source();
$wc_order->set_payment_method( PayPalGateway::ID );
if ( $funding_source ) {
$wc_order->set_payment_method_title( $this->funding_source_renderer->render_name( $funding_source ) );
}
}
/**
* Configures the customer ID.
*
* @param WC_Order $wc_order The WC order.
* @return void
*/
protected function configure_customer( WC_Order $wc_order ): void {
$current_user = wp_get_current_user();
if ( $current_user->ID !== 0 ) {
$wc_order->set_customer_id( $current_user->ID );
}
}
/**
* Configures the applied coupons.
*
* @param WC_Order $wc_order The WC order.
* @param string[] $coupons The list of applied coupons.
* @return void
*/
protected function configure_coupons( WC_Order $wc_order, array $coupons ): void {
foreach ( $coupons as $coupon_code ) {
$wc_order->apply_coupon( $coupon_code );
}
}
}