class-wc-shortcode-checkout.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <?php
  2. /**
  3. * Checkout Shortcode
  4. *
  5. * Used on the checkout page, the checkout shortcode displays the checkout process.
  6. *
  7. * @package WooCommerce/Shortcodes/Checkout
  8. * @version 2.0.0
  9. */
  10. defined( 'ABSPATH' ) || exit;
  11. /**
  12. * Shortcode checkout class.
  13. */
  14. class WC_Shortcode_Checkout {
  15. /**
  16. * Get the shortcode content.
  17. *
  18. * @param array $atts Shortcode attributes.
  19. * @return string
  20. */
  21. public static function get( $atts ) {
  22. return WC_Shortcodes::shortcode_wrapper( array( __CLASS__, 'output' ), $atts );
  23. }
  24. /**
  25. * Output the shortcode.
  26. *
  27. * @param array $atts Shortcode attributes.
  28. */
  29. public static function output( $atts ) {
  30. global $wp;
  31. // Check cart class is loaded or abort.
  32. if ( is_null( WC()->cart ) ) {
  33. return;
  34. }
  35. // Backwards compatibility with old pay and thanks link arguments.
  36. if ( isset( $_GET['order'] ) && isset( $_GET['key'] ) ) { // WPCS: input var ok, CSRF ok.
  37. wc_deprecated_argument( __CLASS__ . '->' . __FUNCTION__, '2.1', '"order" is no longer used to pass an order ID. Use the order-pay or order-received endpoint instead.' );
  38. // Get the order to work out what we are showing.
  39. $order_id = absint( $_GET['order'] ); // WPCS: input var ok.
  40. $order = wc_get_order( $order_id );
  41. if ( $order && $order->has_status( 'pending' ) ) {
  42. $wp->query_vars['order-pay'] = absint( $_GET['order'] ); // WPCS: input var ok.
  43. } else {
  44. $wp->query_vars['order-received'] = absint( $_GET['order'] ); // WPCS: input var ok.
  45. }
  46. }
  47. // Handle checkout actions.
  48. if ( ! empty( $wp->query_vars['order-pay'] ) ) {
  49. self::order_pay( $wp->query_vars['order-pay'] );
  50. } elseif ( isset( $wp->query_vars['order-received'] ) ) {
  51. self::order_received( $wp->query_vars['order-received'] );
  52. } else {
  53. self::checkout();
  54. }
  55. }
  56. /**
  57. * Show the pay page.
  58. *
  59. * @throws Exception When validate fails.
  60. * @param int $order_id Order ID.
  61. */
  62. private static function order_pay( $order_id ) {
  63. do_action( 'before_woocommerce_pay' );
  64. wc_print_notices();
  65. $order_id = absint( $order_id );
  66. // Pay for existing order.
  67. if ( isset( $_GET['pay_for_order'], $_GET['key'] ) && $order_id ) { // WPCS: input var ok, CSRF ok.
  68. try {
  69. $order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok.
  70. $order = wc_get_order( $order_id );
  71. // Order or payment link is invalid.
  72. if ( ! $order || $order->get_id() !== $order_id || $order->get_order_key() !== $order_key ) {
  73. throw new Exception( __( 'Sorry, this order is invalid and cannot be paid for.', 'woocommerce' ) );
  74. }
  75. // Logged out customer does not have permission to pay for this order.
  76. if ( ! current_user_can( 'pay_for_order', $order_id ) && ! is_user_logged_in() ) {
  77. echo '<div class="woocommerce-info">' . esc_html__( 'Please log in to your account below to continue to the payment form.', 'woocommerce' ) . '</div>';
  78. woocommerce_login_form(
  79. array(
  80. 'redirect' => $order->get_checkout_payment_url(),
  81. )
  82. );
  83. return;
  84. }
  85. // Logged in customer trying to pay for someone else's order.
  86. if ( ! current_user_can( 'pay_for_order', $order_id ) ) {
  87. throw new Exception( __( 'This order cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ) );
  88. }
  89. // Does not need payment.
  90. if ( ! $order->needs_payment() ) {
  91. /* translators: %s: order status */
  92. throw new Exception( sprintf( __( 'This order&rsquo;s status is &ldquo;%s&rdquo;&mdash;it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ) );
  93. }
  94. // Ensure order items are still stocked.
  95. foreach ( $order->get_items() as $item_key => $item ) {
  96. if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
  97. $product = $item->get_product();
  98. if ( $product && ! apply_filters( 'woocommerce_pay_order_product_in_stock', $product->is_in_stock(), $product, $order ) ) {
  99. /* translators: %s: product name */
  100. throw new Exception( sprintf( __( 'Sorry, "%s" is no longer in stock so this order cannot be paid for. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
  101. }
  102. }
  103. }
  104. WC()->customer->set_props(
  105. array(
  106. 'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
  107. 'billing_state' => $order->get_billing_state() ? $order->get_billing_state() : null,
  108. 'billing_postcode' => $order->get_billing_postcode() ? $order->get_billing_postcode() : null,
  109. )
  110. );
  111. WC()->customer->save();
  112. $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
  113. if ( count( $available_gateways ) ) {
  114. current( $available_gateways )->set_current();
  115. }
  116. wc_get_template(
  117. 'checkout/form-pay.php', array(
  118. 'order' => $order,
  119. 'available_gateways' => $available_gateways,
  120. 'order_button_text' => apply_filters( 'woocommerce_pay_order_button_text', __( 'Pay for order', 'woocommerce' ) ),
  121. )
  122. );
  123. } catch ( Exception $e ) {
  124. wc_add_notice( $e->getMessage(), 'error' );
  125. }
  126. } elseif ( $order_id ) {
  127. // Pay for order after checkout step.
  128. $order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : ''; // WPCS: input var ok, CSRF ok.
  129. $order = wc_get_order( $order_id );
  130. if ( $order && $order->get_id() === $order_id && $order->get_order_key() === $order_key ) {
  131. if ( $order->needs_payment() ) {
  132. wc_get_template( 'checkout/order-receipt.php', array( 'order' => $order ) );
  133. } else {
  134. /* translators: %s: order status */
  135. wc_add_notice( sprintf( __( 'This order&rsquo;s status is &ldquo;%s&rdquo;&mdash;it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ), 'error' );
  136. }
  137. } else {
  138. wc_add_notice( __( 'Sorry, this order is invalid and cannot be paid for.', 'woocommerce' ), 'error' );
  139. }
  140. } else {
  141. wc_add_notice( __( 'Invalid order.', 'woocommerce' ), 'error' );
  142. }
  143. wc_print_notices();
  144. do_action( 'after_woocommerce_pay' );
  145. }
  146. /**
  147. * Show the thanks page.
  148. *
  149. * @param int $order_id Order ID.
  150. */
  151. private static function order_received( $order_id = 0 ) {
  152. wc_print_notices();
  153. $order = false;
  154. // Get the order.
  155. $order_id = apply_filters( 'woocommerce_thankyou_order_id', absint( $order_id ) );
  156. $order_key = apply_filters( 'woocommerce_thankyou_order_key', empty( $_GET['key'] ) ? '' : wc_clean( wp_unslash( $_GET['key'] ) ) ); // WPCS: input var ok, CSRF ok.
  157. if ( $order_id > 0 ) {
  158. $order = wc_get_order( $order_id );
  159. if ( ! $order || $order->get_order_key() !== $order_key ) {
  160. $order = false;
  161. }
  162. }
  163. // Empty awaiting payment session.
  164. unset( WC()->session->order_awaiting_payment );
  165. // Empty current cart.
  166. wc_empty_cart();
  167. wc_get_template( 'checkout/thankyou.php', array( 'order' => $order ) );
  168. }
  169. /**
  170. * Show the checkout.
  171. */
  172. private static function checkout() {
  173. // Show non-cart errors.
  174. wc_print_notices();
  175. // Check cart has contents.
  176. if ( WC()->cart->is_empty() && ! is_customize_preview() ) {
  177. return;
  178. }
  179. // Check cart contents for errors.
  180. do_action( 'woocommerce_check_cart_items' );
  181. // Calc totals.
  182. WC()->cart->calculate_totals();
  183. // Get checkout object.
  184. $checkout = WC()->checkout();
  185. if ( empty( $_POST ) && wc_notice_count( 'error' ) > 0 ) { // WPCS: input var ok, CSRF ok.
  186. wc_get_template( 'checkout/cart-errors.php', array( 'checkout' => $checkout ) );
  187. } else {
  188. $non_js_checkout = ! empty( $_POST['woocommerce_checkout_update_totals'] ); // WPCS: input var ok, CSRF ok.
  189. if ( wc_notice_count( 'error' ) === 0 && $non_js_checkout ) {
  190. wc_add_notice( __( 'The order totals have been updated. Please confirm your order by pressing the "Place order" button at the bottom of the page.', 'woocommerce' ) );
  191. }
  192. wc_get_template( 'checkout/form-checkout.php', array( 'checkout' => $checkout ) );
  193. }
  194. }
  195. }