/app/gateway/stripe/class-ms-gateway-stripe-api.php

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