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

  1. <?php 
  2. /** 
  3. * Stripe Gateway Integration. 
  4. * 
  5. * Persisted by parent class MS_Model_Option. Singleton. 
  6. * 
  7. * @since 1.0.0 
  8. * @package Membership2 
  9. * @subpackage Model 
  10. */ 
  11. class MS_Gateway_Stripe extends MS_Gateway { 
  12.  
  13. const ID = 'stripe'; 
  14.  
  15. /** 
  16. * Gateway singleton instance. 
  17. * 
  18. * @since 1.0.0 
  19. * @var string $instance 
  20. */ 
  21. public static $instance; 
  22.  
  23. /** 
  24. * Stripe test secret key (sandbox). 
  25. * 
  26. * @see https://support.stripe.com/questions/where-do-i-find-my-api-keys 
  27. * 
  28. * @since 1.0.0 
  29. * @var string $test_secret_key 
  30. */ 
  31. protected $test_secret_key = ''; 
  32.  
  33. /** 
  34. * Stripe Secret key (live). 
  35. * 
  36. * @since 1.0.0 
  37. * @var string $secret_key 
  38. */ 
  39. protected $secret_key = ''; 
  40.  
  41. /** 
  42. * Stripe test publishable key (sandbox). 
  43. * 
  44. * @since 1.0.0 
  45. * @var string $test_publishable_key 
  46. */ 
  47. protected $test_publishable_key = ''; 
  48.  
  49. /** 
  50. * Stripe publishable key (live). 
  51. * 
  52. * @since 1.0.0 
  53. * @var string $publishable_key 
  54. */ 
  55. protected $publishable_key = ''; 
  56.  
  57. /** 
  58. * Instance of the shared stripe API integration 
  59. * 
  60. * @since 1.0.0 
  61. * @var MS_Gateway_Stripe_Api $api 
  62. */ 
  63. protected $_api = null; 
  64.  
  65. /** 
  66. * Initialize the object. 
  67. * 
  68. * @since 1.0.0 
  69. * @internal 
  70. */ 
  71. public function after_load() { 
  72. parent::after_load(); 
  73. $this->_api = MS_Factory::load( 'MS_Gateway_Stripe_Api' ); 
  74.  
  75. $this->id = self::ID; 
  76. $this->name = __( 'Stripe Single Gateway', 'membership2' ); 
  77. $this->group = 'Stripe'; 
  78. $this->manual_payment = true; // Recurring billed/paid manually 
  79. $this->pro_rate = true; 
  80.  
  81. $this->add_filter( 
  82. 'ms_model_pages_get_ms_page_url',  
  83. 'ms_model_pages_get_ms_page_url_cb',  
  84. 99, 4 
  85. ); 
  86.  
  87. /** 
  88. * Force SSL when Stripe in Live mode 
  89. * 
  90. * @since 1.0.2.5 
  91. * 
  92. * @param String $url The modified or raw URL 
  93. * @param String $page_type Check if this is a membership page 
  94. * @param Bool $ssl If SSL enabled or not 
  95. * @param Int $site_id The ID of site 
  96. * 
  97. * @return String $url Modified or raw URL 
  98. */ 
  99. public function ms_model_pages_get_ms_page_url_cb( $url, $page_type, $ssl, $site_id ) { 
  100. /** 
  101. * Constant M2_FORCE_NO_SSL 
  102. * 
  103. * It's needed, if : 
  104. * - the user has no SSL 
  105. * - the user has SSL but doesn't want to force 
  106. * - The user has multiple gateways like Paypal and Stripe and doesn't want to force 
  107. * 
  108. * If the user has SSL certificate, this rule won't work 
  109. */ 
  110. if( ! defined( 'M2_FORCE_NO_SSL' ) ) { 
  111. if ( $this->active && $this->is_live_mode() ) { 
  112. if( $page_type == MS_Model_Pages::MS_PAGE_MEMBERSHIPS || $page_type == MS_Model_Pages::MS_PAGE_REGISTER ) { 
  113. $url = MS_Helper_Utility::get_ssl_url( $url ); 
  114.  
  115. return $url; 
  116.  
  117. /** 
  118. * Processes purchase action. 
  119. * 
  120. * @since 1.0.0 
  121. * @api 
  122. * 
  123. * @param MS_Model_Relationship $subscription The related membership relationship. 
  124. */ 
  125. public function process_purchase( $subscription ) { 
  126. $success = false; 
  127. $note = ''; 
  128. $token = ''; 
  129. $external_id = ''; 
  130. $error = false; 
  131.  
  132. do_action( 
  133. 'ms_gateway_stripe_process_purchase_before',  
  134. $subscription,  
  135. $this 
  136. ); 
  137. $this->_api->set_gateway( $this ); 
  138.  
  139. $member = $subscription->get_member(); 
  140. $invoice = $subscription->get_current_invoice(); 
  141.  
  142. if ( ! empty( $_POST['stripeToken'] ) ) { 
  143. lib3()->array->strip_slashes( $_POST, 'stripeToken' ); 
  144.  
  145. $token = $_POST['stripeToken']; 
  146. $external_id = $token; 
  147. try { 
  148. $customer = $this->_api->get_stripe_customer( $member, $token ); 
  149.  
  150. if ( 0 == $invoice->total ) { 
  151. // Free, just process. 
  152. $invoice->changed(); 
  153. $success = true; 
  154. $note = __( 'No payment for free membership', 'membership2' ); 
  155. } else { 
  156. // Send request to gateway. 
  157. $charge = $this->_api->charge( 
  158. $customer,  
  159. $invoice->total,  
  160. $invoice->currency,  
  161. $invoice->name 
  162. ); 
  163.  
  164. if ( true == $charge->paid ) { 
  165. $invoice->pay_it( self::ID, $charge->id ); 
  166. $note = __( 'Payment successful', 'membership2' ); 
  167. $note .= ' - Token: ' . $token; 
  168. $success = true; 
  169. } else { 
  170. $note = __( 'Stripe payment failed', 'membership2' ); 
  171. } catch ( Exception $e ) { 
  172. $note = 'Stripe error: '. $e->getMessage(); 
  173. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_FAILED, $subscription ); 
  174. MS_Helper_Debug::log( $note ); 
  175. $error = $e; 
  176. } else { 
  177. $note = 'Stripe gateway token not found.'; 
  178. MS_Helper_Debug::log( $note ); 
  179.  
  180. do_action( 
  181. 'ms_gateway_transaction_log',  
  182. self::ID, // gateway ID 
  183. 'process', // request|process|handle 
  184. $success, // success flag 
  185. $subscription->id, // subscription ID 
  186. $invoice->id, // invoice ID 
  187. $invoice->total, // charged amount 
  188. $note, // Descriptive text 
  189. $external_id // External ID 
  190. ); 
  191.  
  192. if ( $error ) { 
  193. throw $e; 
  194.  
  195. return apply_filters( 
  196. 'ms_gateway_stripe_process_purchase',  
  197. $invoice,  
  198. $this 
  199. ); 
  200.  
  201. /** 
  202. * Request automatic payment to the gateway. 
  203. * 
  204. * @since 1.0.0 
  205. * @api 
  206. * 
  207. * @param MS_Model_Relationship $subscription The related membership relationship. 
  208. * @return bool True on success. 
  209. */ 
  210. public function request_payment( $subscription ) { 
  211. $was_paid = false; 
  212. $note = ''; 
  213. $external_id = ''; 
  214.  
  215. do_action( 
  216. 'ms_gateway_stripe_request_payment_before',  
  217. $subscription,  
  218. $this 
  219. ); 
  220. $this->_api->set_gateway( $this ); 
  221.  
  222. $member = $subscription->get_member(); 
  223. $invoice = $subscription->get_current_invoice(); 
  224.  
  225. if ( ! $invoice->is_paid() ) { 
  226. try { 
  227. $customer = $this->_api->find_customer( $member ); 
  228.  
  229. if ( ! empty( $customer ) ) { 
  230. if ( 0 == $invoice->total ) { 
  231. $invoice->changed(); 
  232. $success = true; 
  233. $note = __( 'No payment for free membership', 'membership2' ); 
  234. } else { 
  235. $charge = $this->_api->charge( 
  236. $customer,  
  237. $invoice->total,  
  238. $invoice->currency,  
  239. $invoice->name 
  240. ); 
  241. $external_id = $charge->id; 
  242.  
  243. if ( true == $charge->paid ) { 
  244. $was_paid = true; 
  245. $invoice->pay_it( self::ID, $external_id ); 
  246. $note = __( 'Payment successful', 'membership2' ); 
  247. } else { 
  248. $note = __( 'Stripe payment failed', 'membership2' ); 
  249. } else { 
  250. $note = "Stripe customer is empty for user $member->username"; 
  251. MS_Helper_Debug::log( $note ); 
  252. } catch ( Exception $e ) { 
  253. $note = 'Stripe error: '. $e->getMessage(); 
  254. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_FAILED, $subscription ); 
  255. MS_Helper_Debug::log( $note ); 
  256. } else { 
  257. // Invoice was already paid earlier. 
  258. $was_paid = true; 
  259. $note = __( 'Invoice already paid', 'membership2' ); 
  260.  
  261. do_action( 
  262. 'ms_gateway_transaction_log',  
  263. self::ID, // gateway ID 
  264. 'request', // request|process|handle 
  265. $was_paid, // success flag 
  266. $subscription->id, // subscription ID 
  267. $invoice->id, // invoice ID 
  268. $invoice->total, // charged amount 
  269. $note, // Descriptive text 
  270. $external_id // External ID 
  271. ); 
  272.  
  273. do_action( 
  274. 'ms_gateway_stripe_request_payment_after',  
  275. $subscription,  
  276. $was_paid,  
  277. $this 
  278. ); 
  279.  
  280. return $was_paid; 
  281.  
  282. /** 
  283. * Get Stripe publishable key. 
  284. * 
  285. * @since 1.0.0 
  286. * @api 
  287. * 
  288. * @return string The Stripe API publishable key. 
  289. */ 
  290. public function get_publishable_key() { 
  291. $publishable_key = null; 
  292.  
  293. if ( $this->is_live_mode() ) { 
  294. $publishable_key = $this->publishable_key; 
  295. } else { 
  296. $publishable_key = $this->test_publishable_key; 
  297.  
  298. return apply_filters( 
  299. 'ms_gateway_stripe_get_publishable_key',  
  300. $publishable_key 
  301. ); 
  302.  
  303. /** 
  304. * Get Stripe secret key. 
  305. * 
  306. * @since 1.0.0 
  307. * @internal The secret key should not be used outside this object! 
  308. * 
  309. * @return string The Stripe API secret key. 
  310. */ 
  311. public function get_secret_key() { 
  312. $secret_key = null; 
  313.  
  314. if ( $this->is_live_mode() ) { 
  315. $secret_key = $this->secret_key; 
  316. } else { 
  317. $secret_key = $this->test_secret_key; 
  318.  
  319. return apply_filters( 
  320. 'ms_gateway_stripe_get_secret_key',  
  321. $secret_key 
  322. ); 
  323.  
  324. /** 
  325. * Verify required fields. 
  326. * 
  327. * @since 1.0.0 
  328. * @api 
  329. * 
  330. * @return boolean True if configured. 
  331. */ 
  332. public function is_configured() { 
  333. $key_pub = $this->get_publishable_key(); 
  334. $key_sec = $this->get_secret_key(); 
  335.  
  336. $is_configured = ! ( empty( $key_pub ) || empty( $key_sec ) ); 
  337.  
  338. return apply_filters( 
  339. 'ms_gateway_stripe_is_configured',  
  340. $is_configured 
  341. ); 
  342.  
  343. /** 
  344. * Auto-update some fields of the _api instance if required. 
  345. * 
  346. * @since 1.0.0 
  347. * @internal 
  348. * 
  349. * @param string $key Field name. 
  350. * @param mixed $value Field value. 
  351. */ 
  352. public function __set( $key, $value ) { 
  353. switch ( $key ) { 
  354. case 'test_secret_key': 
  355. case 'test_publishable_key': 
  356. case 'secret_key': 
  357. case 'publishable_key': 
  358. $this->_api->$key = $value; 
  359. break; 
  360.  
  361. if ( property_exists( $this, $key ) ) { 
  362. $this->$key = $value; 
.