PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/membership/app/gateway/stripe/class-ms-gateway-stripe-api.php

https://gitlab.com/najomie/fit-hippie
PHP | 444 lines | 244 code | 53 blank | 147 comment | 30 complexity | e3193613248a0627f6506e5d30638d4e MD5 | raw file
  1. <?php
  2. /**
  3. * Stripe Gateway API Integration.
  4. *
  5. * This object is shared between the Stripe Single and Stripe Subscription
  6. * gateways.
  7. *
  8. * @since 1.0.0
  9. * @package Membership2
  10. * @subpackage Model
  11. */
  12. class MS_Gateway_Stripe_Api extends MS_Model_Option {
  13. const ID = 'stripe';
  14. /**
  15. * Gateway singleton instance.
  16. *
  17. * @since 1.0.0
  18. * @var string $instance
  19. */
  20. public static $instance;
  21. /**
  22. * Holds a reference to the parent gateway (either stripe or stripeplan)
  23. *
  24. * @since 1.0.1.0
  25. * @var MS_Gateway_Stripe|MS_Gateway_Stripeplan
  26. */
  27. protected $_gateway = null;
  28. /**
  29. * Sets the parent gateway of the API object.
  30. *
  31. * The parent gateway object is used to fetch the API keys.
  32. *
  33. * @since 1.0.1.0
  34. * @param MS_Gateway $gateway The parent gateway.
  35. */
  36. public function set_gateway( $gateway ) {
  37. static $Stripe_Loaded = false;
  38. if ( ! $Stripe_Loaded ) {
  39. require_once MS_Plugin::instance()->dir . '/lib/stripe-php/lib/Stripe.php';
  40. do_action(
  41. 'ms_gateway_stripe_load_stripe_lib_after',
  42. $this
  43. );
  44. $Stripe_Loaded = true;
  45. }
  46. $this->_gateway = $gateway;
  47. $secret_key = $this->_gateway->get_secret_key();
  48. M2_Stripe::setApiKey( $secret_key );
  49. }
  50. /**
  51. * Get Member's Stripe Customer Object, creates a new customer if not found.
  52. *
  53. * @since 1.0.0
  54. * @internal
  55. *
  56. * @param MS_Model_Member $member The member.
  57. * @param string $token The credit card token.
  58. */
  59. public function get_stripe_customer( $member, $token ) {
  60. $customer = $this->find_customer( $member );
  61. if ( empty( $customer ) ) {
  62. $customer = M2_Stripe_Customer::create(
  63. array(
  64. 'card' => $token,
  65. 'email' => $member->email,
  66. )
  67. );
  68. $member->set_gateway_profile( self::ID, 'customer_id', $customer->id );
  69. $member->save();
  70. } else {
  71. $this->add_card( $member, $customer, $token );
  72. }
  73. return apply_filters(
  74. 'ms_gateway_stripe_get_stripe_customer',
  75. $customer,
  76. $member,
  77. $this
  78. );
  79. }
  80. /**
  81. * Get Member's Stripe Customer Object.
  82. *
  83. * @since 1.0.0
  84. * @internal
  85. *
  86. * @param MS_Model_Member $member The member.
  87. */
  88. public function find_customer( $member ) {
  89. $customer_id = $member->get_gateway_profile( self::ID, 'customer_id' );
  90. $customer = null;
  91. if ( ! empty( $customer_id ) ) {
  92. $customer = M2_Stripe_Customer::retrieve( $customer_id );
  93. // Seems like the customer was manually deleted on Stripe website.
  94. if ( isset( $customer->deleted ) && $customer->deleted ) {
  95. $customer = null;
  96. $member->set_gateway_profile( self::ID, 'customer_id', '' );
  97. }
  98. }
  99. return apply_filters(
  100. 'ms_gateway_stripe_find_customer',
  101. $customer,
  102. $member,
  103. $this
  104. );
  105. }
  106. /**
  107. * Add card info to Stripe customer profile and to WordPress user meta.
  108. *
  109. * @since 1.0.0
  110. * @api
  111. *
  112. * @param MS_Model_Member $member The member model.
  113. * @param M2_Stripe_Customer $customer The stripe customer object.
  114. * @param string $token The stripe card token generated by the gateway.
  115. */
  116. public function add_card( $member, $customer, $token ) {
  117. $card = false;
  118. // 1. Save card to Stripe profile.
  119. // Stripe API until version 2015-02-16
  120. if ( ! empty( $customer->cards ) ) {
  121. $card = $customer->cards->create( array( 'card' => $token ) );
  122. $customer->default_card = $card->id;
  123. }
  124. // Stripe API since 2015-02-18
  125. if ( ! empty( $customer->sources ) ) {
  126. $card = $customer->sources->create( array( 'card' => $token ) );
  127. $customer->default_source = $card->id;
  128. }
  129. if ( $card ) {
  130. $customer->save();
  131. }
  132. /**
  133. * This action is used by the Taxamo Add-on to check additional country
  134. * evidence (CC country).
  135. *
  136. * @since 1.0.0
  137. */
  138. do_action( 'ms_gateway_stripe_credit_card_saved', $card, $member, $this );
  139. // 2. Save card to WordPress user meta.
  140. if ( $card ) {
  141. $member->set_gateway_profile(
  142. self::ID,
  143. 'card_exp',
  144. gmdate( 'Y-m-d', strtotime( "{$card->exp_year}-{$card->exp_month}-01" ) )
  145. );
  146. $member->set_gateway_profile( self::ID, 'card_num', $card->last4 );
  147. $member->save();
  148. }
  149. do_action(
  150. 'ms_gateway_stripe_add_card_info_after',
  151. $customer,
  152. $token,
  153. $this
  154. );
  155. }
  156. /**
  157. * Creates a one-time charge that is immediately captured.
  158. *
  159. * This means the money is instantly transferred to our own stripe account.
  160. *
  161. * @since 1.0.0
  162. * @internal
  163. *
  164. * @param M2_Stripe_Customer $customer Stripe customer to charge.
  165. * @param float $amount Amount in currency (i.e. in USD, not in cents)
  166. * @param string $currency 3-digit currency code.
  167. * @param string $description This is displayed on the invoice to customer.
  168. * @return M2_Stripe_Charge The resulting charge object.
  169. */
  170. public function charge( $customer, $amount, $currency, $description ) {
  171. $amount = apply_filters(
  172. 'ms_gateway_stripe_charge_amount',
  173. $amount,
  174. $currency
  175. );
  176. $charge = M2_Stripe_Charge::create(
  177. array(
  178. 'customer' => $customer->id,
  179. 'amount' => intval( $amount * 100 ), // Amount in cents!
  180. 'currency' => strtolower( $currency ),
  181. 'description' => $description,
  182. )
  183. );
  184. return apply_filters(
  185. 'ms_gateway_stripe_charge',
  186. $charge,
  187. $customer,
  188. $amount,
  189. $currency,
  190. $description,
  191. $this
  192. );
  193. }
  194. /**
  195. * Fetches an existing subscription from Stripe and returns it.
  196. *
  197. * If the specified customer did not subscribe to the membership then
  198. * boolean FALSE will be returned.
  199. *
  200. * @since 1.0.0
  201. * @internal
  202. *
  203. * @param M2_Stripe_Customer $customer Stripe customer to charge.
  204. * @param MS_Model_Membership $membership The membership.
  205. * @return M2_Stripe_Subscription|false The resulting charge object.
  206. */
  207. public function get_subscription( $customer, $membership ) {
  208. $plan_id = MS_Gateway_Stripeplan::get_the_id(
  209. $membership->id,
  210. 'plan'
  211. );
  212. /*
  213. * Check all subscriptions of the customer and find the subscription
  214. * for the specified membership.
  215. */
  216. $last_checked = false;
  217. $has_more = false;
  218. $subscription = false;
  219. do {
  220. $args = array();
  221. if ( $last_checked ) {
  222. $args['starting_after'] = $last_checked;
  223. }
  224. $active_subs = $customer->subscriptions->all( $args );
  225. $has_more = $active_subs->has_more;
  226. foreach ( $active_subs->data as $sub ) {
  227. if ( $sub->plan->id == $plan_id ) {
  228. $subscription = $sub;
  229. $has_more = false;
  230. break 2;
  231. }
  232. $last_checked = $sub->id;
  233. }
  234. } while ( $has_more );
  235. return apply_filters(
  236. 'ms_gateway_stripe_get_subscription',
  237. $subscription,
  238. $customer,
  239. $membership,
  240. $this
  241. );
  242. }
  243. /**
  244. * Creates a subscription that starts immediately.
  245. *
  246. * @since 1.0.0
  247. * @internal
  248. *
  249. * @param M2_Stripe_Customer $customer Stripe customer to charge.
  250. * @param MS_Model_Invoice $invoice The relevant invoice.
  251. * @return M2_Stripe_Subscription The resulting charge object.
  252. */
  253. public function subscribe( $customer, $invoice ) {
  254. $membership = $invoice->get_membership();
  255. $plan_id = MS_Gateway_Stripeplan::get_the_id(
  256. $membership->id,
  257. 'plan'
  258. );
  259. $subscription = self::get_subscription( $customer, $membership );
  260. /*
  261. * If no active subscription was found for the membership create it.
  262. */
  263. if ( ! $subscription ) {
  264. $tax_percent = null;
  265. $coupon_id = null;
  266. if ( is_numeric( $invoice->tax_rate ) && $invoice->tax_rate > 0 ) {
  267. $tax_percent = floatval( $invoice->tax_rate );
  268. }
  269. if ( $invoice->coupon_id ) {
  270. $coupon_id = MS_Gateway_Stripeplan::get_the_id(
  271. $invoice->coupon_id,
  272. 'coupon'
  273. );
  274. }
  275. $args = array(
  276. 'plan' => $plan_id,
  277. 'tax_percent' => $tax_percent,
  278. 'coupon' => $coupon_id,
  279. );
  280. $subscription = $customer->subscriptions->create( $args );
  281. }
  282. return apply_filters(
  283. 'ms_gateway_stripe_subscribe',
  284. $subscription,
  285. $customer,
  286. $invoice,
  287. $membership,
  288. $this
  289. );
  290. }
  291. /**
  292. * Creates or updates the payment plan specified by the function parameter.
  293. *
  294. * @since 1.0.0
  295. * @internal
  296. *
  297. * @param array $plan_data The plan-object containing all details for Stripe.
  298. */
  299. public function create_or_update_plan( $plan_data ) {
  300. $item_id = $plan_data['id'];
  301. $all_items = MS_Factory::get_transient( 'ms_stripeplan_plans' );
  302. $all_items = lib3()->array->get( $all_items );
  303. if ( ! isset( $all_items[$item_id] )
  304. || ! is_a( $all_items[$item_id], 'M2_Stripe_Plan' )
  305. ) {
  306. try {
  307. $item = M2_Stripe_Plan::retrieve( $item_id );
  308. } catch( Exception $e ) {
  309. // If the plan does not exist then stripe will throw an Exception.
  310. $item = false;
  311. }
  312. $all_items[$item_id] = $item;
  313. } else {
  314. $item = $all_items[$item_id];
  315. }
  316. /*
  317. * Stripe can only update the plan-name, so we have to delete and
  318. * recreate the plan manually.
  319. */
  320. if ( $item && is_a( $item, 'M2_Stripe_Plan' ) ) {
  321. $item->delete();
  322. $all_items[$item_id] = false;
  323. }
  324. if ( $plan_data['amount'] > 0 ) {
  325. $item = M2_Stripe_Plan::create( $plan_data );
  326. $all_items[$item_id] = $item;
  327. }
  328. MS_Factory::set_transient(
  329. 'ms_stripeplan_plans',
  330. $all_items,
  331. HOUR_IN_SECONDS
  332. );
  333. }
  334. /**
  335. * Creates or updates the coupon specified by the function parameter.
  336. *
  337. * @since 1.0.0
  338. * @internal
  339. *
  340. * @param array $coupon_data The object containing all details for Stripe.
  341. */
  342. public function create_or_update_coupon( $coupon_data ) {
  343. $item_id = $coupon_data['id'];
  344. $all_items = MS_Factory::get_transient( 'ms_stripeplan_plans' );
  345. $all_items = lib3()->array->get( $all_items );
  346. if ( ! isset( $all_items[$item_id] )
  347. || ! is_a( $all_items[$item_id], 'M2_Stripe_Coupon' )
  348. ) {
  349. try {
  350. $item = M2_Stripe_Coupon::retrieve( $item_id );
  351. } catch( Exception $e ) {
  352. // If the coupon does not exist then stripe will throw an Exception.
  353. $item = false;
  354. }
  355. $all_items[$item_id] = $item;
  356. } else {
  357. $item = $all_items[$item_id];
  358. }
  359. /*
  360. * Stripe can only update the coupon-name, so we have to delete and
  361. * recreate the coupon manually.
  362. */
  363. if ( $item && is_a( $item, 'M2_Stripe_Coupon' ) ) {
  364. $item->delete();
  365. $all_items[$item_id] = false;
  366. }
  367. $item = M2_Stripe_Coupon::create( $coupon_data );
  368. $all_items[$item_id] = $item;
  369. MS_Factory::set_transient(
  370. 'ms_stripeplan_coupons',
  371. $all_items,
  372. HOUR_IN_SECONDS
  373. );
  374. }
  375. /**
  376. * Little hack to force the plugin to store/load the stripe_api data in same
  377. * option-field as the stripe-gateway settings.
  378. *
  379. * @since 1.0.0
  380. * @return string
  381. */
  382. public function option_key() {
  383. // Option key should be all lowercase.
  384. $key = 'ms_gateway_stripe';
  385. // Network-wide mode uses different options then single-site mode.
  386. if ( MS_Plugin::is_network_wide() ) {
  387. $key .= '-network';
  388. }
  389. return $key;
  390. }
  391. }