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

  1. <?php 
  2. /** 
  3. * Gateway controller. 
  4. * 
  5. * @since 1.0.0 
  6. * 
  7. * @package Membership2 
  8. * @subpackage Controller 
  9. */ 
  10. class MS_Controller_Gateway extends MS_Controller { 
  11.  
  12. /** 
  13. * AJAX action constants. 
  14. * 
  15. * @since 1.0.0 
  16. * 
  17. * @var string 
  18. */ 
  19. const AJAX_ACTION_TOGGLE_GATEWAY = 'toggle_gateway'; 
  20. const AJAX_ACTION_UPDATE_GATEWAY = 'update_gateway'; 
  21.  
  22. /** 
  23. * Allowed actions to execute in template_redirect hook. 
  24. * 
  25. * @since 1.0.0 
  26. * 
  27. * @var string 
  28. */ 
  29. private $allowed_actions = array( 'update_card', 'purchase_button' ); 
  30.  
  31. /** 
  32. * Prepare the gateway controller. 
  33. * 
  34. * @since 1.0.0 
  35. */ 
  36. public function __construct() { 
  37. parent::__construct(); 
  38.  
  39. $this->add_action( 'template_redirect', 'process_actions', 1 ); 
  40.  
  41. $this->add_action( 'ms_controller_gateway_settings_render_view', 'gateway_settings_edit' ); 
  42.  
  43. $this->add_action( 'ms_view_shortcode_invoice_purchase_button', 'invoice_purchase_button', 10, 2 ); 
  44. $this->add_action( 'ms_view_frontend_payment_purchase_button', 'purchase_button', 10, 2 ); 
  45.  
  46. $this->add_action( 'ms_controller_frontend_signup_gateway_form', 'gateway_form_mgr', 1 ); 
  47. $this->add_action( 'ms_controller_frontend_signup_process_purchase', 'process_purchase', 1 ); 
  48. $this->add_filter( 'ms_view_shortcode_membershipsignup_cancel_button', 'cancel_button', 10, 2 ); 
  49.  
  50. $this->add_action( 'ms_view_shortcode_account_card_info', 'card_info' ); 
  51.  
  52. $this->add_action( 'pre_get_posts', 'handle_payment_return', 1 ); 
  53. $this->add_action( 'ms_gateway_transaction_log', 'log_transaction', 10, 8 ); 
  54.  
  55. $this->add_action( 'ms_controller_frontend_enqueue_scripts', 'enqueue_scripts' ); 
  56.  
  57. $this->add_ajax_action( self::AJAX_ACTION_TOGGLE_GATEWAY, 'toggle_ajax_action' ); 
  58. $this->add_ajax_action( self::AJAX_ACTION_UPDATE_GATEWAY, 'ajax_action_update_gateway' ); 
  59.  
  60. /** 
  61. * Handle URI actions for registration. 
  62. * 
  63. * Matches returned 'action' to method to execute. 
  64. * 
  65. * Related action hooks: 
  66. * - template_redirect 
  67. * 
  68. * @since 1.0.0 
  69. */ 
  70. public function process_actions() { 
  71. $action = $this->get_action(); 
  72.  
  73. /** 
  74. * If $action is set, then call relevant method. 
  75. * 
  76. * Methods: 
  77. * @see $allowed_actions property 
  78. * 
  79. */ 
  80. if ( ! empty( $action ) 
  81. && method_exists( $this, $action ) 
  82. && in_array( $action, $this->allowed_actions ) 
  83. ) { 
  84. $this->$action(); 
  85.  
  86. /** 
  87. * Handle Ajax toggle action. 
  88. * 
  89. * Related action hooks: 
  90. * - wp_ajax_toggle_gateway 
  91. * 
  92. * @since 1.0.0 
  93. */ 
  94. public function toggle_ajax_action() { 
  95. $msg = 0; 
  96.  
  97. $fields = array( 'gateway_id' ); 
  98. if ( $this->verify_nonce() 
  99. && self::validate_required( $fields ) 
  100. && $this->is_admin_user() 
  101. ) { 
  102. $msg = $this->gateway_list_do_action( 
  103. 'toggle_activation',  
  104. array( $_POST['gateway_id'] ) 
  105. ); 
  106.  
  107. wp_die( $msg ); 
  108.  
  109. /** 
  110. * Handle Ajax update gateway action. 
  111. * 
  112. * Related action hooks: 
  113. * - wp_ajax_update_gateway 
  114. * 
  115. * @since 1.0.0 
  116. */ 
  117. public function ajax_action_update_gateway() { 
  118. $msg = MS_Helper_Settings::SETTINGS_MSG_NOT_UPDATED; 
  119.  
  120. $fields = array( 'action', 'gateway_id', 'field', 'value' ); 
  121.  
  122. if ( $this->verify_nonce() 
  123. && ( self::validate_required( $fields ) || $_POST['field'] == 'pay_button_url' ) 
  124. && $this->is_admin_user() 
  125. ) { 
  126. lib3()->array->strip_slashes( $_POST, 'value' ); 
  127.  
  128. $msg = $this->gateway_list_do_action( 
  129. $_POST['action'],  
  130. array( $_POST['gateway_id'] ),  
  131. array( $_POST['field'] => $_POST['value'] ) 
  132. ); 
  133.  
  134. wp_die( $msg ); 
  135.  
  136. /** 
  137. * Show gateway settings page. 
  138. * 
  139. * 
  140. * Related action hooks: 
  141. * - ms_controller_gateway_settings_render_view 
  142. * 
  143. * @since 1.0.0 
  144. */ 
  145. public function gateway_settings_edit( $gateway_id ) { 
  146. if ( ! empty( $gateway_id ) 
  147. && MS_Model_Gateway::is_valid_gateway( $gateway_id ) 
  148. ) { 
  149. switch ( $gateway_id ) { 
  150. case MS_Gateway_Manual::ID: 
  151. $view = MS_Factory::create( 'MS_Gateway_Manual_View_Settings' ); 
  152. break; 
  153.  
  154. case MS_Gateway_Paypalsingle::ID: 
  155. $view = MS_Factory::create( 'MS_Gateway_Paypalsingle_View_Settings' ); 
  156. break; 
  157.  
  158. case MS_Gateway_Paypalstandard::ID: 
  159. $view = MS_Factory::create( 'MS_Gateway_Paypalstandard_View_Settings' ); 
  160. break; 
  161.  
  162. case MS_Gateway_Authorize::ID: 
  163. $view = MS_Factory::create( 'MS_Gateway_Authorize_View_Settings' ); 
  164. break; 
  165.  
  166. case MS_Gateway_Stripe::ID: 
  167. $view = MS_Factory::create( 'MS_Gateway_Stripe_View_Settings' ); 
  168. break; 
  169.  
  170. default: 
  171. // Empty form... 
  172. $view = MS_Factory::create( 'MS_View' ); 
  173. break; 
  174.  
  175. $data = array( 
  176. 'model' => MS_Model_Gateway::factory( $gateway_id ),  
  177. 'action' => 'edit',  
  178. ); 
  179.  
  180. $view->data = apply_filters( 
  181. 'ms_gateway_view_settings_edit_data',  
  182. $data 
  183. ); 
  184. $view = apply_filters( 
  185. 'ms_gateway_view_settings_edit',  
  186. $view,  
  187. $gateway_id 
  188. ); 
  189.  
  190. $view->render(); 
  191.  
  192. /** 
  193. * Handle Payment Gateway list actions. 
  194. * 
  195. * @since 1.0.0 
  196. * 
  197. * @param string $action The action to execute. 
  198. * @param int[] $gateways The gateways IDs to process. 
  199. * @param mixed[] $fields The data to process. 
  200. */ 
  201. public function gateway_list_do_action( $action, $gateways, $fields = null ) { 
  202. $msg = MS_Helper_Settings::SETTINGS_MSG_NOT_UPDATED; 
  203. if ( ! $this->is_admin_user() ) { 
  204. return $msg; 
  205.  
  206. foreach ( $gateways as $gateway_id ) { 
  207. $gateway = MS_Model_Gateway::factory( $gateway_id ); 
  208.  
  209. switch ( $action ) { 
  210. case 'toggle_activation': 
  211. $gateway->active = ! $gateway->active; 
  212. $gateway->save(); 
  213. $msg = MS_Helper_Settings::SETTINGS_MSG_UPDATED; 
  214.  
  215. /** 
  216. * Hook called after a gateway-status was toggled. 
  217. * 
  218. * @since 1.0.0 
  219. */ 
  220. do_action( 'ms_gateway_toggle_' . $gateway_id, $gateway ); 
  221. break; 
  222.  
  223. case 'edit': 
  224. case 'update_gateway': 
  225. foreach ( $fields as $field => $value ) { 
  226. $gateway->$field = trim( $value ); 
  227. $gateway->save(); 
  228.  
  229. /** 
  230. * $settings->is_global_payments_set is used to hide global 
  231. * payment settings in the membership setup payment step 
  232. */ 
  233. if ( $gateway->is_configured() ) { 
  234. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  235. $settings->is_global_payments_set = true; 
  236. $settings->save(); 
  237. $msg = MS_Helper_Settings::SETTINGS_MSG_UPDATED; 
  238. } else { 
  239. $msg = MS_Helper_Settings::SETTINGS_MSG_UNCONFIGURED; 
  240.  
  241. /** 
  242. * Hook called after a gateway-settings were modified. 
  243. * 
  244. * @since 1.0.0 
  245. */ 
  246. do_action( 'ms_gateway_changed_' . $gateway_id, $gateway ); 
  247. break; 
  248.  
  249. return apply_filters( 
  250. 'ms_controller_gateway_gateway_list_do_action',  
  251. $msg,  
  252. $action,  
  253. $gateways,  
  254. $fields,  
  255. $this 
  256. ); 
  257.  
  258. /** 
  259. * Show gateway purchase button. 
  260. * 
  261. * Related action hooks: 
  262. * - ms_view_frontend_payment_purchase_button 
  263. * - ms_view_shortcode_invoice_purchase_button 
  264. * 
  265. * @since 1.0.0 
  266. */ 
  267. public function purchase_button( $subscription, $invoice ) { 
  268. // Get only active gateways 
  269. $gateways = MS_Model_Gateway::get_gateways( true ); 
  270. $data = array(); 
  271.  
  272. $membership = $subscription->get_membership(); 
  273. $is_free = false; 
  274.  
  275. if ( $membership->is_free() ) { 
  276. $is_free = true; 
  277. } elseif ( 0 == $invoice->total ) { 
  278. $is_free = true; 
  279. } elseif ( $invoice->uses_trial ) { 
  280. if ( defined( 'MS_PAYPAL_TRIAL_SUBSCRIPTION' ) && MS_PAYPAL_TRIAL_SUBSCRIPTION ) { 
  281. $is_free = false; 
  282. } else { 
  283. $is_free = true; 
  284.  
  285. // show gateway purchase button for every active gateway 
  286. foreach ( $gateways as $gateway ) { 
  287. $view = null; 
  288.  
  289. // Skip gateways that are not configured. 
  290. if ( ! $gateway->is_configured() ) { continue; } 
  291. if ( ! $membership->can_use_gateway( $gateway->id ) ) { continue; } 
  292.  
  293. $data['ms_relationship'] = $subscription; 
  294. $data['gateway'] = $gateway; 
  295. $data['step'] = MS_Controller_Frontend::STEP_PROCESS_PURCHASE; 
  296.  
  297. // Free membership, show only free gateway 
  298. if ( $is_free ) { 
  299. if ( MS_Gateway_Free::ID !== $gateway->id ) { 
  300. continue; 
  301. } elseif ( MS_Gateway_Free::ID === $gateway->id ) { 
  302. // Skip free gateway 
  303. continue; 
  304.  
  305. $view_class = get_class( $gateway ) . '_View_Button'; 
  306. $view = MS_Factory::create( $view_class ); 
  307.  
  308. if ( MS_Gateway_Authorize::ID == $gateway->id ) { 
  309. /** 
  310. * set additional step for authorize.net (gateway specific form) 
  311. * @todo change to use popup, instead of another step (like stripe) 
  312. */ 
  313. $data['step'] = 'gateway_form'; 
  314.  
  315. if ( ! empty( $view ) ) { 
  316. $view = apply_filters( 
  317. 'ms_gateway_view_button',  
  318. $view,  
  319. $gateway->id 
  320. ); 
  321.  
  322. $view->data = apply_filters( 
  323. 'ms_gateway_view_button_data',  
  324. $data,  
  325. $gateway->id 
  326. ); 
  327.  
  328. $html = apply_filters( 
  329. 'ms_controller_gateway_purchase_button_' . $gateway->id,  
  330. $view->to_html(),  
  331. $subscription,  
  332. $this 
  333. ); 
  334.  
  335. echo $html; 
  336.  
  337.  
  338.  
  339. /** 
  340. * Show gateway purchase button in invoice 
  341. * 
  342. * Related action hooks: 
  343. * - ms_view_frontend_payment_purchase_button 
  344. * - ms_view_shortcode_invoice_purchase_button 
  345. * 
  346. * @since 1.0.2.7 
  347. */ 
  348. public function invoice_purchase_button( $subscription, $invoice ) { 
  349. // Get only active gateways 
  350. $gateways = MS_Model_Gateway::get_gateways( true ); 
  351. $data = array(); 
  352.  
  353. $membership = $subscription->get_membership(); 
  354. $is_free = false; 
  355. $is_trial = false; 
  356.  
  357. if ( $membership->is_free() ) { 
  358. $is_free = true; 
  359. } elseif ( 0 == $invoice->total ) { 
  360. $is_free = true; 
  361. } elseif ( $invoice->uses_trial ) { 
  362. if ( defined( 'MS_PAYPAL_TRIAL_SUBSCRIPTION' ) && MS_PAYPAL_TRIAL_SUBSCRIPTION ) { 
  363. $is_free = false; 
  364. } else { 
  365. $is_free = true; 
  366. $is_trial = true; 
  367.  
  368. // show gateway purchase button for every active gateway 
  369. foreach ( $gateways as $gateway ) { 
  370. $view = null; 
  371.  
  372. // Skip gateways that are not configured. 
  373. if ( ! $gateway->is_configured() ) { continue; } 
  374. if ( ! $membership->can_use_gateway( $gateway->id ) ) { continue; } 
  375.  
  376. $data['ms_relationship'] = $subscription; 
  377. $data['gateway'] = $gateway; 
  378. $data['step'] = MS_Controller_Frontend::STEP_PROCESS_PURCHASE; 
  379.  
  380. // Free membership, show only free gateway 
  381. if ( $is_free && ! $is_trial ) { 
  382. if ( MS_Gateway_Free::ID !== $gateway->id ) { 
  383. continue; 
  384. } elseif ( MS_Gateway_Free::ID === $gateway->id ) { 
  385. // Skip free gateway 
  386. continue; 
  387.  
  388. $view_class = get_class( $gateway ) . '_View_Button'; 
  389. $view = MS_Factory::create( $view_class ); 
  390.  
  391. if ( MS_Gateway_Authorize::ID == $gateway->id ) { 
  392. /** 
  393. * set additional step for authorize.net (gateway specific form) 
  394. * @todo change to use popup, instead of another step (like stripe) 
  395. */ 
  396. $data['step'] = 'gateway_form'; 
  397.  
  398. if ( ! empty( $view ) ) { 
  399. $view = apply_filters( 
  400. 'ms_gateway_view_button',  
  401. $view,  
  402. $gateway->id 
  403. ); 
  404.  
  405. $view->data = apply_filters( 
  406. 'ms_gateway_view_button_data',  
  407. $data,  
  408. $gateway->id 
  409. ); 
  410.  
  411. $html = apply_filters( 
  412. 'ms_controller_gateway_purchase_button_'. $gateway->id,  
  413. $view->to_html(),  
  414. $subscription,  
  415. $this 
  416. ); 
  417.  
  418. echo $html; 
  419.  
  420.  
  421. /** 
  422. * Show gateway purchase button. 
  423. * 
  424. * Related action hooks: 
  425. * - ms_view_shortcode_membershipsignup_cancel_button 
  426. * 
  427. * @since 1.0.0 
  428. */ 
  429. public function cancel_button( $button, $subscription ) { 
  430. $view = null; 
  431. $data = array(); 
  432. $data['ms_relationship'] = $subscription; 
  433. $new_button = null; 
  434.  
  435. switch ( $subscription->gateway_id ) { 
  436. case MS_Gateway_Paypalstandard::ID: 
  437. $view = MS_Factory::create( 'MS_Gateway_Paypalstandard_View_Cancel' ); 
  438. $data['gateway'] = $subscription->get_gateway(); 
  439. break; 
  440.  
  441. case MS_Gateway_Authorize::ID: 
  442. case MS_Gateway_Paypalsingle::ID: 
  443. case MS_Gateway_Stripe::ID: 
  444. case MS_Gateway_Free::ID: 
  445. case MS_Gateway_Manual::ID: 
  446. default: 
  447. break; 
  448. $view = apply_filters( 'ms_gateway_view_cancel_button', $view ); 
  449.  
  450. if ( $view && is_a( $view, 'MS_View' ) ) { 
  451. $view->data = apply_filters( 
  452. 'ms_gateway_view_cancel_button_data',  
  453. $data 
  454. ); 
  455.  
  456. $new_button = $view->get_button(); 
  457.  
  458. if ( ! $new_button ) { 
  459. $new_button = $button; 
  460.  
  461. return apply_filters( 
  462. 'ms_controller_gateway_cancel_button',  
  463. $new_button,  
  464. $subscription,  
  465. $this 
  466. ); 
  467.  
  468. /** 
  469. * Set hook to handle gateway extra form to commit payments. 
  470. * 
  471. * Related action hooks: 
  472. * - ms_controller_frontend_signup_gateway_form 
  473. * 
  474. * @since 1.0.0 
  475. */ 
  476. public function gateway_form_mgr() { 
  477. // Display gateway form 
  478. $this->add_filter( 'the_content', 'gateway_form', 10 ); 
  479.  
  480. // Enqueue styles and scripts used 
  481. $this->add_action( 'wp_enqueue_scripts', 'enqueue_scripts' ); 
  482.  
  483. /** 
  484. * Handles gateway extra form to commit payments. 
  485. * 
  486. * Related filter hooks: 
  487. * - the_content 
  488. * 
  489. * @since 1.0.0 
  490. * 
  491. * @param string $content The page content to filter. 
  492. * @return string The filtered content. 
  493. */ 
  494. public function gateway_form( $content ) { 
  495. $data = array(); 
  496. $html = ''; 
  497.  
  498. // Do not parse the form when building the excerpt. 
  499. global $wp_current_filter; 
  500. if ( in_array( 'get_the_excerpt', $wp_current_filter ) ) { 
  501. return ''; 
  502.  
  503. $fields = array( 'gateway', 'ms_relationship_id' ); 
  504. if ( self::validate_required( $fields ) 
  505. && MS_Model_Gateway::is_valid_gateway( $_POST['gateway'] ) 
  506. ) { 
  507. $data['gateway'] = $_POST['gateway']; 
  508. $data['ms_relationship_id'] = $_POST['ms_relationship_id']; 
  509. $view = null; 
  510.  
  511. $subscription = MS_Factory::load( 
  512. 'MS_Model_Relationship',  
  513. $_POST['ms_relationship_id'] 
  514. ); 
  515.  
  516. switch ( $_POST['gateway'] ) { 
  517. case MS_Gateway_Authorize::ID: 
  518. $member = $subscription->get_member(); 
  519. $view = MS_Factory::create( 'MS_Gateway_Authorize_View_Form' ); 
  520. $gateway = MS_Model_Gateway::factory( MS_Gateway_Authorize::ID ); 
  521. $data['countries'] = $gateway->get_country_codes(); 
  522.  
  523. $data['action'] = $this->get_action(); 
  524.  
  525. if ( 'update_card' == $this->get_action() ) { 
  526. // Only new card option available on update card action. 
  527. $data['cim_profiles'] = array(); 
  528. } else { 
  529. // show existing credit card. 
  530. $data['cim_profiles'] = $gateway->get_cim_profile( $member ); 
  531.  
  532. lib3()->array->strip_slashes( $_POST, 'auth_error' ); 
  533.  
  534. $data['cim_payment_profile_id'] = $gateway->get_cim_payment_profile_id( $member ); 
  535. $data['auth_error'] = ! empty( $_POST['auth_error'] ) ? $_POST['auth_error'] : ''; 
  536. break; 
  537.  
  538. default: 
  539. break; 
  540.  
  541. $view = apply_filters( 
  542. 'ms_gateway_view_form',  
  543. $view,  
  544. $_POST['gateway'],  
  545. $subscription 
  546. ); 
  547.  
  548. if ( $view && is_a( $view, 'MS_View' ) ) { 
  549. $view->data = apply_filters( 
  550. 'ms_gateway_view_form_data',  
  551. $data 
  552. ); 
  553.  
  554. $html = $view->to_html(); 
  555.  
  556. return apply_filters( 
  557. 'ms_controller_gateway_form',  
  558. $html,  
  559. $this 
  560. ); 
  561.  
  562. /** 
  563. * Process purchase using gateway. 
  564. * 
  565. * Related Action Hooks: 
  566. * - ms_controller_frontend_signup_process_purchase 
  567. * 
  568. * @since 1.0.0 
  569. */ 
  570. public function process_purchase() { 
  571. $fields = array( 'gateway', 'ms_relationship_id' ); 
  572.  
  573. lib3()->array->equip_request( 'gateway', 'ms_relationship_id' ); 
  574.  
  575. $valid = true; 
  576. $nonce_name = $_REQUEST['gateway'] . '_' . $_REQUEST['ms_relationship_id']; 
  577.  
  578. if ( ! self::validate_required( $fields, 'any' ) ) { 
  579. $valid = false; 
  580. $err = 'GAT-01 (invalid fields)'; 
  581. } elseif ( ! MS_Model_Gateway::is_valid_gateway( $_REQUEST['gateway'] ) ) { 
  582. $valid = false; 
  583. $err = 'GAT-02 (invalid gateway)'; 
  584. } elseif ( ! $this->verify_nonce( $nonce_name, 'any' ) ) { 
  585. $valid = false; 
  586. $err = 'GAT-03 (invalid nonce)'; 
  587.  
  588. if ( $valid ) { 
  589. $subscription = MS_Factory::load( 
  590. 'MS_Model_Relationship',  
  591. $_REQUEST['ms_relationship_id'] 
  592. ); 
  593.  
  594. $gateway_id = $_REQUEST['gateway']; 
  595. $gateway = MS_Model_Gateway::factory( $gateway_id ); 
  596.  
  597. try { 
  598. $invoice = $gateway->process_purchase( $subscription ); 
  599.  
  600. // If invoice is successfully paid, redirect to welcome page. 
  601. if ( $invoice->is_paid() 
  602. || ( $invoice->uses_trial 
  603. && MS_Model_Invoice::STATUS_BILLED == $invoice->status 
  604. ) { 
  605. // Make sure to respect the single-membership rule 
  606. $this->validate_membership_states( $subscription ); 
  607.  
  608. // Redirect user to the Payment-Completed page. 
  609. if ( ! defined( 'IS_UNIT_TEST' ) ) { 
  610. MS_Model_Pages::redirect_to( 
  611. MS_Model_Pages::MS_PAGE_REG_COMPLETE,  
  612. array( 'ms_relationship_id' => $subscription->id ) 
  613. ); 
  614. } elseif ( MS_Gateway_Manual::ID == $gateway_id ) { 
  615. // For manual gateway payments. 
  616. $this->add_action( 'the_content', 'purchase_info_content' ); 
  617. } else { 
  618. // Something went wrong, the payment was not successful. 
  619. $this->add_action( 'the_content', 'purchase_error_content' ); 
  620. } catch ( Exception $e ) { 
  621. MS_Helper_Debug::log( $e->getMessage() ); 
  622.  
  623. switch ( $gateway_id ) { 
  624. case MS_Gateway_Authorize::ID: 
  625. $_POST['auth_error'] = $e->getMessage(); 
  626. // call action to step back 
  627. do_action( 'ms_controller_frontend_signup_gateway_form' ); 
  628. break; 
  629.  
  630. case MS_Gateway_Stripe::ID: 
  631. case MS_Gateway_Stripeplan::ID: 
  632. $_POST['error'] = sprintf( 
  633. __( 'Error: %s', 'membership2' ),  
  634. $e->getMessage() 
  635. ); 
  636.  
  637. // Hack to send the error message back to the payment_table. 
  638. MS_Plugin::instance()->controller->controllers['frontend']->add_action( 
  639. 'the_content',  
  640. 'payment_table', 1 
  641. ); 
  642. break; 
  643.  
  644. default: 
  645. do_action( 'ms_controller_gateway_form_error', $e ); 
  646. $this->add_action( 'the_content', 'purchase_error_content' ); 
  647. break; 
  648. } else { 
  649. MS_Helper_Debug::log( 'Error Code ' . $err ); 
  650.  
  651. $this->add_action( 'the_content', 'purchase_error_content' ); 
  652.  
  653. // Hack to show signup page in case of errors 
  654. $ms_page = MS_Model_Pages::get_page( MS_Model_Pages::MS_PAGE_REGISTER ); 
  655.  
  656. if ( $ms_page ) { 
  657. // During unit-testing the $ms_page object might be empty. 
  658. global $wp_query; 
  659. $wp_query->query_vars['page_id'] = $ms_page->ID; 
  660. $wp_query->query_vars['post_type'] = 'page'; 
  661.  
  662. do_action( 
  663. 'ms_controller_gateway_process_purchase_after',  
  664. $this 
  665. ); 
  666.  
  667. /** 
  668. * Make sure that we respect the Single-Membership rule. 
  669. * This rule is active when the "Multiple-Memberships" Add-on is DISABLED. 
  670. * 
  671. * @since 1.0.0 
  672. * 
  673. * @param MS_Model_Relationship $new_relationship 
  674. */ 
  675. protected function validate_membership_states( $new_relationship ) { 
  676. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MULTI_MEMBERSHIPS ) ) { 
  677. // Multiple memberships allowed. No need to check anything. 
  678. return; 
  679.  
  680. $cancel_these = array( 
  681. MS_Model_Relationship::STATUS_TRIAL,  
  682. MS_Model_Relationship::STATUS_ACTIVE,  
  683. MS_Model_Relationship::STATUS_PENDING,  
  684. ); 
  685.  
  686. $member = $new_relationship->get_member(); 
  687. foreach ( $member->subscriptions as $subscription ) { 
  688. if ( $subscription->id === $new_relationship->id ) { continue; } 
  689. if ( in_array( $subscription->status, $cancel_these ) ) { 
  690. $subscription->cancel_membership(); 
  691.  
  692. /** 
  693. * Show signup page with custom content. 
  694. * 
  695. * This is used by manual gateway (overridden) to show payment info. 
  696. * 
  697. * Related action hooks: 
  698. * 
  699. * @since 1.0.0 
  700. * 
  701. * @param string $content The page content to filter. 
  702. * @return string The filtered content. 
  703. */ 
  704. public function purchase_info_content( $content ) { 
  705. return apply_filters( 
  706. 'ms_controller_gateway_purchase_info_content',  
  707. $content,  
  708. $this 
  709. ); 
  710.  
  711. /** 
  712. * Show error message in the signup page. 
  713. * 
  714. * Related action hooks: 
  715. * 
  716. * @since 1.0.0 
  717. */ 
  718. public function purchase_error_content( $content ) { 
  719. return apply_filters( 
  720. 'ms_controller_gateway_purchase_error_content',  
  721. __( 'Sorry, your signup request has failed. Try again.', 'membership2' ),  
  722. $content,  
  723. $this 
  724. ); 
  725.  
  726. /** 
  727. * Handle payment gateway return IPNs. 
  728. * 
  729. * Used by Paypal gateways. 
  730. * A redirection rule is set up in the main MS_Plugin object 
  731. * (protected_content.php): 
  732. * /ms-payment-return/XYZ becomes index.php?paymentgateway=XYZ 
  733. * 
  734. * Related action hooks: 
  735. * - pre_get_posts 
  736. * 
  737. * @todo Review how this works when we use OAuth API's with gateways. 
  738. * 
  739. * @since 1.0.0 
  740. * 
  741. * @param WP_Query $wp_query The WordPress query object 
  742. */ 
  743. public function handle_payment_return( $wp_query ) { 
  744. // Do not check custom loops. 
  745. if ( ! $wp_query->is_main_query() ) { return; } 
  746.  
  747. if ( ! empty( $wp_query->query_vars['paymentgateway'] ) ) { 
  748. $gateway = $wp_query->query_vars['paymentgateway']; 
  749.  
  750. /** 
  751. * In 1.1.0 the underscore in payment gateway names was removed. 
  752. * To compensate for this we need to continue listen to these old 
  753. * gateway-names. 
  754. */ 
  755. switch ( $gateway ) { 
  756. case 'paypal_single': $gateway = 'paypalsingle'; break; 
  757. case 'paypal_standard': $gateway = 'paypalstandard'; break; 
  758. case 'paypal-single': $gateway = 'paypalsingle'; break; 
  759. case 'paypal-standard': $gateway = 'paypalstandard'; break; 
  760. case 'paypalsolo': $gateway = 'paypalsingle'; break; // M1 
  761. case 'paypalexpress': $gateway = 'paypalstandard'; break; //M1 
  762.  
  763. if ( MS_Model_Gateway::is_active( $gateway ) ) { 
  764. $action = 'ms_gateway_handle_payment_return_' . $gateway; 
  765. do_action( $action ); 
  766. } else { 
  767. // Log the payment attempt when the gateway is not active. 
  768. if ( MS_Model_Gateway::is_valid_gateway( $gateway ) ) { 
  769. $note = __( 'Gateway is inactive', 'membership2' ); 
  770. } else { 
  771. $note = sprintf( 
  772. __( 'Unknown Gateway: %s', 'membership2' ),  
  773. $gateway 
  774. ); 
  775.  
  776. do_action( 
  777. 'ms_gateway_transaction_log',  
  778. $gateway, // gateway ID 
  779. 'handle', // request|process|handle 
  780. false, // success flag 
  781. 0, // subscription ID 
  782. 0, // invoice ID 
  783. 0, // charged amount 
  784. $note, // Descriptive text 
  785. '' // External ID 
  786. ); 
  787.  
  788. /** 
  789. * Show gateway credit card information. 
  790. * 
  791. * If a card is used, show it in account's page. 
  792. * 
  793. * Related action hooks: 
  794. * - ms_view_shortcode_account_card_info 
  795. * 
  796. * @since 1.0.0 
  797. * 
  798. * @param mixed $data The data passed to hooked view. 
  799. */ 
  800. public function card_info( $data = null ) { 
  801. if ( ! empty( $data['gateway'] ) && is_array( $data['gateway'] ) ) { 
  802. $gateways = array(); 
  803.  
  804. foreach ( $data['gateway'] as $ms_relationship_id => $gateway ) { 
  805. // avoid duplicates 
  806. if ( ! in_array( $gateway->id, $gateways ) ) { 
  807. $gateways[] = $gateway->id; 
  808. } else { 
  809. continue; 
  810. $view = null; 
  811.  
  812. switch ( $gateway->id ) { 
  813. case MS_Gateway_Stripe::ID: 
  814. $member = MS_Model_Member::get_current_member(); 
  815. $data['stripe'] = $member->get_gateway_profile( 
  816. $gateway->id 
  817. ); 
  818.  
  819. if ( empty( $data['stripe']['card_exp'] ) ) { 
  820. continue 2; 
  821.  
  822. $view = MS_Factory::create( 'MS_Gateway_Stripe_View_Card' ); 
  823. $data['member'] = $member; 
  824. $data['publishable_key'] = $gateway->get_publishable_key(); 
  825. $data['ms_relationship_id'] = $ms_relationship_id; 
  826. $data['gateway'] = $gateway; 
  827. break; 
  828.  
  829. case MS_Gateway_Authorize::ID: 
  830. $member = MS_Model_Member::get_current_member(); 
  831. $data['authorize'] = $member->get_gateway_profile( 
  832. $gateway->id 
  833. ); 
  834.  
  835. if ( empty( $data['authorize']['card_exp'] ) ) { 
  836. continue 2; 
  837.  
  838. $view = MS_Factory::create( 'MS_Gateway_Authorize_View_Card' ); 
  839. $data['member'] = $member; 
  840. $data['ms_relationship_id'] = $ms_relationship_id; 
  841. $data['gateway'] = $gateway; 
  842. break; 
  843.  
  844. default: 
  845. break; 
  846.  
  847. if ( ! empty( $view ) ) { 
  848. $view = apply_filters( 
  849. 'ms_gateway_view_change_card',  
  850. $view,  
  851. $gateway->id 
  852. ); 
  853. $view->data = apply_filters( 
  854. 'ms_gateway_view_change_card_data',  
  855. $data,  
  856. $gateway->id 
  857. ); 
  858.  
  859. $html = $view->to_html(); 
  860. echo '' . $html; 
  861.  
  862. /** 
  863. * Handle update credit card information in gateway. 
  864. * 
  865. * Used to change credit card info in account's page. 
  866. * 
  867. * Related action hooks: 
  868. * - template_redirect 
  869. * 
  870. * @since 1.0.0 
  871. */ 
  872. public function update_card() { 
  873. if ( ! empty( $_POST['gateway'] ) ) { 
  874. $gateway = MS_Model_Gateway::factory( $_POST['gateway'] ); 
  875. $member = MS_Model_Member::get_current_member(); 
  876.  
  877. switch ( $gateway->id ) { 
  878. case MS_Gateway_Stripe::ID: 
  879. if ( ! empty( $_POST['stripeToken'] ) && $this->verify_nonce() ) { 
  880. lib3()->array->strip_slashes( $_POST, 'stripeToken' ); 
  881.  
  882. $gateway->add_card( $member, $_POST['stripeToken'] ); 
  883. if ( ! empty( $_POST['ms_relationship_id'] ) ) { 
  884. $ms_relationship = MS_Factory::load( 
  885. 'MS_Model_Relationship',  
  886. $_POST['ms_relationship_id'] 
  887. ); 
  888. MS_Model_Event::save_event( 
  889. MS_Model_Event::TYPE_UPDATED_INFO,  
  890. $ms_relationship 
  891. ); 
  892.  
  893. wp_safe_redirect( 
  894. esc_url_raw( add_query_arg( array( 'msg' => 1 ) ) ) 
  895. ); 
  896. exit; 
  897. break; 
  898.  
  899. case MS_Gateway_Authorize::ID: 
  900. if ( $this->verify_nonce() ) { 
  901. do_action( 
  902. 'ms_controller_frontend_signup_gateway_form',  
  903. $this 
  904. ); 
  905. } elseif ( ! empty( $_POST['ms_relationship_id'] ) 
  906. && $this->verify_nonce( $_POST['gateway'] .'_' . $_POST['ms_relationship_id'] ) 
  907. ) { 
  908. $gateway->update_cim_profile( $member ); 
  909. $gateway->save_card_info( $member ); 
  910. if ( ! empty( $_POST['ms_relationship_id'] ) ) { 
  911. $ms_relationship = MS_Factory::load( 
  912. 'MS_Model_Relationship',  
  913. $_POST['ms_relationship_id'] 
  914. ); 
  915. MS_Model_Event::save_event( 
  916. MS_Model_Event::TYPE_UPDATED_INFO,  
  917. $ms_relationship 
  918. ); 
  919.  
  920. wp_safe_redirect( 
  921. esc_url_raw( add_query_arg( array( 'msg' => 1 ) ) ) 
  922. ); 
  923. exit; 
  924. break; 
  925.  
  926. default: 
  927. break; 
  928.  
  929. do_action( 
  930. 'ms_controller_gateway_update_card',  
  931. $this 
  932. ); 
  933.  
  934. /** 
  935. * Saves transaction details to the database. The transaction logs can later 
  936. * be displayed in the Billings section. 
  937. * 
  938. * @since 1.0.0 
  939. * @internal Action handler for 'ms_gateway_transaction_log' 
  940. * 
  941. * 
  942. * @param string $gateway_id The gateway ID. 
  943. * @param string $method Following values: 
  944. * "handle": IPN response 
  945. * "process": Process order (i.e. user comes from Payment screen) 
  946. * "request": Automatically request recurring payment 
  947. * @param bool $success True means that the transaction was paid/successful. 
  948. * False indicates an error. 
  949. * NULL indicates a message that was intentionally skipped. 
  950. * @param int $subscription_id 
  951. * @param int $invoice_id 
  952. * @param float $amount Payment amount. 
  953. * @param string $notes Additional text to describe the transaction or error. 
  954. * @param string $external_id The gateways transaction ID. 
  955. */ 
  956. public function log_transaction( $gateway_id, $method, $success, $subscription_id, $invoice_id, $amount, $notes, $external_id ) { 
  957. $log = MS_Factory::create( 'MS_Model_Transactionlog' ); 
  958. $log->description = $notes; 
  959. $log->gateway_id = $gateway_id; 
  960. $log->method = $method; 
  961. $log->success = $success; 
  962. $log->subscription_id = $subscription_id; 
  963. $log->invoice_id = $invoice_id; 
  964. $log->amount = $amount; 
  965. $log->external_id = $external_id; 
  966. $log->save(); 
  967.  
  968. /** 
  969. * Adds CSS and javascript 
  970. * 
  971. * @since 1.0.0 
  972. */ 
  973. public function enqueue_scripts( $step = null ) { 
  974. if ( empty( $step ) && ! empty( $_POST['step'] ) ) { 
  975. $step = $_POST['step']; 
  976.  
  977. lib3()->array->equip_post( 'gateway' ); 
  978. $gateway_id = $_POST['gateway']; 
  979.  
  980. switch ( $step ) { 
  981. case MS_Controller_Frontend::STEP_GATEWAY_FORM: 
  982. case MS_Controller_Frontend::STEP_PROCESS_PURCHASE: 
  983. if ( MS_Gateway_Authorize::ID == $gateway_id ) { 
  984. wp_enqueue_script( 'jquery-validate' ); 
  985.  
  986. $data = array( 
  987. 'ms_init' => array( 'gateway_authorize' ),  
  988. ); 
  989.  
  990. lib3()->ui->add( 'core' ); 
  991. lib3()->ui->add( 'select' ); 
  992. lib3()->ui->data( 'ms_data', $data ); 
  993. wp_enqueue_script( 'ms-public' ); 
  994. break; 
.