PageRenderTime 52ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/woocommerce-gateway-stripe/includes/class-wc-gateway-stripe-addons.php

https://gitlab.com/hunt9310/ras
PHP | 394 lines | 218 code | 54 blank | 122 comment | 47 complexity | d8a3d9facb000d421f88d3d53274240f MD5 | raw file
  1. <?php
  2. if ( ! defined( 'ABSPATH' ) ) {
  3. exit;
  4. }
  5. /**
  6. * WC_Gateway_Stripe_Addons class.
  7. *
  8. * @extends WC_Gateway_Stripe
  9. */
  10. class WC_Gateway_Stripe_Addons extends WC_Gateway_Stripe {
  11. /**
  12. * Constructor
  13. */
  14. public function __construct() {
  15. parent::__construct();
  16. if ( class_exists( 'WC_Subscriptions_Order' ) ) {
  17. add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
  18. add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 );
  19. add_action( 'wcs_renewal_order_created', array( $this, 'delete_renewal_meta' ), 10 );
  20. add_action( 'woocommerce_subscription_failing_payment_method_updated_stripe', array( $this, 'update_failing_payment_method' ), 10, 2 );
  21. // display the credit card used for a subscription in the "My Subscriptions" table
  22. add_filter( 'woocommerce_my_subscriptions_payment_method', array( $this, 'maybe_render_subscription_payment_method' ), 10, 2 );
  23. // allow store managers to manually set Stripe as the payment method on a subscription
  24. add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
  25. add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
  26. }
  27. if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
  28. add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this, 'process_pre_order_release_payment' ) );
  29. }
  30. }
  31. /**
  32. * Is $order_id a subscription?
  33. * @param int $order_id
  34. * @return boolean
  35. */
  36. protected function is_subscription( $order_id ) {
  37. return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
  38. }
  39. /**
  40. * Is $order_id a pre-order?
  41. * @param int $order_id
  42. * @return boolean
  43. */
  44. protected function is_pre_order( $order_id ) {
  45. return ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order_id ) );
  46. }
  47. /**
  48. * Process the payment based on type.
  49. * @param int $order_id
  50. * @return array
  51. */
  52. public function process_payment( $order_id, $retry = true, $force_customer = false ) {
  53. if ( $this->is_subscription( $order_id ) ) {
  54. // Regular payment with force customer enabled
  55. return parent::process_payment( $order_id, true, true );
  56. } elseif ( $this->is_pre_order( $order_id ) ) {
  57. return $this->process_pre_order( $order_id, $retry, $force_customer );
  58. } else {
  59. return parent::process_payment( $order_id, $retry, $force_customer );
  60. }
  61. }
  62. /**
  63. * Updates other subscription sources.
  64. */
  65. protected function save_source( $order, $source ) {
  66. parent::save_source( $order, $source );
  67. // Also store it on the subscriptions being purchased or paid for in the order
  68. if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order->id ) ) {
  69. $subscriptions = wcs_get_subscriptions_for_order( $order->id );
  70. } elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order->id ) ) {
  71. $subscriptions = wcs_get_subscriptions_for_renewal_order( $order->id );
  72. } else {
  73. $subscriptions = array();
  74. }
  75. foreach( $subscriptions as $subscription ) {
  76. update_post_meta( $subscription->id, '_stripe_customer_id', $source->customer );
  77. update_post_meta( $subscription->id, '_stripe_card_id', $source->source );
  78. }
  79. }
  80. /**
  81. * process_subscription_payment function.
  82. * @param mixed $order
  83. * @param int $amount (default: 0)
  84. * @param string $stripe_token (default: '')
  85. * @param bool initial_payment
  86. */
  87. public function process_subscription_payment( $order = '', $amount = 0 ) {
  88. if ( $amount * 100 < 50 ) {
  89. return new WP_Error( 'stripe_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
  90. }
  91. // Get source from order
  92. $source = $this->get_order_source( $order );
  93. // If no order source was defined, use user source instead.
  94. if ( ! $source->customer ) {
  95. $source = $this->get_source( $order->customer_user );
  96. }
  97. // Or fail :(
  98. if ( ! $source->customer ) {
  99. return new WP_Error( 'stripe_error', __( 'Customer not found', 'woocommerce-gateway-stripe' ) );
  100. }
  101. WC_Stripe::log( "Info: Begin processing subscription payment for order {$order->id} for the amount of {$amount}" );
  102. // Make the request
  103. $request = $this->generate_payment_request( $order, $source );
  104. $request['capture'] = 'true';
  105. $request['amount'] = $this->get_stripe_amount( $amount, $request['currency'] );
  106. $request['metadata'] = array(
  107. 'payment_type' => 'recurring'
  108. );
  109. $response = WC_Stripe_API::request( $request );
  110. // Process valid response
  111. if ( ! is_wp_error( $response ) ) {
  112. $this->process_response( $response, $order );
  113. }
  114. return $response;
  115. }
  116. /**
  117. * Process the pre-order
  118. * @param int $order_id
  119. * @return array
  120. */
  121. public function process_pre_order( $order_id, $retry, $force_customer ) {
  122. if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) {
  123. try {
  124. $order = wc_get_order( $order_id );
  125. if ( $order->get_total() * 100 < 50 ) {
  126. throw new Exception( __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce-gateway-stripe' ) );
  127. }
  128. $source = $this->get_source( get_current_user_id(), true );
  129. // We need a source on file to continue.
  130. if ( empty( $source->customer ) || empty( $source->source ) ) {
  131. throw new Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
  132. }
  133. // Store source to order meta
  134. $this->save_source( $order, $source );
  135. // Reduce stock levels
  136. $order->reduce_order_stock();
  137. // Remove cart
  138. WC()->cart->empty_cart();
  139. // Is pre ordered!
  140. WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
  141. // Return thank you page redirect
  142. return array(
  143. 'result' => 'success',
  144. 'redirect' => $this->get_return_url( $order )
  145. );
  146. } catch ( Exception $e ) {
  147. wc_add_notice( $e->getMessage(), 'error' );
  148. return;
  149. }
  150. } else {
  151. return parent::process_payment( $order_id, $retry, $force_customer );
  152. }
  153. }
  154. /**
  155. * Process a pre-order payment when the pre-order is released
  156. * @param WC_Order $order
  157. * @return void
  158. */
  159. public function process_pre_order_release_payment( $order ) {
  160. try {
  161. // Define some callbacks if the first attempt fails.
  162. $retry_callbacks = array(
  163. 'remove_order_source_before_retry',
  164. 'remove_order_customer_before_retry',
  165. );
  166. while ( 1 ) {
  167. $source = $this->get_order_source( $order );
  168. $response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
  169. if ( is_wp_error( $response ) ) {
  170. if ( 0 === sizeof( $retry_callbacks ) ) {
  171. throw new Exception( $response->get_error_message() );
  172. } else {
  173. $retry_callback = array_shift( $retry_callbacks );
  174. call_user_func( array( $this, $retry_callback ), $order );
  175. }
  176. } else {
  177. // Successful
  178. $this->process_response( $response, $order );
  179. break;
  180. }
  181. }
  182. } catch ( Exception $e ) {
  183. $order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $e->getMessage() );
  184. // Mark order as failed if not already set,
  185. // otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
  186. if ( ! $order->has_status( 'failed' ) ) {
  187. $order->update_status( 'failed', $order_note );
  188. } else {
  189. $order->add_order_note( $order_note );
  190. }
  191. }
  192. }
  193. /**
  194. * Don't transfer Stripe customer/token meta to resubscribe orders.
  195. * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
  196. */
  197. public function delete_resubscribe_meta( $resubscribe_order ) {
  198. delete_post_meta( $resubscribe_order->id, '_stripe_customer_id' );
  199. delete_post_meta( $resubscribe_order->id, '_stripe_card_id' );
  200. $this->delete_renewal_meta( $resubscribe_order );
  201. }
  202. /**
  203. * Don't transfer Stripe fee/ID meta to renewal orders.
  204. * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
  205. */
  206. public function delete_renewal_meta( $renewal_order ) {
  207. delete_post_meta( $renewal_order->id, 'Stripe Fee' );
  208. delete_post_meta( $renewal_order->id, 'Net Revenue From Stripe' );
  209. delete_post_meta( $renewal_order->id, 'Stripe Payment ID' );
  210. return $renewal_order;
  211. }
  212. /**
  213. * scheduled_subscription_payment function.
  214. *
  215. * @param $amount_to_charge float The amount to charge.
  216. * @param $renewal_order WC_Order A WC_Order object created to record the renewal payment.
  217. */
  218. public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
  219. $response = $this->process_subscription_payment( $renewal_order, $amount_to_charge );
  220. if ( is_wp_error( $response ) ) {
  221. $renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
  222. }
  223. }
  224. /**
  225. * Remove order meta
  226. * @param object $order
  227. */
  228. public function remove_order_source_before_retry( $order ) {
  229. delete_post_meta( $order->id, '_stripe_card_id' );
  230. }
  231. /**
  232. * Remove order meta
  233. * @param object $order
  234. */
  235. public function remove_order_customer_before_retry( $order ) {
  236. delete_post_meta( $order->id, '_stripe_customer_id' );
  237. }
  238. /**
  239. * Update the customer_id for a subscription after using Stripe to complete a payment to make up for
  240. * an automatic renewal payment which previously failed.
  241. *
  242. * @access public
  243. * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
  244. * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
  245. * @return void
  246. */
  247. public function update_failing_payment_method( $subscription, $renewal_order ) {
  248. update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id );
  249. update_post_meta( $subscription->id, '_stripe_card_id', $renewal_order->stripe_card_id );
  250. }
  251. /**
  252. * Include the payment meta data required to process automatic recurring payments so that store managers can
  253. * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
  254. *
  255. * @since 2.5
  256. * @param array $payment_meta associative array of meta data required for automatic payments
  257. * @param WC_Subscription $subscription An instance of a subscription object
  258. * @return array
  259. */
  260. public function add_subscription_payment_meta( $payment_meta, $subscription ) {
  261. $payment_meta[ $this->id ] = array(
  262. 'post_meta' => array(
  263. '_stripe_customer_id' => array(
  264. 'value' => get_post_meta( $subscription->id, '_stripe_customer_id', true ),
  265. 'label' => 'Stripe Customer ID',
  266. ),
  267. '_stripe_card_id' => array(
  268. 'value' => get_post_meta( $subscription->id, '_stripe_card_id', true ),
  269. 'label' => 'Stripe Card ID',
  270. ),
  271. ),
  272. );
  273. return $payment_meta;
  274. }
  275. /**
  276. * Validate the payment meta data required to process automatic recurring payments so that store managers can
  277. * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
  278. *
  279. * @since 2.5
  280. * @param string $payment_method_id The ID of the payment method to validate
  281. * @param array $payment_meta associative array of meta data required for automatic payments
  282. * @return array
  283. */
  284. public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) {
  285. if ( $this->id === $payment_method_id ) {
  286. if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) {
  287. throw new Exception( 'A "_stripe_customer_id" value is required.' );
  288. } elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) {
  289. throw new Exception( 'Invalid customer ID. A valid "_stripe_customer_id" must begin with "cus_".' );
  290. }
  291. if ( ! empty( $payment_meta['post_meta']['_stripe_card_id']['value'] ) && 0 !== strpos( $payment_meta['post_meta']['_stripe_card_id']['value'], 'card_' ) ) {
  292. throw new Exception( 'Invalid card ID. A valid "_stripe_card_id" must begin with "card_".' );
  293. }
  294. }
  295. }
  296. /**
  297. * Render the payment method used for a subscription in the "My Subscriptions" table
  298. *
  299. * @since 1.7.5
  300. * @param string $payment_method_to_display the default payment method text to display
  301. * @param WC_Subscription $subscription the subscription details
  302. * @return string the subscription payment method
  303. */
  304. public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
  305. // bail for other payment methods
  306. if ( $this->id !== $subscription->payment_method || ! $subscription->customer_user ) {
  307. return $payment_method_to_display;
  308. }
  309. $stripe_customer = new WC_Stripe_Customer();
  310. $stripe_customer_id = get_post_meta( $subscription->id, '_stripe_customer_id', true );
  311. $stripe_card_id = get_post_meta( $subscription->id, '_stripe_card_id', true );
  312. // If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data.
  313. if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) {
  314. $user_id = $subscription->customer_user;
  315. $stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true );
  316. $stripe_card_id = get_user_meta( $user_id, '_stripe_card_id', true );
  317. }
  318. // If we couldn't find a Stripe customer linked to the account, fallback to the order meta data.
  319. if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) {
  320. $stripe_customer_id = get_post_meta( $subscription->order->id, '_stripe_customer_id', true );
  321. $stripe_card_id = get_post_meta( $subscription->order->id, '_stripe_card_id', true );
  322. }
  323. $stripe_customer->set_id( $stripe_customer_id );
  324. $cards = $stripe_customer->get_cards();
  325. if ( $cards ) {
  326. $found_card = false;
  327. foreach ( $cards as $card ) {
  328. if ( $card->id === $stripe_card_id ) {
  329. $found_card = true;
  330. $payment_method_to_display = sprintf( __( 'Via %s card ending in %s', 'woocommerce-gateway-stripe' ), ( isset( $card->type ) ? $card->type : $card->brand ), $card->last4 );
  331. break;
  332. }
  333. }
  334. if ( ! $found_card ) {
  335. $payment_method_to_display = sprintf( __( 'Via %s card ending in %s', 'woocommerce-gateway-stripe' ), ( isset( $cards[0]->type ) ? $cards[0]->type : $cards[0]->brand ), $cards[0]->last4 );
  336. }
  337. }
  338. return $payment_method_to_display;
  339. }
  340. }