WC_Gateway_Paypal_Request

Generates requests to send to PayPal.

Defined (1)

The class is defined in the following location(s).

/includes/gateways/paypal/includes/class-wc-gateway-paypal-request.php  
  1. class WC_Gateway_Paypal_Request { 
  2.  
  3. /** 
  4. * Stores line items to send to PayPal. 
  5. * @var array 
  6. */ 
  7. protected $line_items = array(); 
  8.  
  9. /** 
  10. * Pointer to gateway making the request. 
  11. * @var WC_Gateway_Paypal 
  12. */ 
  13. protected $gateway; 
  14.  
  15. /** 
  16. * Endpoint for requests from PayPal. 
  17. * @var string 
  18. */ 
  19. protected $notify_url; 
  20.  
  21. /** 
  22. * Constructor. 
  23. * @param WC_Gateway_Paypal $gateway 
  24. */ 
  25. public function __construct( $gateway ) { 
  26. $this->gateway = $gateway; 
  27. $this->notify_url = WC()->api_request_url( 'WC_Gateway_Paypal' ); 
  28.  
  29. /** 
  30. * Get the PayPal request URL for an order. 
  31. * @param WC_Order $order 
  32. * @param bool $sandbox 
  33. * @return string 
  34. */ 
  35. public function get_request_url( $order, $sandbox = false ) { 
  36. $paypal_args = http_build_query( $this->get_paypal_args( $order ), '', '&' ); 
  37.  
  38. WC_Gateway_Paypal::log( 'PayPal Request Args for order ' . $order->get_order_number() . ': ' . wc_print_r( $paypal_args, true ) ); 
  39.  
  40. if ( $sandbox ) { 
  41. return 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' . $paypal_args; 
  42. } else { 
  43. return 'https://www.paypal.com/cgi-bin/webscr?' . $paypal_args; 
  44.  
  45. /** 
  46. * Limit length of an arg. 
  47. * @param string $string 
  48. * @param integer $limit 
  49. * @return string 
  50. */ 
  51. protected function limit_length( $string, $limit = 127 ) { 
  52. if ( strlen( $string ) > $limit ) { 
  53. $string = substr( $string, 0, $limit - 3 ) . '...'; 
  54. return $string; 
  55.  
  56. /** 
  57. * Get PayPal Args for passing to PP. 
  58. * @param WC_Order $order 
  59. * @return array 
  60. */ 
  61. protected function get_paypal_args( $order ) { 
  62. WC_Gateway_Paypal::log( 'Generating payment form for order ' . $order->get_order_number() . '. Notify URL: ' . $this->notify_url ); 
  63.  
  64. return apply_filters( 'woocommerce_paypal_args', array_merge( 
  65. array( 
  66. 'cmd' => '_cart',  
  67. 'business' => $this->gateway->get_option( 'email' ),  
  68. 'no_note' => 1,  
  69. 'currency_code' => get_woocommerce_currency(),  
  70. 'charset' => 'utf-8',  
  71. 'rm' => is_ssl() ? 2 : 1,  
  72. 'upload' => 1,  
  73. 'return' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->gateway->get_return_url( $order ) ) ),  
  74. 'cancel_return' => esc_url_raw( $order->get_cancel_order_url_raw() ),  
  75. 'page_style' => $this->gateway->get_option( 'page_style' ),  
  76. 'image_url' => esc_url_raw( $this->gateway->get_option( 'image_url' ) ),  
  77. 'paymentaction' => $this->gateway->get_option( 'paymentaction' ),  
  78. 'bn' => 'WooThemes_Cart',  
  79. 'invoice' => $this->limit_length( $this->gateway->get_option( 'invoice_prefix' ) . $order->get_order_number(), 127 ),  
  80. 'custom' => json_encode( array( 'order_id' => $order->get_id(), 'order_key' => $order->get_order_key() ) ),  
  81. 'notify_url' => $this->limit_length( $this->notify_url, 255 ),  
  82. 'first_name' => $this->limit_length( $order->get_billing_first_name(), 32 ),  
  83. 'last_name' => $this->limit_length( $order->get_billing_last_name(), 64 ),  
  84. 'address1' => $this->limit_length( $order->get_billing_address_1(), 100 ),  
  85. 'address2' => $this->limit_length( $order->get_billing_address_2(), 100 ),  
  86. 'city' => $this->limit_length( $order->get_billing_city(), 40 ),  
  87. 'state' => $this->get_paypal_state( $order->get_billing_country(), $order->get_billing_state() ),  
  88. 'zip' => $this->limit_length( wc_format_postcode( $order->get_billing_postcode(), $order->get_billing_country() ), 32 ),  
  89. 'country' => $this->limit_length( $order->get_billing_country(), 2 ),  
  90. 'email' => $this->limit_length( $order->get_billing_email() ),  
  91. ),  
  92. $this->get_phone_number_args( $order ),  
  93. $this->get_shipping_args( $order ),  
  94. $this->get_line_item_args( $order ) 
  95. ), $order ); 
  96.  
  97. /** 
  98. * Get phone number args for paypal request. 
  99. * @param WC_Order $order 
  100. * @return array 
  101. */ 
  102. protected function get_phone_number_args( $order ) { 
  103. if ( in_array( $order->get_billing_country(), array( 'US', 'CA' ) ) ) { 
  104. $phone_number = str_replace( array( '(', '-', ' ', ')', '.' ), '', $order->get_billing_phone() ); 
  105. $phone_number = ltrim( $phone_number, '+1' ); 
  106. $phone_args = array( 
  107. 'night_phone_a' => substr( $phone_number, 0, 3 ),  
  108. 'night_phone_b' => substr( $phone_number, 3, 3 ),  
  109. 'night_phone_c' => substr( $phone_number, 6, 4 ),  
  110. ); 
  111. } else { 
  112. $phone_args = array( 
  113. 'night_phone_b' => $order->get_billing_phone(),  
  114. ); 
  115. return $phone_args; 
  116.  
  117. /** 
  118. * Get shipping args for paypal request. 
  119. * @param WC_Order $order 
  120. * @return array 
  121. */ 
  122. protected function get_shipping_args( $order ) { 
  123. $shipping_args = array(); 
  124.  
  125. if ( 'yes' == $this->gateway->get_option( 'send_shipping' ) ) { 
  126. $shipping_args['address_override'] = $this->gateway->get_option( 'address_override' ) === 'yes' ? 1 : 0; 
  127. $shipping_args['no_shipping'] = 0; 
  128.  
  129. // If we are sending shipping, send shipping address instead of billing 
  130. $shipping_args['first_name'] = $this->limit_length( $order->get_shipping_first_name(), 32 ); 
  131. $shipping_args['last_name'] = $this->limit_length( $order->get_shipping_last_name(), 64 ); 
  132. $shipping_args['address1'] = $this->limit_length( $order->get_shipping_address_1(), 100 ); 
  133. $shipping_args['address2'] = $this->limit_length( $order->get_shipping_address_2(), 100 ); 
  134. $shipping_args['city'] = $this->limit_length( $order->get_shipping_city(), 40 ); 
  135. $shipping_args['state'] = $this->get_paypal_state( $order->get_shipping_country(), $order->get_shipping_state() ); 
  136. $shipping_args['country'] = $this->limit_length( $order->get_shipping_country(), 2 ); 
  137. $shipping_args['zip'] = $this->limit_length( wc_format_postcode( $order->get_shipping_postcode(), $order->get_shipping_country() ), 32 ); 
  138. } else { 
  139. $shipping_args['no_shipping'] = 1; 
  140.  
  141. return $shipping_args; 
  142.  
  143. /** 
  144. * Get line item args for paypal request. 
  145. * @param WC_Order $order 
  146. * @return array 
  147. */ 
  148. protected function get_line_item_args( $order ) { 
  149.  
  150. /** 
  151. * Try passing a line item per product if supported. 
  152. */ 
  153. if ( ( ! wc_tax_enabled() || ! wc_prices_include_tax() ) && $this->prepare_line_items( $order ) ) { 
  154.  
  155. $line_item_args = array(); 
  156. $line_item_args['tax_cart'] = $this->number_format( $order->get_total_tax(), $order ); 
  157.  
  158. if ( $order->get_total_discount() > 0 ) { 
  159. $line_item_args['discount_amount_cart'] = $this->number_format( $this->round( $order->get_total_discount(), $order ), $order ); 
  160.  
  161. // Add shipping costs. Paypal ignores anything over 5 digits (999.99 is the max). 
  162. // We also check that shipping is not the **only** cost as PayPal won't allow payment 
  163. // if the items have no cost. 
  164. if ( $order->get_shipping_total() > 0 && $order->get_shipping_total() < 999.99 && $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) !== $this->number_format( $order->get_total(), $order ) ) { 
  165. $line_item_args['shipping_1'] = $this->number_format( $order->get_shipping_total(), $order ); 
  166. } elseif ( $order->get_shipping_total() > 0 ) { 
  167. $this->add_line_item( sprintf( __( 'Shipping via %s', 'woocommerce' ), $order->get_shipping_method() ), 1, $this->number_format( $order->get_shipping_total(), $order ) ); 
  168.  
  169. $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); 
  170.  
  171. /** 
  172. * Send order as a single item. 
  173. * For shipping, we longer use shipping_1 because paypal ignores it if *any* shipping rules are within paypal, and paypal ignores anything over 5 digits (999.99 is the max). 
  174. */ 
  175. } else { 
  176.  
  177. $this->delete_line_items(); 
  178.  
  179. $line_item_args = array(); 
  180. $all_items_name = $this->get_order_item_names( $order ); 
  181. $this->add_line_item( $all_items_name ? $all_items_name : __( 'Order', 'woocommerce' ), 1, $this->number_format( $order->get_total() - $this->round( $order->get_shipping_total() + $order->get_shipping_tax(), $order ), $order ), $order->get_order_number() ); 
  182.  
  183. // Add shipping costs. Paypal ignores anything over 5 digits (999.99 is the max). 
  184. // We also check that shipping is not the **only** cost as PayPal won't allow payment 
  185. // if the items have no cost. 
  186. if ( $order->get_shipping_total() > 0 && $order->get_shipping_total() < 999.99 && $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) !== $this->number_format( $order->get_total(), $order ) ) { 
  187. $line_item_args['shipping_1'] = $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ); 
  188. } elseif ( $order->get_shipping_total() > 0 ) { 
  189. $this->add_line_item( sprintf( __( 'Shipping via %s', 'woocommerce' ), $order->get_shipping_method() ), 1, $this->number_format( $order->get_shipping_total() + $order->get_shipping_tax(), $order ) ); 
  190.  
  191. $line_item_args = array_merge( $line_item_args, $this->get_line_items() ); 
  192.  
  193. return $line_item_args; 
  194.  
  195. /** 
  196. * Get order item names as a string. 
  197. * @param WC_Order $order 
  198. * @return string 
  199. */ 
  200. protected function get_order_item_names( $order ) { 
  201. $item_names = array(); 
  202.  
  203. foreach ( $order->get_items() as $item ) { 
  204. $item_name = $item->get_name(); 
  205. $item_meta = strip_tags( wc_display_item_meta( $item, array( 
  206. 'before' => "",  
  207. 'separator' => ", ",  
  208. 'after' => "",  
  209. 'echo' => false,  
  210. 'autop' => false,  
  211. ) ) ); 
  212.  
  213. if ( $item_meta ) { 
  214. $item_name .= ' (' . $item_meta . ')'; 
  215.  
  216. $item_names[] = $item_name . ' x ' . $item->get_quantity(); 
  217.  
  218. return apply_filters( 'woocommerce_paypal_get_order_item_names', implode( ', ', $item_names ), $order ); 
  219.  
  220. /** 
  221. * Get order item names as a string. 
  222. * @param WC_Order $order 
  223. * @param array $item 
  224. * @return string 
  225. */ 
  226. protected function get_order_item_name( $order, $item ) { 
  227. $item_name = $item->get_name(); 
  228. $item_meta = strip_tags( wc_display_item_meta( $item, array( 
  229. 'before' => "",  
  230. 'separator' => ", ",  
  231. 'after' => "",  
  232. 'echo' => false,  
  233. 'autop' => false,  
  234. ) ) ); 
  235.  
  236. if ( $item_meta ) { 
  237. $item_name .= ' (' . $item_meta . ')'; 
  238.  
  239. return apply_filters( 'woocommerce_paypal_get_order_item_name', $item_name, $order, $item ); 
  240.  
  241. /** 
  242. * Return all line items. 
  243. */ 
  244. protected function get_line_items() { 
  245. return $this->line_items; 
  246.  
  247. /** 
  248. * Remove all line items. 
  249. */ 
  250. protected function delete_line_items() { 
  251. $this->line_items = array(); 
  252.  
  253. /** 
  254. * Get line items to send to paypal. 
  255. * @param WC_Order $order 
  256. * @return bool 
  257. */ 
  258. protected function prepare_line_items( $order ) { 
  259. $this->delete_line_items(); 
  260. $calculated_total = 0; 
  261.  
  262. // Products 
  263. foreach ( $order->get_items( array( 'line_item', 'fee' ) ) as $item ) { 
  264. if ( 'fee' === $item['type'] ) { 
  265. $item_line_total = $this->number_format( $item['line_total'], $order ); 
  266. $line_item = $this->add_line_item( $item->get_name(), 1, $item_line_total ); 
  267. $calculated_total += $item_line_total; 
  268. } else { 
  269. $product = $item->get_product(); 
  270. $sku = $product ? $product->get_sku() : ''; 
  271. $item_line_total = $this->number_format( $order->get_item_subtotal( $item, false ), $order ); 
  272. $line_item = $this->add_line_item( $this->get_order_item_name( $order, $item ), $item->get_quantity(), $item_line_total, $sku ); 
  273. $calculated_total += $item_line_total * $item->get_quantity(); 
  274.  
  275. if ( ! $line_item ) { 
  276. return false; 
  277.  
  278. // Check for mismatched totals. 
  279. if ( $this->number_format( $calculated_total + $order->get_total_tax() + $this->round( $order->get_shipping_total(), $order ) - $this->round( $order->get_total_discount(), $order ), $order ) != $this->number_format( $order->get_total(), $order ) ) { 
  280. return false; 
  281.  
  282. return true; 
  283.  
  284. /** 
  285. * Add PayPal Line Item. 
  286. * @param string $item_name 
  287. * @param int $quantity 
  288. * @param float $amount 
  289. * @param string $item_number 
  290. * @return bool successfully added or not 
  291. */ 
  292. protected function add_line_item( $item_name, $quantity = 1, $amount = 0, $item_number = '' ) { 
  293. $index = ( sizeof( $this->line_items ) / 4 ) + 1; 
  294.  
  295. if ( $amount < 0 || $index > 9 ) { 
  296. return false; 
  297.  
  298. $item = apply_filters( 'woocommerce_paypal_line_item', array( 
  299. 'item_name' => html_entity_decode( wc_trim_string( $item_name ? $item_name : __( 'Item', 'woocommerce' ), 127 ), ENT_NOQUOTES, 'UTF-8' ),  
  300. 'quantity' => (int) $quantity,  
  301. 'amount' => (float) $amount,  
  302. 'item_number' => $item_number,  
  303. ), $item_name, $quantity, $amount, $item_number ); 
  304.  
  305. $this->line_items[ 'item_name_' . $index ] = $this->limit_length( $item['item_name'], 127 ); 
  306. $this->line_items[ 'quantity_' . $index ] = $item['quantity']; 
  307. $this->line_items[ 'amount_' . $index ] = $item['amount']; 
  308. $this->line_items[ 'item_number_' . $index ] = $this->limit_length( $item['item_number'], 127 ); 
  309.  
  310. return true; 
  311.  
  312. /** 
  313. * Get the state to send to paypal. 
  314. * @param string $cc 
  315. * @param string $state 
  316. * @return string 
  317. */ 
  318. protected function get_paypal_state( $cc, $state ) { 
  319. if ( 'US' === $cc ) { 
  320. return $state; 
  321.  
  322. $states = WC()->countries->get_states( $cc ); 
  323.  
  324. if ( isset( $states[ $state ] ) ) { 
  325. return $states[ $state ]; 
  326.  
  327. return $state; 
  328.  
  329. /** 
  330. * Check if currency has decimals. 
  331. * @param string $currency 
  332. * @return bool 
  333. */ 
  334. protected function currency_has_decimals( $currency ) { 
  335. if ( in_array( $currency, array( 'HUF', 'JPY', 'TWD' ) ) ) { 
  336. return false; 
  337.  
  338. return true; 
  339.  
  340. /** 
  341. * Round prices. 
  342. * @param double $price 
  343. * @param WC_Order $order 
  344. * @return double 
  345. */ 
  346. protected function round( $price, $order ) { 
  347. $precision = 2; 
  348.  
  349. if ( ! $this->currency_has_decimals( $order->get_currency() ) ) { 
  350. $precision = 0; 
  351.  
  352. return round( $price, $precision ); 
  353.  
  354. /** 
  355. * Format prices. 
  356. * @param float|int $price 
  357. * @param WC_Order $order 
  358. * @return string 
  359. */ 
  360. protected function number_format( $price, $order ) { 
  361. $decimals = 2; 
  362.  
  363. if ( ! $this->currency_has_decimals( $order->get_currency() ) ) { 
  364. $decimals = 0; 
  365.  
  366. return number_format( $price, $decimals, '.', '' );