Process.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. class Laybuy_Processes_CreateQuote_WC_LT_3_6_Process extends Laybuy_Processes_CreateQuote_CreateQuoteAbstract
  3. {
  4. public $checkout;
  5. public function setCheckout($checkout)
  6. {
  7. $this->checkout = $checkout;
  8. }
  9. public function getCheckout()
  10. {
  11. return $this->checkout;
  12. }
  13. public function execute()
  14. {
  15. $checkout = $this->getCheckout();
  16. if (!$checkout) {
  17. throw new InvalidArgumentException(
  18. 'Parameter checkout is not set. Please call setCheckout before executing the process'
  19. );
  20. }
  21. $posted = method_exists($checkout, 'get_posted_data') ? $checkout->get_posted_data() : $checkout->posted;
  22. if ($posted['payment_method'] !== self::PAYMENT_METHOD_LAYBUY) {
  23. return;
  24. }
  25. $quote_id = wp_insert_post( array(
  26. 'post_content' => 'Thank you for your order. Now redirecting to Laybuy to complete your payment...',
  27. 'post_title' => 'Laybuy Order',
  28. 'post_status' => 'publish',
  29. 'post_type' => 'laybuy_quote'
  30. ), true );
  31. if (is_wp_error($quote_id)) {
  32. $errors_str = implode($quote_id->get_error_messages(), ' ');
  33. WC_Laybuy_Logger::error("WC_Payment_Gateway_Laybuy::create_order_quote() returned -1 (Could not create laybuy_quote post. WordPress threw error(s): {$errors_str})");
  34. return -1;
  35. }
  36. WC_Laybuy_Logger::log("New WP Quote created with ID:{$quote_id} and permalink:\"" . get_permalink( $quote_id ) . "\"");
  37. $cart = WC()->cart;
  38. $session = WC()->session;
  39. // Billing
  40. $billing = array();
  41. $billing_encoded = array();
  42. if ( $checkout->checkout_fields['billing'] ) {
  43. foreach ( array_keys( $checkout->checkout_fields['billing'] ) as $field ) {
  44. $field_name = str_replace( 'billing_', '', $field );
  45. $billing[$field_name] = $checkout->get_posted_address_data( $field_name );
  46. $billing_encoded[$field_name] = $this->_encode($billing[ $field_name ]);
  47. }
  48. }
  49. if (empty($billing['phone']) || strlen(preg_replace('/[^0-9+]/i', '', $billing['phone'])) <= 6) {
  50. $billing['phone'] = "00 000 000";
  51. }
  52. $nonce = wp_create_nonce( "laybuy_quote_{$quote_id}_create" );
  53. update_post_meta( $quote_id, '_laybuy_quote_nonce', $nonce );
  54. $data = array(
  55. 'amount' => $cart->total,
  56. 'returnUrl' => $this->_buildReturnUrl($quote_id, $nonce),
  57. 'merchantReference' => $this->_makeUniqueReference($quote_id),
  58. 'customer' => array(
  59. 'firstName' => $billing['first_name'],
  60. 'lastName' => $billing['last_name'],
  61. 'email' => $billing['email'],
  62. 'phone' => $billing['phone']
  63. ),
  64. 'billingAddress' => array(
  65. "address1" => $billing['address_1'],
  66. "city" => $billing['city'],
  67. "postcode" => $billing['postcode'],
  68. "country" => $billing['country'],
  69. ),
  70. 'items' => array()
  71. );
  72. if ('yes' === get_option('woocommerce_prices_include_tax') || get_woocommerce_currency() === WC_Laybuy_Helper::CURRENCY_CODE_GB) {
  73. $data['tax'] = number_format($cart->tax_total + $cart->shipping_tax_total, 2, '.', '');
  74. }
  75. // Shipping total
  76. $shipping_total = $cart->shipping_total;
  77. if ($cart->shipping_tax_total > 0) {
  78. $shipping_total += $cart->shipping_tax_total;
  79. }
  80. // items total
  81. $items_total = $cart->total;
  82. // shipping
  83. if ($shipping_total > 0) {
  84. $data['items'][] = array(
  85. 'id' => 'shipping_fee_for_order#' . $quote_id,
  86. 'description' => 'Shipping fee for this order',
  87. 'quantity' => '1',
  88. 'price' => $shipping_total
  89. );
  90. $items_total -= $shipping_total;
  91. }
  92. // tax
  93. if ($cart->tax_total && 'no' === get_option( 'woocommerce_prices_include_tax' ) ) {
  94. $data['items'][] = array(
  95. 'id' => 'total_tax_amount_for_order#' . $quote_id,
  96. 'description' => 'Tax amount for this order',
  97. 'quantity' => '1',
  98. 'price' => $cart->tax_total
  99. );
  100. $items_total -= $cart->tax_total;
  101. }
  102. $data['items'][] = [
  103. 'id' => 'item_for_order___#' . $quote_id,
  104. 'description' => 'Purchase from ' . get_bloginfo('name'),
  105. 'quantity' => 1,
  106. 'price' => $items_total
  107. ];
  108. // Store data to build a WC_Order object later.
  109. $cart_items = array();
  110. // see WC_Checkout::create_order_line_items
  111. foreach ($cart->get_cart() as $cart_item_key => $values) {
  112. $product = $values['data'];
  113. if ( WC_Laybuy_Helper::is_wc_gt( '3.0' )) {
  114. $cart_items[$cart_item_key] = array(
  115. 'props' => array(
  116. 'quantity' => $values['quantity'],
  117. 'variation' => $this->_encode($values['variation']),
  118. 'subtotal' => $values['line_subtotal'],
  119. 'total' => $values['line_total'],
  120. 'subtotal_tax' => $values['line_subtotal_tax'],
  121. 'total_tax' => $values['line_tax'],
  122. 'taxes' => $values['line_tax_data']
  123. )
  124. );
  125. if ($product) {
  126. $cart_items[$cart_item_key]['id'] = $product->get_id();
  127. $cart_items[$cart_item_key]['props'] = array_merge($cart_items[$cart_item_key]['props'], array(
  128. 'name' => $this->_encode($product->get_name()),
  129. 'tax_class' => $this->_encode($product->get_tax_class()),
  130. 'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),
  131. 'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0
  132. ));
  133. }
  134. // custom fields
  135. foreach( $values as $field => $field_value ) {
  136. if ( $this->_isProductDetailCustom($field) ) {
  137. $cart_items[$cart_item_key][$field] = $this->_encode($field_value);
  138. }
  139. }
  140. } else {
  141. $cart_items[$cart_item_key] = array(
  142. 'class' => $this->_encode(get_class($product)),
  143. 'id' => $product->id,
  144. 'quantity' => $values['quantity'],
  145. 'variation' => $this->_encode($values['variation']),
  146. 'totals' => array(
  147. 'subtotal' => $values['line_subtotal'],
  148. 'subtotal_tax' => $values['line_subtotal_tax'],
  149. 'total' => $values['line_total'],
  150. 'tax' => $values['line_tax'],
  151. 'tax_data' => $values['line_tax_data'] # Since WooCommerce 2.2
  152. )
  153. );
  154. }
  155. }
  156. // Fees
  157. $cart_fees = array();
  158. foreach ( $cart->get_fees() as $fee_key => $fee ) {
  159. $cart_fees[$fee_key] = $this->_encode($fee);
  160. }
  161. // Discounts
  162. if ($cart->has_discount()) {
  163. // The total is stored in $cart->get_total_discount(), but we should also be able to get a list.
  164. $data['discounts'] = array();
  165. foreach ($cart->coupon_discount_amounts as $code => $amount) {
  166. $data['discounts'][] = array(
  167. 'displayName' => $code,
  168. 'amount' => array(
  169. 'amount' => number_format($amount, 2, '.', ''),
  170. 'currency' => get_woocommerce_currency()
  171. )
  172. );
  173. }
  174. }
  175. // Coupons
  176. $cart_coupons = array();
  177. foreach ($cart->get_coupons() as $code => $coupon) {
  178. $cart_coupons[$code] = array(
  179. 'discount_amount' => $cart->get_coupon_discount_amount($code),
  180. 'discount_tax_amount' => $cart->get_coupon_discount_tax_amount($code)
  181. );
  182. }
  183. // Taxes
  184. $cart_taxes = array();
  185. foreach (array_keys($cart->taxes + $cart->shipping_taxes) as $tax_rate_id) {
  186. if ($tax_rate_id && $tax_rate_id !== apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' )) {
  187. $cart_taxes[$tax_rate_id] = array(
  188. 'tax_amount' => $cart->get_tax_amount($tax_rate_id),
  189. 'shipping_tax_amount' => $cart->get_shipping_tax_amount($tax_rate_id)
  190. );
  191. }
  192. }
  193. // Shipping costs.
  194. if ($shipping_total > 0) {
  195. $data['shippingAmount'] = array(
  196. 'amount' => number_format($shipping_total, 2, '.', ''),
  197. 'currency' => get_woocommerce_currency()
  198. );
  199. }
  200. // Shipping address.
  201. $shipping = array();
  202. $shipping_encoded = array();
  203. if ( $checkout->checkout_fields['shipping'] ) {
  204. foreach ( array_keys( $checkout->checkout_fields['shipping'] ) as $field ) {
  205. $field_name = str_replace( 'shipping_', '', $field );
  206. $shipping[ $field_name ] = $checkout->get_posted_address_data( $field_name, 'shipping' );
  207. $shipping_encoded[ $field_name ] = $this->_encode($shipping[ $field_name ]);
  208. }
  209. }
  210. // shipping methods
  211. $chosen_shipping_methods = $session->get( 'chosen_shipping_methods' );
  212. // Shipping packages.
  213. $shipping_packages = array();
  214. foreach (WC()->shipping->get_packages() as $package_key => $package) {
  215. if (isset($package['rates'][$checkout->shipping_methods[$package_key]])) {
  216. $shipping_rate = $package['rates'][$checkout->shipping_methods[$package_key]];
  217. $package_metadata = $shipping_rate->get_meta_data();
  218. $shipping_packages[$package_key] = array();
  219. if (version_compare( WC_VERSION, '3.0.0', '>=' )) {
  220. $shipping_packages[$package_key]['package'] = $this->_encode($package);
  221. } else {
  222. $shipping_packages[$package_key]['id'] = $shipping_rate->id;
  223. $shipping_packages[$package_key]['label'] = $this->_encode($shipping_rate->label);
  224. $shipping_packages[$package_key]['cost'] = $shipping_rate->cost;
  225. $shipping_packages[$package_key]['taxes'] = $shipping_rate->taxes;
  226. $shipping_packages[$package_key]['method_id'] = $shipping_rate->method_id;
  227. }
  228. $shipping_packages[$package_key]['package_metadata'] = $this->_encode($package_metadata);
  229. }
  230. }
  231. WC_Laybuy_Logger::log("Sending a request to create a Laybuy Order");
  232. WC_Laybuy_Logger::log(print_r($data, true));
  233. # Process Get Laybuy Token Start
  234. // Send the order data to Laybuy to get a token.
  235. $response = $this->getProcessManager()->getApiGateway()->createOrder($data);
  236. if ($response === false || $response->result === Laybuy_ApiGateway::PAYMENT_STATUS_ERROR) {
  237. $this->getProcessManager()->cancelQuote($quote_id, 'failed');
  238. # Log the error and return a truthy integer (otherwise WooCommerce will not bypass the standard order creation process).
  239. WC_Laybuy_Logger::error("Laybuy_ApiGateway::createOrder() returned -2 (Laybuy did not provide a token for this order.)");
  240. WC_Laybuy_Logger::error("API Payload: " . print_r($data, true));
  241. WC_Laybuy_Logger::error("API Response: " . var_export($response, true));
  242. return -2;
  243. }
  244. WC_Laybuy_Logger::log("Laybuy Order Created\nResponse Data: ". print_r($response, true));
  245. # Post Create
  246. // Add the meta data to the Laybuy_Quote post record.
  247. add_post_meta( $quote_id, 'status', 'pending' );
  248. add_post_meta( $quote_id, 'token', $response->token );
  249. add_post_meta( $quote_id, 'customer_id', apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) ); # WC_Checkout::$customer_id is private. See WC_Checkout::process_checkout() for how it populates this property.
  250. add_post_meta( $quote_id, 'cart_hash', md5( json_encode( wc_clean( $cart->get_cart_for_session() ) ) . $cart->total ) );
  251. add_post_meta( $quote_id, 'cart_shipping_total', $cart->shipping_total );
  252. add_post_meta( $quote_id, 'cart_shipping_tax_total', $cart->shipping_tax_total );
  253. add_post_meta( $quote_id, 'cart_discount_total', $cart->get_cart_discount_total() );
  254. add_post_meta( $quote_id, 'cart_discount_tax_total', $cart->get_cart_discount_tax_total() );
  255. add_post_meta( $quote_id, 'cart_tax_total', $cart->tax_total );
  256. add_post_meta( $quote_id, 'cart_total', $cart->total );
  257. add_post_meta( $quote_id, 'cart_items', json_encode($cart_items) );
  258. add_post_meta( $quote_id, 'cart_fees', json_encode($cart_fees) );
  259. add_post_meta( $quote_id, 'cart_coupons', json_encode($cart_coupons) );
  260. add_post_meta( $quote_id, 'cart_taxes', json_encode($cart_taxes) );
  261. add_post_meta( $quote_id, 'cart_needs_shipping', (bool)$cart->needs_shipping() );
  262. add_post_meta( $quote_id, 'shipping_packages', json_encode($shipping_packages) );
  263. add_post_meta( $quote_id, 'billing_address', json_encode($billing_encoded) );
  264. add_post_meta( $quote_id, 'shipping_address', json_encode($shipping_encoded) );
  265. add_post_meta( $quote_id, 'api_data', json_encode($data) );
  266. add_post_meta( $quote_id, 'currency', $this->_encode(get_woocommerce_currency()));
  267. add_post_meta( $quote_id, 'total', $this->_encode($cart->total) );
  268. if ( WC_Laybuy_Helper::is_wc_gt( '3.0' ) ) {
  269. add_post_meta( $quote_id, 'chosen_shipping_methods', json_encode($chosen_shipping_methods) );
  270. }
  271. // Store the Checkout Posted Data within a Post Meta to run the woocommerce_checkout_order_processed hooks
  272. add_post_meta( $quote_id, 'posted', json_encode($posted) );
  273. add_post_meta( $quote_id, '$_POST', $this->_encode($_POST) );
  274. $result = array(
  275. 'result' => 'success',
  276. 'redirect' => $response->paymentUrl
  277. );
  278. if ( is_ajax() ) {
  279. wp_send_json( $result );
  280. } else {
  281. wp_redirect( $result['redirect'] );
  282. exit;
  283. }
  284. }
  285. }