/app/addon/taxamo/class-ms-addon-taxamo.php

  1. <?php 
  2. /** 
  3. * Add-On controller for: Taxamo 
  4. * 
  5. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  6. * How it works: 
  7. * 
  8. * Tax details are stored in the user-metadata. 
  9. * When the user metadata don't contain tax details then Taxamo API is used to 
  10. * get the defaults for the country detected by IP address. 
  11. * 
  12. * If this details are not correct then the user can modify the tax details 
  13. * stored in user-metadata via his profile and on the checkout page by providing 
  14. * two matching proofs such as VAT Number, Credit Card Number, etc. 
  15. * 
  16. * Though tax details are stored in user-metadata each invoice includes basic 
  17. * details (tax-rate and tax-name) since an invoice cannot be modified once it 
  18. * was paid. Changes in the user-meta will affect all invoices that are 
  19. * generated in the future. 
  20. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  21. * 
  22. * @since 1.0.0 
  23. * 
  24. * @package Membership2 
  25. * @subpackage Controller 
  26. */ 
  27. class MS_Addon_Taxamo extends MS_Addon { 
  28.  
  29. /** 
  30. * The Add-on ID 
  31. * 
  32. * @since 1.0.0 
  33. */ 
  34. const ID = 'addon_taxamo'; 
  35.  
  36. // Ajax Actions 
  37. const AJAX_SAVE_SETTING = 'addon_taxamo_save'; 
  38. const AJAX_SAVE_USERPROFILE = 'addon_taxamo_profile'; 
  39.  
  40. /** 
  41. * HTML code that is output in the page footer when tax-editor dialog is 
  42. * available. 
  43. * 
  44. * @since 1.0.0 
  45. * @var string 
  46. */ 
  47. static protected $footer_html = ''; 
  48.  
  49. /** 
  50. * Checks if the current Add-on is enabled 
  51. * 
  52. * @since 1.0.0 
  53. * @return bool 
  54. */ 
  55. static public function is_active() { 
  56. return MS_Model_Addon::is_enabled( self::ID ); 
  57.  
  58. /** 
  59. * Returns the Add-on ID (self::ID). 
  60. * 
  61. * @since 1.0.1.0 
  62. * @return string 
  63. */ 
  64. public function get_id() { 
  65. return self::ID; 
  66.  
  67. /** 
  68. * Initializes the Add-on. Always executed. 
  69. * 
  70. * @since 1.0.0 
  71. */ 
  72. public function init() { 
  73. if ( self::is_active() ) { 
  74. $this->add_action( 
  75. 'ms_tax_editor',  
  76. 'tax_editor' 
  77. ); 
  78.  
  79. // Add new settings tab 
  80. $this->add_filter( 
  81. 'ms_controller_settings_get_tabs',  
  82. 'settings_tabs',  
  83. 10, 2 
  84. ); 
  85.  
  86. $this->add_filter( 
  87. 'ms_view_settings_edit_render_callback',  
  88. 'manage_render_callback',  
  89. 10, 3 
  90. ); 
  91.  
  92. // Save settings via ajax 
  93. $this->add_ajax_action( 
  94. self::AJAX_SAVE_SETTING,  
  95. 'ajax_save_setting' 
  96. ); 
  97.  
  98. // Save settings via ajax 
  99. $this->add_ajax_action( 
  100. self::AJAX_SAVE_USERPROFILE,  
  101. 'ajax_save_userprofile',  
  102. true, true 
  103. ); 
  104.  
  105. // Confirm payments with Taxamo 
  106. $this->add_action( 
  107. 'ms_gateway_paypalsingle_payment_processed_' . MS_Model_Invoice::STATUS_PAID,  
  108. 'confirm_payment' 
  109. ); 
  110. $this->add_action( 
  111. 'ms_gateway_paypalstandard_payment_processed_' . MS_Model_Invoice::STATUS_PAID,  
  112. 'confirm_payment' 
  113. ); 
  114.  
  115. // Track the transaction in taxamo 
  116. $this->add_action( 
  117. 'ms_invoice_paid',  
  118. 'invoice_paid' 
  119. ); 
  120.  
  121. // Add taxes to the price, based on users country. 
  122. $this->add_filter( 
  123. 'ms_apply_taxes',  
  124. 'apply_taxes' 
  125. ); 
  126.  
  127. // Set tax-details on a new invoice. 
  128. $this->add_filter( 
  129. 'ms_invoice_tax_rate',  
  130. 'invoice_tax_rate' 
  131. ); 
  132.  
  133. $this->add_filter( 
  134. 'ms_invoice_tax_name',  
  135. 'invoice_tax_name' 
  136. ); 
  137.  
  138. $this->add_filter( 
  139. 'ms_model_invoice_create_before_save',  
  140. 'invoice_tax_profile' 
  141. ); 
  142.  
  143. $this->add_filter( 
  144. 'ms_gateway_stripe_credit_card_saved',  
  145. 'stripe_card_profile' 
  146. ); 
  147.  
  148. /** 
  149. * Registers the Add-On 
  150. * 
  151. * @since 1.0.0 
  152. * @param array $list The Add-Ons list. 
  153. * @return array The updated Add-Ons list. 
  154. */ 
  155. public function register( $list ) { 
  156. $list[ self::ID ] = (object) array( 
  157. 'name' => __( 'Taxamo', 'membership2' ),  
  158. 'description' => __( 'Addresses EU VAT regulations.', 'membership2' ),  
  159. 'icon' => 'wpmui-fa wpmui-fa-euro',  
  160. 'details' => array( 
  161. array( 
  162. 'type' => MS_Helper_Html::TYPE_HTML_TEXT,  
  163. 'title' => __( 'Settings', 'membership2' ),  
  164. 'desc' => __( 'When this Add-on is enabled you will see a new section in the "Settings" page with additional options.', 'membership2' ),  
  165. ),  
  166. ),  
  167. ); 
  168.  
  169. return $list; 
  170.  
  171. /** 
  172. * Returns the Taxamo-Settings model. 
  173. * 
  174. * @since 1.0.0 
  175. * @return MS_Addon_Taxamo_Model 
  176. */ 
  177. static public function model() { 
  178. static $Model = null; 
  179.  
  180. if ( null === $Model ) { 
  181. $Model = MS_Factory::load( 'MS_Addon_Taxamo_Model' ); 
  182.  
  183. return $Model; 
  184.  
  185. /** 
  186. * Add taxamo settings tab in settings page. 
  187. * 
  188. * @since 1.0.0 
  189. * 
  190. * @param array $tabs The current tabs. 
  191. * @return array The filtered tabs. 
  192. */ 
  193. public function settings_tabs( $tabs ) { 
  194. $tabs[ self::ID ] = array( 
  195. 'title' => __( 'Taxamo', 'membership2' ),  
  196. 'url' => MS_Controller_Plugin::get_admin_url( 
  197. 'settings',  
  198. array( 'tab' => self::ID ) 
  199. ),  
  200. ); 
  201.  
  202. return $tabs; 
  203.  
  204. /** 
  205. * Add taxamo settings-view callback. 
  206. * 
  207. * @since 1.0.0 
  208. * 
  209. * @param array $callback The current function callback. 
  210. * @param string $tab The current membership rule tab. 
  211. * @param array $data The data shared to the view. 
  212. * @return array The filtered callback. 
  213. */ 
  214. public function manage_render_callback( $callback, $tab, $data ) { 
  215. if ( self::ID == $tab ) { 
  216. $view = MS_Factory::load( 'MS_Addon_Taxamo_View' ); 
  217. $callback = array( $view, 'render_tab' ); 
  218.  
  219. return $callback; 
  220.  
  221. /** 
  222. * Handle Ajax update custom setting action. 
  223. * 
  224. * @since 1.0.0 
  225. */ 
  226. public function ajax_save_setting() { 
  227. $msg = MS_Helper_Settings::SETTINGS_MSG_NOT_UPDATED; 
  228.  
  229. $isset = array( 'field', 'value' ); 
  230. if ( $this->verify_nonce() 
  231. && self::validate_required( $isset, 'POST', false ) 
  232. && $this->is_admin_user() 
  233. ) { 
  234. $model = self::model(); 
  235. lib3()->array->strip_slashes( $_POST, 'value' ); 
  236.  
  237. $model->set( $_POST['field'], $_POST['value'] ); 
  238. $model->save(); 
  239. $msg = MS_Helper_Settings::SETTINGS_MSG_UPDATED; 
  240.  
  241. wp_die( $msg ); 
  242.  
  243. /** 
  244. * Adds taxes to the net-amount. 
  245. * 
  246. * @since 1.0.0 
  247. * @param numeric $net_value Net value 
  248. * @return numeric Gross value 
  249. */ 
  250. public function apply_taxes( $net_value ) { 
  251. $gross_value = 0; 
  252.  
  253. if ( is_numeric( $net_value ) ) { 
  254. $tax = MS_Addon_Taxamo_Api::tax_info( $net_value ); 
  255. $gross_value = $net_value + $tax->amount; 
  256.  
  257. return $gross_value; 
  258.  
  259. /** 
  260. * Return the tax-rate for the users country. 
  261. * 
  262. * @since 1.0.0 
  263. * @param numeric $rate Default rate (0) 
  264. * @return numeric Tax rate to apply (e.g. 20 for 20%) 
  265. */ 
  266. public function invoice_tax_rate( $rate ) { 
  267. $tax = MS_Addon_Taxamo_Api::tax_info(); 
  268.  
  269. return $tax->rate; 
  270.  
  271. /** 
  272. * Return the tax-name for the users country. 
  273. * 
  274. * @since 1.0.0 
  275. * @param string $name Default name (empty string) 
  276. * @return string Tax display-name (e.g. 'EU Standard Tax (20 %)') 
  277. */ 
  278. public function invoice_tax_name( $rate ) { 
  279. $tax = MS_Addon_Taxamo_Api::tax_info(); 
  280.  
  281. return $tax->rate . '% ' . $tax->name; 
  282.  
  283. /** 
  284. * Saves tax-profile to the invoice. 
  285. * 
  286. * @since 1.0.0 
  287. * @param MS_Model_Invoice $invoice The invoice object. 
  288. * @return MS_Model_Invoice 
  289. */ 
  290. public function invoice_tax_profile( $invoice ) { 
  291. $invoice->set_custom_data( 'tax_profile', MS_Addon_Taxamo_Api::get_tax_profile() ); 
  292.  
  293. return $invoice; 
  294.  
  295. /** 
  296. * When a payment was made via stripe we can use the card details as an 
  297. * evidence for the tax-country. 
  298. * 
  299. * @since 1.0.0 
  300. * @param Stripe_Card $card The card details. 
  301. */ 
  302. public function stripe_card_profile( $card ) { 
  303. $card_info = $card->brand . ' Last4: ' . $card->last4 . ' (Stripe ID ' . $card->id . ')'; 
  304.  
  305. $card_country = (object) array( 
  306. 'code' => $card->country,  
  307. ); 
  308.  
  309. MS_Addon_Taxamo_Api::set_tax_profile( 'card_info', $card_info ); 
  310. MS_Addon_Taxamo_Api::set_tax_profile( 'card_country', $card_country ); 
  311.  
  312. /** 
  313. * When an invoice was paid we need to notify taxamo of the transaction. 
  314. * 
  315. * @since 1.0.0 
  316. * @param MS_Model_Invoice $invoice The processed invoice. 
  317. */ 
  318. public function invoice_paid( $invoice ) { 
  319. if ( ! $invoice->is_paid() ) { return; } 
  320. if ( 0 == $invoice->total ) { return; } 
  321.  
  322. $membership = $invoice->get_membership(); 
  323. $member = $invoice->get_member(); 
  324.  
  325. MS_Addon_Taxamo_Api::register_payment( 
  326. $invoice->total, // Transaction amount 
  327. $membership->name, // Transaction title 
  328. $invoice->tax_rate, // Tax-rate 
  329. $invoice->get_invoice_number(), // Internal Transaction ID = Invoice Number 
  330. $member->full_name, // Buyer name 
  331. $member->email, // Buyer email 
  332. $invoice->gateway_id, // Payment gateway 
  333. $invoice->currency, // Currency of invoice 
  334. $invoice->checkout_ip // IP of the user 
  335. ); 
  336.  
  337. /** 
  338. * Load taxamo scripts. 
  339. * 
  340. * @since 1.0.0 
  341. * @param MS_Model_Invoice $invoice Optional. The invoice to edit. 
  342. */ 
  343. public function tax_editor( $invoice ) { 
  344. static $Done = false; 
  345.  
  346. // Only one tax-editor is possible per page. 
  347. if ( $Done ) { return; } 
  348. $Done = true; 
  349.  
  350. $this->add_action( 
  351. 'wp_footer',  
  352. 'tax_editor_footer' 
  353. ); 
  354.  
  355. $plugin_url = MS_Plugin::instance()->url; 
  356.  
  357. wp_enqueue_style( 
  358. 'ms-addon-taxamo',  
  359. $plugin_url . '/app/addon/taxamo/assets/css/taxamo.css' 
  360. ); 
  361.  
  362. wp_enqueue_script( 
  363. 'ms-addon-taxamo',  
  364. $plugin_url . '/app/addon/taxamo/assets/js/taxamo.js',  
  365. array( 'jquery' ) 
  366. ); 
  367.  
  368. $view = MS_Factory::load( 'MS_Addon_Taxamo_Userprofile' ); 
  369. $view->data = apply_filters( 
  370. 'ms_addon_taxamo_editor_data',  
  371. array( 'invoice' => $invoice ),  
  372. $this 
  373. ); 
  374. self::$footer_html .= '<div id="ms-taxamo-wrapper">' . $view->to_html() . '</div>'; 
  375.  
  376. do_action( 'ms_addon_taxamo_enqueue_scripts', $this ); 
  377.  
  378. /** 
  379. * Outputs tax-editor code in the page footer. 
  380. * 
  381. * @since 1.0.0 
  382. */ 
  383. public function tax_editor_footer() { 
  384. echo self::$footer_html; 
  385. ?> 
  386. <script> 
  387. window.ms_taxamo = { 
  388. "ajax_url" : "<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>" 
  389. }; 
  390. </script> 
  391. <?php 
  392.  
  393. /** 
  394. * Handle Ajax action to update a user tax-profile field. 
  395. * 
  396. * @since 1.0.0 
  397. */ 
  398. public function ajax_save_userprofile() { 
  399. $response = ''; 
  400.  
  401. $isset = array( 
  402. 'country_choice',  
  403. 'declared_country',  
  404. 'vat_number',  
  405. ); 
  406.  
  407. if ( $this->verify_nonce() 
  408. && self::validate_required( $isset, 'POST', false ) 
  409. ) { 
  410. $invoice_id = intval( $_POST['invoice_id'] ); 
  411. $invoice = MS_Factory::load( 'MS_Model_Invoice', $invoice_id ); 
  412. $data = $_POST; 
  413. unset( $data['invoice_id'] ); 
  414. unset( $data['action'] ); 
  415. unset( $data['_wpnonce'] ); 
  416.  
  417. $data['declared_country'] = (object) array( 
  418. 'code' => $data['declared_country'],  
  419. ); 
  420.  
  421. foreach ( $data as $field => $value ) { 
  422. $value = apply_filters( 
  423. 'ms_addon_taxamo_userprofile_value',  
  424. $value,  
  425. $field,  
  426. $invoice_id,  
  427. $this 
  428. ); 
  429.  
  430. MS_Addon_Taxamo_Api::set_tax_profile( $field, $value ); 
  431.  
  432. // User profile updated. Now update the tax-rate in the invoice. 
  433. if ( $invoice->is_valid() ) { 
  434. $profile = MS_Addon_Taxamo_Api::get_tax_profile(); 
  435. $invoice->set_custom_data( 'tax_profile', $profile ); 
  436. $invoice->total_amount_changed(); 
  437. $invoice->save(); 
  438.  
  439. $view = MS_Factory::load( 'MS_Addon_Taxamo_Userprofile' ); 
  440. $view->data = apply_filters( 
  441. 'ms_addon_taxamo_editor_data',  
  442. array( 'invoice' => $invoice ),  
  443. $this 
  444. ); 
  445. $response = trim( $view->to_html() ); 
  446.  
  447. wp_die( $response ); 
  448.  
.