woocommerce-paypal-payments/modules/ppcp-button/src/Helper/WooCommerceOrderCreator.php

361 lines
11 KiB
PHP
Raw Normal View History

2024-05-14 15:29:18 +04:00
<?php
/**
* Can create WC orders.
*
* @package WooCommerce\PayPalCommerce\Button\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Button\Helper;
2024-07-18 20:15:02 +04:00
use Exception;
2024-05-14 19:26:05 +04:00
use RuntimeException;
2024-05-15 15:30:39 +04:00
use WC_Cart;
2024-07-18 20:15:02 +04:00
use WC_Data_Exception;
2024-05-14 15:29:18 +04:00
use WC_Order;
2024-05-16 17:21:17 +04:00
use WC_Order_Item_Product;
2024-05-14 15:29:18 +04:00
use WC_Order_Item_Shipping;
2024-07-17 19:15:19 +04:00
use WC_Product;
2024-05-24 19:08:44 +04:00
use WC_Subscription;
2024-05-21 18:42:03 +04:00
use WC_Subscriptions_Product;
2024-07-17 19:15:19 +04:00
use WC_Tax;
2024-05-14 15:29:18 +04:00
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;
2024-05-21 18:42:03 +04:00
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;
2024-05-24 19:08:44 +04:00
use WP_Error;
2024-05-14 15:29:18 +04:00
/**
* Class WooCommerceOrderCreator
*/
class WooCommerceOrderCreator {
/**
* The funding source renderer.
*
* @var FundingSourceRenderer
*/
protected $funding_source_renderer;
/**
* The Session handler.
*
* @var SessionHandler
*/
protected $session_handler;
2024-05-21 18:42:03 +04:00
/**
* The subscription helper
*
* @var SubscriptionHelper
*/
protected $subscription_helper;
2024-05-14 15:29:18 +04:00
/**
* WooCommerceOrderCreator constructor.
*
* @param FundingSourceRenderer $funding_source_renderer The funding source renderer.
* @param SessionHandler $session_handler The session handler.
2024-05-21 18:42:03 +04:00
* @param SubscriptionHelper $subscription_helper The subscription helper.
2024-05-14 15:29:18 +04:00
*/
public function __construct(
FundingSourceRenderer $funding_source_renderer,
2024-05-21 18:42:03 +04:00
SessionHandler $session_handler,
SubscriptionHelper $subscription_helper
2024-05-14 15:29:18 +04:00
) {
$this->funding_source_renderer = $funding_source_renderer;
$this->session_handler = $session_handler;
2024-05-21 18:42:03 +04:00
$this->subscription_helper = $subscription_helper;
2024-05-14 15:29:18 +04:00
}
/**
* Creates WC order based on given PayPal order.
*
2024-05-15 15:30:39 +04:00
* @param Order $order The PayPal order.
* @param WC_Cart $wc_cart The Cart.
2024-05-14 15:29:18 +04:00
* @return WC_Order The WC order.
2024-05-17 15:51:17 +04:00
* @throws RuntimeException If problem creating.
2024-05-14 15:29:18 +04:00
*/
2024-05-15 15:30:39 +04:00
public function create_from_paypal_order( Order $order, WC_Cart $wc_cart ): WC_Order {
2024-05-14 15:29:18 +04:00
$wc_order = wc_create_order();
2024-05-14 19:26:05 +04:00
if ( ! $wc_order instanceof WC_Order ) {
throw new RuntimeException( 'Problem creating WC order.' );
}
2024-07-18 20:15:02 +04:00
try {
$payer = $order->payer();
$shipping = $order->purchase_units()[0]->shipping();
$this->configure_payment_source( $wc_order );
$this->configure_customer( $wc_order );
$this->configure_line_items( $wc_order, $wc_cart, $payer, $shipping );
$this->configure_shipping( $wc_order, $payer, $shipping, $wc_cart );
$this->configure_coupons( $wc_order, $wc_cart->get_applied_coupons() );
$wc_order->calculate_totals();
$wc_order->save();
} catch ( Exception $exception ) {
$wc_order->delete( true );
throw new RuntimeException( 'Failed to create WooCommerce order: ' . $exception->getMessage() );
}
2024-05-14 15:29:18 +04:00
return $wc_order;
}
/**
* Configures the line items.
*
2024-05-21 18:42:03 +04:00
* @param WC_Order $wc_order The WC order.
* @param WC_Cart $wc_cart The Cart.
* @param Payer|null $payer The payer.
* @param Shipping|null $shipping The shipping.
2024-05-14 15:29:18 +04:00
* @return void
2024-07-17 19:15:19 +04:00
* @psalm-suppress InvalidScalarArgument
2024-05-14 15:29:18 +04:00
*/
2024-05-21 18:42:03 +04:00
protected function configure_line_items( WC_Order $wc_order, WC_Cart $wc_cart, ?Payer $payer, ?Shipping $shipping ): void {
2024-05-15 15:30:39 +04:00
$cart_contents = $wc_cart->get_cart();
2024-05-16 17:21:17 +04:00
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'];
2024-05-14 15:29:18 +04:00
2024-05-16 17:21:17 +04:00
$item = new WC_Order_Item_Product();
$item->set_product_id( $product_id );
$item->set_quantity( $quantity );
2024-05-14 19:26:05 +04:00
2024-05-16 17:21:17 +04:00
if ( $variation_id ) {
$item->set_variation_id( $variation_id );
$item->set_variation( $variation_attributes );
}
$product = wc_get_product( $variation_id ?: $product_id );
2024-05-15 15:30:39 +04:00
if ( ! $product ) {
2024-05-14 19:26:05 +04:00
return;
}
2024-07-17 19:15:19 +04:00
$subtotal = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) );
$subtotal = apply_filters( 'woocommerce_paypal_payments_shipping_callback_cart_line_item_total', $subtotal, $cart_item );
2024-05-21 18:42:03 +04:00
2024-05-16 17:21:17 +04:00
$item->set_name( $product->get_name() );
2024-07-17 19:15:19 +04:00
$item->set_subtotal( $subtotal );
$item->set_total( $subtotal );
$this->configure_taxes( $product, $item, $subtotal );
2024-05-21 18:42:03 +04:00
$product_id = $product->get_id();
if ( $this->is_subscription( $product_id ) ) {
$subscription = $this->create_subscription( $wc_order, $product_id );
$sign_up_fee = WC_Subscriptions_Product::get_sign_up_fee( $product );
2024-07-17 19:15:19 +04:00
$subscription_total = (float) $subtotal + (float) $sign_up_fee;
2024-05-21 18:42:03 +04:00
$item->set_subtotal( $subscription_total );
$item->set_total( $subscription_total );
$subscription->add_product( $product );
2024-07-18 20:19:33 +04:00
$this->configure_shipping( $subscription, $payer, $shipping, $wc_cart );
2024-05-21 18:42:03 +04:00
$this->configure_payment_source( $subscription );
$this->configure_coupons( $subscription, $wc_cart->get_applied_coupons() );
$dates = array(
'trial_end' => WC_Subscriptions_Product::get_trial_expiration_date( $product_id ),
'next_payment' => WC_Subscriptions_Product::get_first_renewal_payment_date( $product_id ),
'end' => WC_Subscriptions_Product::get_expiration_date( $product_id ),
);
$subscription->update_dates( $dates );
$subscription->calculate_totals();
$subscription->payment_complete_for_order( $wc_order );
}
2024-05-16 17:21:17 +04:00
$wc_order->add_item( $item );
2024-05-14 15:29:18 +04:00
}
}
/**
* Configures the shipping & billing addresses for WC order from given payer.
*
* @param WC_Order $wc_order The WC order.
2024-05-14 19:26:05 +04:00
* @param Payer|null $payer The payer.
2024-05-14 15:29:18 +04:00
* @param Shipping|null $shipping The shipping.
2024-07-18 20:15:02 +04:00
* @param WC_Cart $wc_cart The Cart.
2024-05-14 15:29:18 +04:00
* @return void
2024-07-18 20:15:02 +04:00
* @throws WC_Data_Exception|RuntimeException When failing to configure shipping.
2024-05-14 15:29:18 +04:00
*/
2024-07-18 20:15:02 +04:00
protected function configure_shipping( WC_Order $wc_order, ?Payer $payer, ?Shipping $shipping, WC_Cart $wc_cart ): void {
2024-05-14 15:29:18 +04:00
$shipping_address = null;
2024-05-15 15:30:39 +04:00
$billing_address = null;
2024-05-14 19:26:05 +04:00
$shipping_options = null;
2024-05-15 15:30:39 +04:00
if ( $payer ) {
$address = $payer->address();
$payer_name = $payer->name();
2024-05-14 19:26:05 +04:00
$billing_address = array(
2024-05-15 15:30:39 +04:00
'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() : '',
2024-05-14 19:26:05 +04:00
);
}
2024-05-14 15:29:18 +04:00
if ( $shipping ) {
$address = $shipping->address();
$shipping_address = array(
2024-05-14 19:26:05 +04:00
'first_name' => $shipping->name(),
2024-05-14 15:29:18 +04:00
'last_name' => '',
2024-05-14 19:26:05 +04:00
'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(),
2024-05-14 15:29:18 +04:00
);
2024-05-14 19:26:05 +04:00
$shipping_options = $shipping->options()[0] ?? '';
2024-05-14 15:29:18 +04:00
}
2024-07-18 20:15:02 +04:00
if ( $wc_cart->needs_shipping() && empty( $shipping_options ) ) {
throw new RuntimeException( 'No shipping method has been selected.' );
}
2024-05-14 19:26:05 +04:00
if ( $shipping_address ) {
$wc_order->set_shipping_address( $shipping_address );
}
2024-05-14 15:29:18 +04:00
2024-05-14 19:26:05 +04:00
if ( $billing_address || $shipping_address ) {
$wc_order->set_billing_address( $billing_address ?: $shipping_address );
}
2024-05-14 15:29:18 +04:00
if ( $shipping_options ) {
$shipping = new WC_Order_Item_Shipping();
$shipping->set_method_title( $shipping_options->label() );
$shipping->set_method_id( $shipping_options->id() );
2024-05-14 19:26:05 +04:00
$shipping->set_total( $shipping_options->amount()->value_str() );
2024-05-14 15:29:18 +04:00
2024-05-21 18:42:03 +04:00
$items = $wc_order->get_items();
$items_in_package = array();
foreach ( $items as $item ) {
2024-05-24 19:08:44 +04:00
$items_in_package[] = $item->get_name() . ' &times; ' . (string) $item->get_quantity();
2024-05-21 18:42:03 +04:00
}
$shipping->add_meta_data( __( 'Items', 'woocommerce-paypal-payments' ), implode( ', ', $items_in_package ) );
2024-05-14 15:29:18 +04:00
$wc_order->add_item( $shipping );
}
$wc_order->calculate_totals();
2024-05-14 15:29:18 +04:00
}
/**
* 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 );
2024-05-14 19:26:05 +04:00
if ( $funding_source ) {
$wc_order->set_payment_method_title( $this->funding_source_renderer->render_name( $funding_source ) );
}
2024-05-14 15:29:18 +04:00
}
/**
* 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 );
}
}
2024-05-15 15:30:39 +04:00
/**
* 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 );
}
}
2024-07-17 19:15:19 +04:00
/**
* Configures the taxes.
*
* @param WC_Product $product The Product.
* @param WC_Order_Item_Product $item The line item.
* @param float|string $subtotal The subtotal.
* @return void
* @psalm-suppress InvalidScalarArgument
*/
protected function configure_taxes( WC_Product $product, WC_Order_Item_Product $item, $subtotal ): void {
$tax_rates = WC_Tax::get_rates( $product->get_tax_class() );
$taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, true );
$item->set_tax_class( $product->get_tax_class() );
$item->set_total_tax( (float) array_sum( $taxes ) );
}
2024-05-21 18:42:03 +04:00
/**
* Checks if the product with given ID is WC subscription.
*
* @param int $product_id The product ID.
* @return bool true if the product is subscription, otherwise false.
*/
protected function is_subscription( int $product_id ): bool {
if ( ! $this->subscription_helper->plugin_is_active() ) {
return false;
}
return WC_Subscriptions_Product::is_subscription( $product_id );
}
/**
* Creates WC subscription from given order and product ID.
*
* @param WC_Order $wc_order The WC order.
* @param int $product_id The product ID.
2024-05-24 19:08:44 +04:00
* @return WC_Subscription The subscription order
* @throws RuntimeException If problem creating.
2024-05-21 18:42:03 +04:00
*/
2024-05-24 19:08:44 +04:00
protected function create_subscription( WC_Order $wc_order, int $product_id ): WC_Subscription {
$subscription = wcs_create_subscription(
2024-05-21 18:42:03 +04:00
array(
'order_id' => $wc_order->get_id(),
'status' => 'pending',
'billing_period' => WC_Subscriptions_Product::get_period( $product_id ),
'billing_interval' => WC_Subscriptions_Product::get_interval( $product_id ),
'customer_id' => $wc_order->get_customer_id(),
)
);
2024-05-24 19:08:44 +04:00
if ( $subscription instanceof WP_Error ) {
throw new RuntimeException( $subscription->get_error_message() );
}
return $subscription;
2024-05-21 18:42:03 +04:00
}
2024-05-14 15:29:18 +04:00
}