2022-03-07 12:54:02 +01:00
< ? php
2022-04-14 17:17:56 +02:00
/**
* Create order for PUI .
*
* @ package WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice
*/
2022-03-07 12:54:02 +01:00
declare ( strict_types = 1 );
2022-04-22 12:19:55 +02:00
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint ;
2022-03-07 12:54:02 +01:00
use Psr\Log\LoggerInterface ;
use RuntimeException ;
2022-07-04 12:20:34 +02:00
use stdClass ;
2022-08-30 14:40:03 +02:00
use WC_Customer ;
2022-08-29 15:18:40 +02:00
use WC_Order ;
2022-08-30 14:40:03 +02:00
use WC_Order_Item_Fee ;
2022-08-29 15:18:40 +02:00
use WC_Order_Item_Product ;
use WC_Product ;
use WC_Tax ;
2022-03-07 12:54:02 +01:00
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer ;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait ;
2022-08-29 15:18:40 +02:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item ;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money ;
2022-03-07 12:54:02 +01:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order ;
use WooCommerce\PayPalCommerce\ApiClient\Entity\PurchaseUnit ;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException ;
use WooCommerce\PayPalCommerce\ApiClient\Factory\OrderFactory ;
2022-04-22 12:19:55 +02:00
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\FraudNet ;
2022-04-22 15:21:00 +02:00
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayUponInvoice\PaymentSource ;
2022-04-19 11:31:47 +02:00
use WP_Error ;
2022-03-07 12:54:02 +01:00
2022-04-14 17:17:56 +02:00
/**
* Class OrderEndpoint .
*/
2022-04-22 11:49:30 +02:00
class PayUponInvoiceOrderEndpoint {
2022-03-07 12:54:02 +01:00
use RequestTrait ;
/**
2022-04-14 17:17:56 +02:00
* The host .
*
2022-03-07 12:54:02 +01:00
* @ var string
*/
protected $host ;
/**
2022-04-14 17:17:56 +02:00
* The bearer .
*
2022-03-07 12:54:02 +01:00
* @ var Bearer
*/
protected $bearer ;
/**
2022-04-14 17:17:56 +02:00
* The order factory .
*
2022-03-07 12:54:02 +01:00
* @ var OrderFactory
*/
protected $order_factory ;
2022-03-09 16:23:03 +01:00
/**
2022-04-14 17:17:56 +02:00
* The FraudNet entity .
*
2022-03-09 16:23:03 +01:00
* @ var FraudNet
*/
2022-04-14 17:17:56 +02:00
protected $fraudnet ;
2022-03-07 12:54:02 +01:00
2022-03-09 17:22:17 +01:00
/**
2022-04-14 17:17:56 +02:00
* The logger .
*
2022-03-09 17:22:17 +01:00
* @ var LoggerInterface
*/
protected $logger ;
2022-04-14 17:17:56 +02:00
/**
* OrderEndpoint constructor .
*
* @ param string $host The host .
* @ param Bearer $bearer The bearer .
* @ param OrderFactory $order_factory The order factory .
* @ param FraudNet $fraudnet FrauNet entity .
* @ param LoggerInterface $logger The logger .
*/
2022-03-09 16:23:03 +01:00
public function __construct (
string $host ,
Bearer $bearer ,
OrderFactory $order_factory ,
2022-04-14 17:17:56 +02:00
FraudNet $fraudnet ,
2022-03-09 16:23:03 +01:00
LoggerInterface $logger
) {
2022-03-07 14:49:41 +01:00
$this -> host = $host ;
$this -> bearer = $bearer ;
2022-03-07 12:54:02 +01:00
$this -> order_factory = $order_factory ;
2022-03-07 14:49:41 +01:00
$this -> logger = $logger ;
2022-04-14 17:17:56 +02:00
$this -> fraudnet = $fraudnet ;
2022-03-07 12:54:02 +01:00
}
/**
* Creates an order .
*
* @ param PurchaseUnit [] $items The purchase unit items for the order .
2022-04-14 17:17:56 +02:00
* @ param PaymentSource $payment_source The payment source .
2022-03-07 12:54:02 +01:00
* @ return Order
2022-04-14 17:17:56 +02:00
* @ throws RuntimeException When there is a problem with the payment source .
* @ throws PayPalApiException When there is a problem creating the order .
2022-03-07 12:54:02 +01:00
*/
2022-08-29 15:18:40 +02:00
public function create ( array $items , PaymentSource $payment_source , WC_Order $wc_order ) : Order {
2022-04-28 11:02:00 +02:00
2022-03-07 12:54:02 +01:00
$data = array (
2022-03-07 14:49:41 +01:00
'intent' => 'CAPTURE' ,
2022-03-07 12:54:02 +01:00
'processing_instruction' => 'ORDER_COMPLETE_ON_PAYMENT_APPROVAL' ,
2022-03-07 14:49:41 +01:00
'purchase_units' => array_map (
2022-03-07 12:54:02 +01:00
static function ( PurchaseUnit $item ) : array {
return $item -> to_array ();
},
$items
),
2022-03-07 14:49:41 +01:00
'payment_source' => array (
2022-03-09 17:22:17 +01:00
'pay_upon_invoice' => $payment_source -> to_array (),
2022-03-07 12:54:02 +01:00
),
);
2022-08-30 14:40:03 +02:00
$data = $this -> ensure_taxes ( $wc_order , $data );
2022-04-28 11:02:00 +02:00
$data = $this -> ensure_shipping ( $data , $payment_source -> to_array () );
2022-03-07 12:54:02 +01:00
$bearer = $this -> bearer -> bearer ();
2022-03-07 14:49:41 +01:00
$url = trailingslashit ( $this -> host ) . 'v2/checkout/orders' ;
$args = array (
2022-03-07 12:54:02 +01:00
'method' => 'POST' ,
'headers' => array (
2022-03-07 14:49:41 +01:00
'Authorization' => 'Bearer ' . $bearer -> token (),
'Content-Type' => 'application/json' ,
'Prefer' => 'return=representation' ,
2022-04-25 14:31:43 +02:00
'PayPal-Client-Metadata-Id' => $this -> fraudnet -> session_id (),
2022-03-07 14:49:41 +01:00
'PayPal-Request-Id' => uniqid ( 'ppcp-' , true ),
2022-03-07 12:54:02 +01:00
),
'body' => wp_json_encode ( $data ),
);
$response = $this -> request ( $url , $args );
2022-04-19 12:41:13 +02:00
if ( $response instanceof WP_Error ) {
2022-03-07 14:49:41 +01:00
throw new RuntimeException ( $response -> get_error_message () );
2022-03-07 12:54:02 +01:00
}
$json = json_decode ( $response [ 'body' ] );
$status_code = ( int ) wp_remote_retrieve_response_code ( $response );
2022-04-14 17:17:56 +02:00
if ( ! in_array ( $status_code , array ( 200 , 201 ), true ) ) {
2022-04-04 12:58:44 +02:00
$issue = $json -> details [ 0 ] -> issue ? ? null ;
2022-04-08 12:03:24 +02:00
2022-04-14 17:17:56 +02:00
$site_country_code = explode ( '-' , get_bloginfo ( 'language' ) )[ 0 ] ? ? '' ;
if ( 'PAYMENT_SOURCE_INFO_CANNOT_BE_VERIFIED' === $issue ) {
if ( 'de' === $site_country_code ) {
throw new RuntimeException ( 'Die Kombination aus Ihrem Namen und Ihrer Anschrift konnte nicht validiert werden. Bitte korrigieren Sie Ihre Daten und versuchen Sie es erneut. Weitere Informationen finden Sie in den Ratepay <a href="https://www.ratepay.com/legal-payment-dataprivacy/?lang=de" target="_blank">Datenschutzbestimmungen</a> oder nutzen Sie das Ratepay <a href="https://www.ratepay.com/kontakt/" target="_blank">Kontaktformular</a>.' );
2022-04-08 12:03:24 +02:00
} else {
2022-04-14 17:17:56 +02:00
throw new RuntimeException ( 'The combination of your name and address could not be validated. Please correct your data and try again. You can find further information in the <a href="https://www.ratepay.com/en/ratepay-data-privacy-statement/" target="_blank">Ratepay Data Privacy Statement</a> or you can contact Ratepay using this <a href="https://www.ratepay.com/en/contact/" target="_blank">contact form</a>.' );
2022-04-08 12:03:24 +02:00
}
2022-04-04 12:58:44 +02:00
}
2022-04-14 17:17:56 +02:00
if ( 'PAYMENT_SOURCE_DECLINED_BY_PROCESSOR' === $issue ) {
if ( 'de' === $site_country_code ) {
throw new RuntimeException ( 'Die gewählte Zahlungsart kann nicht genutzt werden. Diese Entscheidung basiert auf einem automatisierten <a href="https://www.ratepay.com/legal-payment-dataprivacy/?lang=de" target="_blank">Datenverarbeitungsverfahren</a>. Weitere Informationen finden Sie in den Ratepay Datenschutzbestimmungen oder nutzen Sie das Ratepay <a href="https://www.ratepay.com/kontakt/" target="_blank">Kontaktformular</a>.' );
2022-04-08 12:03:24 +02:00
} else {
2022-04-14 17:17:56 +02:00
throw new RuntimeException ( 'It is not possible to use the selected payment method. This decision is based on automated data processing. You can find further information in the <a href="https://www.ratepay.com/en/ratepay-data-privacy-statement/" target="_blank">Ratepay Data Privacy Statement</a> or you can contact Ratepay using this <a href="https://www.ratepay.com/en/contact/" target="_blank">contact form</a>.' );
2022-04-08 12:03:24 +02:00
}
2022-04-04 12:58:44 +02:00
}
2022-03-07 14:49:41 +01:00
throw new PayPalApiException ( $json , $status_code );
2022-03-07 12:54:02 +01:00
}
return $this -> order_factory -> from_paypal_response ( $json );
}
2022-03-20 14:15:56 +01:00
2022-04-14 17:17:56 +02:00
/**
2022-07-04 12:20:34 +02:00
* Get PayPal order as object .
2022-04-14 17:17:56 +02:00
*
* @ param string $id The PayPal order ID .
2022-07-04 12:20:34 +02:00
* @ return stdClass
2022-04-14 17:17:56 +02:00
* @ throws RuntimeException When there is a problem getting the order .
* @ throws PayPalApiException When there is a problem getting the order .
*/
2022-07-04 12:20:34 +02:00
public function order ( string $id ) : stdClass {
2022-03-20 14:15:56 +01:00
$bearer = $this -> bearer -> bearer ();
$url = trailingslashit ( $this -> host ) . 'v2/checkout/orders/' . $id ;
$args = array (
'headers' => array (
'Authorization' => 'Bearer ' . $bearer -> token (),
'Content-Type' => 'application/json' ,
2022-04-14 17:17:56 +02:00
'PayPal-Request-Id' => uniqid ( 'ppcp-' , true ),
2022-03-20 14:15:56 +01:00
),
);
$response = $this -> request ( $url , $args );
2022-04-19 11:31:47 +02:00
if ( $response instanceof WP_Error ) {
2022-03-20 14:15:56 +01:00
throw new RuntimeException ( $response -> get_error_message () );
}
$json = json_decode ( $response [ 'body' ] );
$status_code = ( int ) wp_remote_retrieve_response_code ( $response );
if ( 200 !== $status_code ) {
throw new PayPalApiException ( $json , $status_code );
}
2022-07-04 12:20:34 +02:00
return $json ;
2022-03-20 14:15:56 +01:00
}
2022-04-28 11:02:00 +02:00
/**
* Ensures purchase units contains shipping by using payment source data .
*
* @ param array $data The data .
* @ param array $payment_source The payment source .
* @ return array
*/
private function ensure_shipping ( array $data , array $payment_source ) : array {
if ( isset ( $data [ 'purchase_units' ][ 0 ][ 'shipping' ] ) ) {
return $data ;
}
$given_name = $payment_source [ 'name' ][ 'given_name' ] ? ? '' ;
$surname = $payment_source [ 'name' ][ 'surname' ] ? ? '' ;
$address = $payment_source [ 'billing_address' ] ? ? array ();
2022-04-28 11:26:25 +02:00
$data [ 'purchase_units' ][ 0 ][ 'shipping' ][ 'name' ] = array ( 'full_name' => $given_name . ' ' . $surname );
$data [ 'purchase_units' ][ 0 ][ 'shipping' ][ 'address' ] = $address ;
2022-04-28 11:02:00 +02:00
return $data ;
}
2022-08-29 15:18:40 +02:00
/**
* @ param WC_Order $wc_order
* @ param array $data
* @ param array $items
* @ return array
*/
2022-08-29 16:16:34 +02:00
private function ensure_taxes ( WC_Order $wc_order , array $data ) : array
2022-08-29 15:18:40 +02:00
{
$items = array_map (
function ( WC_Order_Item_Product $item ) use ( $wc_order ) : Item {
$product = $item -> get_product ();
$currency = $wc_order -> get_currency ();
2022-08-30 14:40:03 +02:00
$quantity = $item -> get_quantity ();
2022-08-29 15:18:40 +02:00
$unit_amount = $wc_order -> get_item_subtotal ( $item , false , false );
$tax_rates = WC_Tax :: get_rates ( $product -> get_tax_class ());
$tax_rate = reset ( $tax_rates )[ 'rate' ] ? ? 0 ;
$tax = $unit_amount * ( $tax_rate / 100 );
$tax = new Money ( $tax , $currency );
return new Item (
mb_substr ( $item -> get_name (), 0 , 127 ),
new Money ( $wc_order -> get_item_subtotal ( $item , false , false ), $currency ),
$quantity ,
substr ( wp_strip_all_tags ( $product instanceof WC_Product ? $product -> get_description () : '' ),
0 , 127 ) ? : '' ,
$tax ,
$product instanceof WC_Product ? $product -> get_sku () : '' ,
( $product instanceof WC_Product && $product -> is_virtual ()) ? Item :: DIGITAL_GOODS : Item :: PHYSICAL_GOODS ,
2022-08-30 14:40:03 +02:00
$tax_rate
2022-08-29 15:18:40 +02:00
);
},
$wc_order -> get_items (),
array_keys ( $wc_order -> get_items ())
);
2022-08-30 14:40:03 +02:00
$fees = array_map (
function ( WC_Order_Item_Fee $item ) use ( $wc_order ) : Item {
$currency = $wc_order -> get_currency ();
$unit_amount = $item -> get_amount ();
$total_tax = $item -> get_total_tax ();
$tax_rate = ( $total_tax / $unit_amount ) * 100 ;
$tax = $unit_amount * ( $tax_rate / 100 );
$tax = new Money ( $tax , $currency );
return new Item (
$item -> get_name (),
new Money ( ( float ) $item -> get_amount (), $wc_order -> get_currency () ),
$item -> get_quantity (),
'' ,
$tax ,
'' ,
'PHYSICAL_GOODS' ,
$tax_rate
);
},
$wc_order -> get_fees ()
);
$items = array_merge ( $items , $fees );
2022-08-29 15:18:40 +02:00
$items_count = count ( $data [ 'purchase_units' ][ 0 ][ 'items' ]);
for ( $i = 0 ; $i < $items_count ; $i ++ ) {
if ( ! isset ( $data [ 'purchase_units' ][ 0 ][ 'items' ][ $i ][ 'tax' ])) {
$data [ 'purchase_units' ][ 0 ][ 'items' ][ $i ] = $items [ $i ] -> to_array ();
}
}
$shipping = ( float ) $wc_order -> calculate_shipping ();
$total = 0 ;
$tax_total = 0 ;
foreach ( $items as $item ) {
$unit_amount = ( float ) $item -> unit_amount () -> value ();
$tax = ( float ) $item -> tax () -> value ();
$qt = $item -> quantity ();
$total += (( $unit_amount + $tax ) * $qt );
$tax_total += $tax * $qt ;
}
2022-08-29 16:16:34 +02:00
$data [ 'purchase_units' ][ 0 ][ 'amount' ][ 'value' ] = number_format ( $total + $shipping , 2 , '.' , '' );
$data [ 'purchase_units' ][ 0 ][ 'amount' ][ 'breakdown' ][ 'tax_total' ][ 'value' ] = number_format ( $tax_total , 2 , '.' , '' );
$shipping_taxes = ( float ) $wc_order -> get_shipping_tax ();
2022-08-30 14:40:03 +02:00
$fees_taxes = 0 ;
foreach ( $wc_order -> get_fees () as $fee ) {
$unit_amount = $fee -> get_amount ();
$total_tax = $fee -> get_total_tax ();
$tax_rate = ( $total_tax / $unit_amount ) * 100 ;
$tax = $unit_amount * ( $tax_rate / 100 );
$fees_taxes += $tax ;
}
if ( $shipping_taxes > 0 || $fees_taxes > 0 ) {
2022-08-29 16:16:34 +02:00
$name = $data [ 'purchase_units' ][ 0 ][ 'items' ][ 0 ][ 'name' ];
$category = $data [ 'purchase_units' ][ 0 ][ 'items' ][ 0 ][ 'category' ];
2022-08-29 16:20:39 +02:00
$tax_rate = $data [ 'purchase_units' ][ 0 ][ 'items' ][ 0 ][ 'tax_rate' ];
2022-08-29 16:16:34 +02:00
unset ( $data [ 'purchase_units' ][ 0 ][ 'items' ]);
$data [ 'purchase_units' ][ 0 ][ 'items' ][ 0 ] = array (
'name' => $name ,
'unit_amount' => array (
'currency_code' => 'EUR' ,
'value' => $data [ 'purchase_units' ][ 0 ][ 'amount' ][ 'breakdown' ][ 'item_total' ][ 'value' ],
),
'category' => $category ,
'quantity' => 1 ,
'tax' => array (
'currency_code' => 'EUR' ,
'value' => number_format ( $tax_total + $shipping_taxes , 2 , '.' , '' ),
),
2022-08-29 16:20:39 +02:00
'tax_rate' => $tax_rate ,
2022-08-29 16:16:34 +02:00
);
$data [ 'purchase_units' ][ 0 ][ 'amount' ][ 'value' ] = number_format ( $total + $shipping + $shipping_taxes , 2 , '.' , '' );
$data [ 'purchase_units' ][ 0 ][ 'amount' ][ 'breakdown' ][ 'tax_total' ][ 'value' ] = number_format ( $tax_total + $shipping_taxes , 2 , '.' , '' );
}
2022-08-29 15:18:40 +02:00
return $data ;
}
2022-03-07 12:54:02 +01:00
}