/includes/class-tracking.php

https://github.com/seb86/AffiliateWP · PHP · 598 lines · 297 code · 152 blank · 149 comment · 63 complexity · f0ed4a3c0804443639bd764c97dbad6d MD5 · raw file

  1. <?php
  2. class Affiliate_WP_Tracking {
  3. private $referral_var;
  4. private $expiration_time;
  5. public $referral;
  6. /**
  7. * Get things started
  8. *
  9. * @since 1.0
  10. */
  11. public function __construct() {
  12. $this->set_expiration_time();
  13. $this->set_referral_var();
  14. /*
  15. * Referrals are tracked via javascript by default
  16. * This fails on sites that have jQuery errors, so a fallback method is available
  17. * With the fallback, the template_redirect action is used
  18. */
  19. if( ! $this->use_fallback_method() ) {
  20. add_action( 'wp_head', array( $this, 'header_scripts' ) );
  21. add_action( 'wp_enqueue_scripts', array( $this, 'load_scripts' ) );
  22. add_action( 'wp_ajax_nopriv_affwp_track_visit', array( $this, 'track_visit' ) );
  23. add_action( 'wp_ajax_affwp_track_visit', array( $this, 'track_visit' ) );
  24. add_action( 'wp_ajax_affwp_get_affiliate_id', array( $this, 'ajax_get_affiliate_id_from_login' ) );
  25. add_action( 'wp_ajax_nopriv_affwp_get_affiliate_id', array( $this, 'ajax_get_affiliate_id_from_login' ) );
  26. } else {
  27. add_action( 'template_redirect', array( $this, 'fallback_track_visit' ), -9999 );
  28. }
  29. add_action( 'init', array( $this, 'rewrites' ) );
  30. add_action( 'pre_get_posts', array( $this, 'unset_query_arg' ) );
  31. add_action( 'redirect_canonical', array( $this, 'prevent_canonical_redirect' ), 0, 2 );
  32. add_action( 'wp_ajax_nopriv_affwp_track_conversion', array( $this, 'track_conversion' ) );
  33. add_action( 'wp_ajax_affwp_track_conversion', array( $this, 'track_conversion' ) );
  34. }
  35. /**
  36. * Output header scripts
  37. *
  38. * @since 1.0
  39. */
  40. public function header_scripts() {
  41. ?>
  42. <script type="text/javascript">
  43. var AFFWP = AFFWP || {};
  44. AFFWP.referral_var = '<?php echo $this->get_referral_var(); ?>';
  45. AFFWP.expiration = <?php echo $this->get_expiration_time(); ?>;
  46. </script>
  47. <?php
  48. }
  49. /**
  50. * Output the conversion tracking script
  51. *
  52. * @since 1.0
  53. */
  54. public function conversion_script( $args = array() ) {
  55. $defaults = array(
  56. 'amount' => '',
  57. 'description' => '',
  58. 'context' => '',
  59. 'reference' => ''
  60. );
  61. $args = wp_parse_args( $args, $defaults );
  62. if( empty( $args['amount'] ) && ! empty( $_REQUEST['amount'] ) && 0 !== $args['amount'] ) {
  63. // Allow the amount to be passed via a query string or post request
  64. $args['amount'] = affwp_sanitize_amount( sanitize_text_field( urldecode( $_REQUEST['amount'] ) ) );
  65. }
  66. if( empty( $args['reference'] ) && ! empty( $_REQUEST['reference'] ) ) {
  67. // Allow the reference to be passed via a query string or post request
  68. $args['reference'] = sanitize_text_field( $_REQUEST['reference'] );
  69. }
  70. if( empty( $args['context'] ) && ! empty( $_REQUEST['context'] ) ) {
  71. $args['context'] = sanitize_text_field( $_REQUEST['context'] );
  72. }
  73. if( empty( $args['description'] ) && ! empty( $_REQUEST['description'] ) ) {
  74. $args['description'] = sanitize_text_field( $_REQUEST['description'] );
  75. }
  76. if( empty( $args['status'] ) && ! empty( $_REQUEST['status'] ) ) {
  77. $args['status'] = sanitize_text_field( $_REQUEST['status'] );
  78. }
  79. $md5 = md5( $args['amount'] . $args['description'] . $args['reference'] . $args['context'] . $args['status'] );
  80. ?>
  81. <script type="text/javascript">
  82. jQuery(document).ready(function($) {
  83. var ref = $.cookie( 'affwp_ref' );
  84. var visit = $.cookie( 'affwp_ref_visit_id' );
  85. // If a referral var is present and a referral cookie is not already set
  86. if( ref && visit ) {
  87. // Fire an ajax request to log the hit
  88. $.ajax({
  89. type: "POST",
  90. data: {
  91. action : 'affwp_track_conversion',
  92. affiliate : ref,
  93. amount : '<?php echo $args["amount"]; ?>',
  94. status : '<?php echo $args["status"]; ?>',
  95. description : '<?php echo $args["description"]; ?>',
  96. context : '<?php echo $args["context"]; ?>',
  97. reference : '<?php echo $args["reference"]; ?>',
  98. md5 : '<?php echo $md5; ?>'
  99. },
  100. url: affwp_scripts.ajaxurl,
  101. success: function (response) {
  102. if ( window.console && window.console.log ) {
  103. console.log( response );
  104. }
  105. }
  106. }).fail(function (response) {
  107. if ( window.console && window.console.log ) {
  108. console.log( response );
  109. }
  110. }).done(function (response) {
  111. });
  112. }
  113. });
  114. </script>
  115. <?php
  116. }
  117. /**
  118. * Load JS files
  119. *
  120. * @since 1.0
  121. */
  122. public function load_scripts() {
  123. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
  124. wp_enqueue_script( 'jquery-cookie', AFFILIATEWP_PLUGIN_URL . 'assets/js/jquery.cookie.js', array( 'jquery' ), '1.4.0' );
  125. wp_enqueue_script( 'affwp-tracking', AFFILIATEWP_PLUGIN_URL . 'assets/js/tracking' . $suffix . '.js', array( 'jquery-cookie' ), AFFILIATEWP_VERSION );
  126. wp_localize_script( 'jquery-cookie', 'affwp_scripts', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
  127. }
  128. /**
  129. * Registers the rewrite rules for pretty affiliate links
  130. *
  131. * @since 1.3
  132. */
  133. public function rewrites() {
  134. add_rewrite_endpoint( $this->get_referral_var(), EP_ALL );
  135. }
  136. /**
  137. * Removes our tracking query arg so as not to interfere with the WP query, see https://core.trac.wordpress.org/ticket/25143
  138. *
  139. * @since 1.3.1
  140. */
  141. public function unset_query_arg( $query ) {
  142. if ( is_admin() || ! $query->is_main_query() ) {
  143. return;
  144. }
  145. $key = affiliate_wp()->tracking->get_referral_var();
  146. $ref = $query->get( $key );
  147. if ( ! empty( $ref ) ) {
  148. $this->referral = $ref;
  149. // unset ref var from $wp_query
  150. $query->set( $key, null );
  151. global $wp;
  152. // unset ref var from $wp
  153. unset( $wp->query_vars[ $key ] );
  154. // if in home (because $wp->query_vars is empty) and 'show_on_front' is page
  155. if ( empty( $wp->query_vars ) && get_option( 'show_on_front' ) === 'page' ) {
  156. // reset and re-parse query vars
  157. $wp->query_vars['page_id'] = get_option( 'page_on_front' );
  158. $query->parse_query( $wp->query_vars );
  159. }
  160. }
  161. }
  162. /**
  163. * Filters on canonical redirects
  164. *
  165. * @since 1.4
  166. * @return string
  167. */
  168. public function prevent_canonical_redirect( $redirect_url, $requested_url ) {
  169. if( ! is_front_page() ) {
  170. return $redirect_url;
  171. }
  172. $key = affiliate_wp()->tracking->get_referral_var();
  173. $ref = get_query_var( $key );
  174. if( ! empty( $ref ) || false !== strpos( $requested_url, $key ) ) {
  175. $redirect_url = $requested_url;
  176. }
  177. return $redirect_url;
  178. }
  179. /**
  180. * Record referral visit via ajax
  181. *
  182. * @since 1.0
  183. */
  184. public function track_visit() {
  185. $affiliate_id = absint( $_POST['affiliate'] );
  186. if( $this->is_valid_affiliate( $affiliate_id ) ) {
  187. // Store the visit in the DB
  188. $visit_id = affiliate_wp()->visits->add( array(
  189. 'affiliate_id' => $affiliate_id,
  190. 'ip' => $this->get_ip(),
  191. 'url' => sanitize_text_field( $_POST['url'] ),
  192. 'referrer' => sanitize_text_field( $_POST['referrer'] )
  193. ) );
  194. echo $visit_id; exit;
  195. } else {
  196. die( '-2' );
  197. }
  198. }
  199. /**
  200. * Record referral conversion via ajax
  201. *
  202. * This is called anytime a referred visitor lands on a success page, defined by the [affiliate_conversion_script] short code
  203. *
  204. * @since 1.0
  205. */
  206. public function track_conversion() {
  207. $affiliate_id = absint( $_POST['affiliate'] );
  208. if( $this->is_valid_affiliate( $affiliate_id ) ) {
  209. $md5 = md5( $_POST['amount'] . $_POST['description'] . $_POST['reference'] . $_POST['context'] . $_POST['status'] );
  210. if( $md5 !== $_POST['md5'] ) {
  211. die( '-3' ); // The args were modified
  212. }
  213. if( affiliate_wp()->referrals->get_by( 'visit_id', $this->get_visit_id() ) ) {
  214. die( '-4' ); // This visit has already generated a referral
  215. }
  216. $status = ! empty( $_POST['status'] ) ? $_POST['status'] : 'unpaid';
  217. $amount = sanitize_text_field( urldecode( $_POST['amount'] ) );
  218. if( $amount > 0 ) {
  219. $amount = affwp_calc_referral_amount( $amount, $affiliate_id );
  220. }
  221. // Store the visit in the DB
  222. $referal_id = affiliate_wp()->referrals->add( array(
  223. 'affiliate_id' => $affiliate_id,
  224. 'amount' => $amount,
  225. 'status' => $status,
  226. 'description' => sanitize_text_field( $_POST['description'] ),
  227. 'context' => sanitize_text_field( $_POST['context'] ),
  228. 'reference' => sanitize_text_field( $_POST['reference'] ),
  229. 'visit_id' => $this->get_visit_id()
  230. ) );
  231. affiliate_wp()->visits->update( $this->get_visit_id(), array( 'referral_id' => $referal_id ), '', 'visit' );
  232. echo $referal_id; exit;
  233. } else {
  234. die( '-2' );
  235. }
  236. }
  237. /**
  238. * Record referral visit via template_redirect
  239. *
  240. * @since 1.0
  241. */
  242. public function fallback_track_visit() {
  243. $affiliate_id = $this->referral;
  244. if( empty( $affiliate_id ) ) {
  245. $affiliate_id = ! empty( $_GET[ $this->get_referral_var() ] ) ? $_GET[ $this->get_referral_var() ] : false;
  246. }
  247. if( empty( $affiliate_id ) ) {
  248. return;
  249. }
  250. if( ! is_numeric( $affiliate_id ) ) {
  251. $affiliate_id = $this->get_affiliate_id_from_login( $affiliate_id );
  252. }
  253. $affiliate_id = absint( $affiliate_id );
  254. if( $this->is_valid_affiliate( $affiliate_id ) && ! $this->get_visit_id() ) {
  255. $this->set_affiliate_id( $affiliate_id );
  256. // Store the visit in the DB
  257. $visit_id = affiliate_wp()->visits->add( array(
  258. 'affiliate_id' => $affiliate_id,
  259. 'ip' => $this->get_ip(),
  260. 'url' => $this->get_current_page_url(),
  261. 'referrer' => ! empty( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ''
  262. ) );
  263. $this->set_visit_id( $visit_id );
  264. }
  265. }
  266. /**
  267. * Get the referral variable
  268. *
  269. * @since 1.0
  270. */
  271. public function get_referral_var() {
  272. return $this->referral_var;
  273. }
  274. /**
  275. * Set the referral variable
  276. *
  277. * @since 1.0
  278. */
  279. public function set_referral_var() {
  280. $var = affiliate_wp()->settings->get( 'referral_var', 'ref' );
  281. $this->referral_var = apply_filters( 'affwp_referral_var', $var );
  282. }
  283. /**
  284. * Set the cookie expiration time
  285. *
  286. * @since 1.0
  287. */
  288. public function set_expiration_time() {
  289. // Default time is 1 day
  290. $days = affiliate_wp()->settings->get( 'cookie_exp', 1 );
  291. $this->expiration_time = apply_filters( 'affwp_cookie_expiration_time', $days );
  292. }
  293. /**
  294. * Get the cookie expiration time in days
  295. *
  296. * @since 1.0
  297. */
  298. public function get_expiration_time() {
  299. return $this->expiration_time;
  300. }
  301. /**
  302. * Determine if current visit was referred
  303. *
  304. * @since 1.0
  305. */
  306. public function was_referred() {
  307. $bool = isset( $_COOKIE['affwp_ref'] ) && $this->is_valid_affiliate( $_COOKIE['affwp_ref'] );
  308. return (bool) apply_filters( 'affwp_was_referred', $bool, $this );
  309. }
  310. /**
  311. * Get the visit ID
  312. *
  313. * @since 1.0
  314. */
  315. public function get_visit_id() {
  316. return ! empty( $_COOKIE['affwp_ref_visit_id'] ) ? absint( $_COOKIE['affwp_ref_visit_id'] ) : false;
  317. }
  318. /**
  319. * Set the visit ID
  320. *
  321. * @since 1.0
  322. */
  323. public function set_visit_id( $visit_id = 0 ) {
  324. setcookie( 'affwp_ref_visit_id', $visit_id, strtotime( '+' . $this->get_expiration_time() . ' days' ), COOKIEPATH, COOKIE_DOMAIN );
  325. }
  326. /**
  327. * Get the referring affiliate ID
  328. *
  329. * @since 1.0
  330. */
  331. public function get_affiliate_id() {
  332. $affiliate_id = ! empty( $_COOKIE['affwp_ref'] ) ? $_COOKIE['affwp_ref'] : false;
  333. if( ! empty( $cookie ) ) {
  334. $affiliate_id = absint( $affiliate_id );
  335. }
  336. return $affiliate_id;
  337. }
  338. /**
  339. * Get the affiliate's ID from their user login
  340. *
  341. * @since 1.3
  342. */
  343. public function get_affiliate_id_from_login( $login = '' ) {
  344. $affiliate_id = 0;
  345. if( ! empty( $login ) ) {
  346. $user = get_user_by( 'login', sanitize_text_field( $login ) );
  347. if( $user ) {
  348. $affiliate_id = affwp_get_affiliate_id( $user->ID );
  349. }
  350. }
  351. return $affiliate_id;
  352. }
  353. /**
  354. * Get the affiliate's ID from their user login
  355. *
  356. * @since 1.3
  357. */
  358. public function ajax_get_affiliate_id_from_login() {
  359. $success = 0;
  360. $affiliate_id = 0;
  361. if( ! empty( $_POST['affiliate'] ) ) {
  362. $affiliate_id = $this->get_affiliate_id_from_login( $_POST['affiliate'] );
  363. if( ! empty( $affiliate_id ) ) {
  364. $success = 1;
  365. }
  366. }
  367. $return = array(
  368. 'success' => $success,
  369. 'affiliate_id' => $affiliate_id
  370. );
  371. wp_send_json_success( $return );
  372. }
  373. /**
  374. * Set the referring affiliate ID
  375. *
  376. * @since 1.0
  377. */
  378. public function set_affiliate_id( $affiliate_id = 0 ) {
  379. setcookie( 'affwp_ref', $affiliate_id, strtotime( '+' . $this->get_expiration_time() . ' days' ), COOKIEPATH, COOKIE_DOMAIN );
  380. }
  381. /**
  382. * Check if it is a valid affiliate
  383. *
  384. * @since 1.0
  385. */
  386. public function is_valid_affiliate( $affiliate_id = 0 ) {
  387. if( empty( $affiliate_id ) ) {
  388. $affiliate_id = $this->get_affiliate_id();
  389. }
  390. $is_self = is_user_logged_in() && get_current_user_id() == affiliate_wp()->affiliates->get_column( 'user_id', $affiliate_id );
  391. $active = 'active' == affwp_get_affiliate_status( $affiliate_id );
  392. $valid = affiliate_wp()->affiliates->affiliate_exists( $affiliate_id );
  393. return ! empty( $valid ) && ! $is_self && $active;
  394. }
  395. /**
  396. * Get the visitor's IP address
  397. *
  398. * @since 1.0
  399. */
  400. public function get_ip() {
  401. if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
  402. //check ip from share internet
  403. $ip = $_SERVER['HTTP_CLIENT_IP'];
  404. } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
  405. //to check ip is pass from proxy
  406. $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  407. } else {
  408. $ip = $_SERVER['REMOTE_ADDR'];
  409. }
  410. return apply_filters( 'affwp_get_ip', $ip );
  411. }
  412. /**
  413. * Get the current page URL
  414. *
  415. * @since 1.0
  416. * @global $post
  417. * @return string $page_url Current page URL
  418. */
  419. function get_current_page_url() {
  420. global $post;
  421. if ( is_front_page() ) {
  422. $page_url = home_url();
  423. } else {
  424. $page_url = 'http';
  425. if ( isset( $_SERVER["HTTPS"] ) && $_SERVER["HTTPS"] == "on" ) {
  426. $page_url .= "s";
  427. }
  428. $page_url .= "://";
  429. if ( $_SERVER["SERVER_PORT"] != "80" ) {
  430. $page_url .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
  431. } else {
  432. $page_url .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
  433. }
  434. }
  435. return apply_filters( 'affwp_get_current_page_url', $page_url );
  436. }
  437. /**
  438. * Determine if we need to use the fallback tracking method
  439. *
  440. * @since 1.0
  441. */
  442. public function use_fallback_method() {
  443. $use_fallback = affiliate_wp()->settings->get( 'tracking_fallback', false );
  444. return apply_filters( 'affwp_use_fallback_tracking_method', $use_fallback );
  445. }
  446. }