PluginProbe
Route ‑ Shipping Protection / 2.4.2
Route ‑ Shipping Protection v2.4.2
2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.2.4 2.2.5 2.2.6 2.2.8 2.2.9 2.3.0 2.3.1 2.3.2 2.3.3 All 151 releases
routeapp / includes / class-routeapp-setup.php

class-routeapp-setup.php in Route ‑ Shipping Protection 2.4.2, at includes/class-routeapp-setup.php

950 lines 32.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * A Route Magento Extension that adds secure shipping
4 * insurance to your orders
5 *
6 * Php version 7.0^
7 *
8 * @category
9 * @package Route_Route
10 * @author Route Development Team <dev@routeapp.io>
11 * @copyright 2019 Route App Inc. Copyright (c) https://www.routeapp.io/
12 * @license https://www.routeapp.io/merchant-terms-of-use Proprietary License
13 * @link https://magento.routeapp.io/magento2/index.html
14 */
15
16
17 class Route_Setup
18 {
19
20 const WOOCOMMERCE = "woocommerce";
21
22 const REGISTRATION_FIXED = '0';
23 const REGISTRATION_STEP_USER_LOGIN_SUCCESS = '1-200';
24 const FAILED_REGISTRATION_STEP_USER = "1";
25 const FAILED_REGISTRATION_STEP_USER_DUPLICATED = '1-409';
26 const FAILED_REGISTRATION_STEP_USER_LOGIN_FAILED = '1-401';
27 const FAILED_REGISTRATION_STEP_USER_ACTIVATION = '2';
28 const FAILED_REGISTRATION_STEP_MERCHANT = '3';
29 const FAILED_REGISTRATION_STEP_MERCHANT_DUPLICATED = '3-409';
30
31 const FAILED_REGISTRATION = 'routeapp_failed_registration';
32 const USER_TOKEN_OPTION = 'routeapp_user_token';
33 const USER_ID_OPTION = 'routeapp_user_id';
34 const SECRET_TOKEN_OPTION = 'routeapp_secret_token';
35 const PUBLIC_TOKEN_OPTION = 'routeapp_public_token';
36 const USER_ID = 'routeapp_user_id';
37 const REGISTRATION_STEP = 'routeapp_route_registration_step';
38 const DASHBOARD = 'https://dashboard.route.com/login?redirect=onboarding';
39 const DASHBOARD_STAGE = 'https://dashboard-stage.route.com/login?redirect=onboarding';
40 const REDIRECTED = '2';
41 const MODULE_INSTALLED = '1';
42 /**
43 * After duplicate-user / login-conflict notice auto-redirect runs once, show manual link only (avoids redirect loops).
44 */
45 const USER_CONFLICT_AUTO_REDIRECT_DONE_OPTION = 'routeapp_user_conflict_auto_redirect_done';
46 const ACTIVATION_LINK = 'activation_link';
47 const SETUP_CHECK_WIDGET_INSTALLATION_DATE = 'route_setup_check_widget_installation_date';
48 const SETUP_CHECK_WIDGET_API_CALL = 'route_setup_check_widget_api_call';
49 const SETUP_CHECK_WIDGET_PHP = 'route_setup_check_widget_php';
50 const SETUP_CHECK_WIDGET_JS = 'route_setup_check_widget_js';
51 const ACTIVE = 'Active';
52
53 /**
54 * Stable per-site identifier for ownership / OTP callbacks (used in the ownership-resolution URL path).
55 *
56 * @var string
57 */
58 const STORE_HASH_OPTION = 'routeapp_store_hash';
59
60 /**
61 * Short alphanumeric id length (10–12) for /app/ownership-resolution/{hash}.
62 */
63 const STORE_HASH_LENGTH = 12;
64
65 /**
66 * Register rewrite, query vars, and front controller for /app/ownership-resolution/{storeHash}.
67 */
68 public static function init_ownership_routing() {
69 add_action( 'init', array( __CLASS__, 'register_ownership_rewrite_rule' ), 5 );
70 add_filter( 'query_vars', array( __CLASS__, 'ownership_query_vars' ) );
71 add_action( 'template_redirect', array( __CLASS__, 'template_redirect_ownership_resolution' ), 1 );
72 }
73
74 /**
75 * @param array $vars Query vars.
76 * @return array
77 */
78 public static function ownership_query_vars( $vars ) {
79 $vars[] = 'routeapp_ownership';
80 $vars[] = 'routeapp_store_hash';
81 return $vars;
82 }
83
84 /**
85 * Pretty URL: {site}/app/ownership-resolution/{storeHash} on the store origin.
86 */
87 public static function register_ownership_rewrite_rule() {
88 add_rewrite_rule(
89 '^app/ownership-resolution/([a-zA-Z0-9]{10,12})/?$',
90 'index.php?routeapp_ownership=1&routeapp_store_hash=$matches[1]',
91 'top'
92 );
93 }
94
95 /**
96 * Handle GET /app/ownership-resolution/{storeHash}?token= / ?oneTimeToken=
97 */
98 public static function template_redirect_ownership_resolution() {
99 if ( ! (int) get_query_var( 'routeapp_ownership' ) ) {
100 return;
101 }
102 $path_hash = get_query_var( 'routeapp_store_hash' );
103 $token = '';
104 if ( isset( $_GET['oneTimeToken'] ) ) {
105 $token = sanitize_text_field( wp_unslash( $_GET['oneTimeToken'] ) );
106 }
107 if ( $token === '' && isset( $_GET['token'] ) ) {
108 $token = sanitize_text_field( wp_unslash( $_GET['token'] ) );
109 }
110 self::ownership_resolution_handle( $path_hash, $token );
111 }
112
113 public static function init()
114 {
115 self::ensure_store_hash();
116
117 if ( ! self::get_route_public_instance() ) {
118 return;
119 }
120
121 if ( ! self::is_fresh_new_installation() ) {
122 self::update_merchant_status();
123 return;
124 }
125
126 self::create_user();
127 }
128
129 /**
130 * True when activation should run create_user() / merchant onboarding against the Route API.
131 *
132 * Gated only on missing valid merchant credentials. Requiring an empty routeapp_route_registration_step
133 * used to block re-onboarding after tokens were cleared while the step option stayed set.
134 *
135 * @return bool
136 */
137 public static function is_fresh_new_installation()
138 {
139 return ! self::has_valid_merchant_tokens();
140 }
141
142 /**
143 * Public/secret missing but we have prior onboarding state — user should use Dashboard login + OTP or POST user_login.
144 *
145 * @return bool
146 */
147 public static function should_prompt_merchant_token_recovery()
148 {
149 if ( self::has_merchant_tokens() ) {
150 return false;
151 }
152 return ! empty( get_option( self::USER_TOKEN_OPTION ) )
153 || ! empty( self::registration_step() )
154 || ! empty( get_option( 'routeapp_merchant_id' ) );
155 }
156
157 /**
158 * Create and persist a unique store hash for this WordPress installation (used in ownership-resolution URL).
159 *
160 * @return string
161 */
162 /**
163 * Whether the stored hash matches the short format (10–12 alnum).
164 *
165 * @param string $hash Hash.
166 * @return bool
167 */
168 private static function is_valid_store_hash_format( $hash ) {
169 return is_string( $hash ) && (bool) preg_match( '/^[a-zA-Z0-9]{10,12}$/', $hash );
170 }
171
172 public static function ensure_store_hash() {
173 $hash = get_option( self::STORE_HASH_OPTION );
174 if ( self::is_valid_store_hash_format( $hash ) ) {
175 return $hash;
176 }
177 if ( function_exists( 'wp_generate_password' ) ) {
178 $hash = wp_generate_password( self::STORE_HASH_LENGTH, false, false );
179 } else {
180 $hash = substr( bin2hex( random_bytes( 8 ) ), 0, self::STORE_HASH_LENGTH );
181 }
182 update_option( self::STORE_HASH_OPTION, $hash, false );
183 return $hash;
184 }
185
186 /**
187 * Stored store hash (must exist before building platformUrl for Route Dashboard).
188 *
189 * @return string
190 */
191 public static function get_store_hash() {
192 return (string) get_option( self::STORE_HASH_OPTION, '' );
193 }
194
195 /**
196 * Callback URL for Route Dashboard platformUrl: {store-web-url}/app/ownership-resolution/{storeHash}
197 * (avoids wp-json / rest_route encoding on the merchant origin).
198 *
199 * @return string
200 */
201 public static function get_ownership_resolution_platform_url() {
202 $store_hash = self::ensure_store_hash();
203 return home_url( '/app/ownership-resolution/' . $store_hash );
204 }
205
206 /**
207 * Prepare user data, call user create request
208 * and handle response
209 *
210 * @return bool
211 */
212 public static function create_user()
213 {
214 $routeapp_public = self::get_route_public_instance();
215 $response = $routeapp_public->routeapp_create_user(self::get_user_data());
216
217 try{
218 if ($routeapp_public->routeapp_last_request_has_failed()) {
219 self::set_registration_failed_as(
220 $routeapp_public->routeapp_last_request_has_conflicted() ?
221 self::FAILED_REGISTRATION_STEP_USER_DUPLICATED:
222 self::FAILED_REGISTRATION_STEP_USER
223 );
224 throw new Exception($routeapp_public->routeapp_last_request_has_conflicted() ?
225 'FAILED_REGISTRATION_STEP_USER_DUPLICATED':
226 'FAILED_REGISTRATION_STEP_USER');
227 }
228 }catch (Exception $exception) {
229 $extraData = array(
230 'params' => self::get_user_data(),
231 'method' => 'POST',
232 'endpoint' => 'users'
233 );
234 $routeapp_public->routeapp_log($exception, $extraData);
235 return false;
236 }
237
238 if (self::register_user($response)) {
239 self::set_registration_failed_as(self::REGISTRATION_FIXED);
240 self::clear_user_conflict_auto_redirect_flag();
241 self::set_as_installed();
242
243 return true;
244 }
245 }
246
247 /**
248 * Prepare user data, call user create request
249 * and handle response
250 *
251 * @return bool
252 */
253 public static function register_user_login($username, $password)
254 {
255 $routeapp_public = self::get_route_public_instance();
256 $response = $routeapp_public->routeapp_user_login($username, $password);
257
258 try{
259 if ($routeapp_public->routeapp_last_request_has_failed()) {
260 self::set_registration_failed_as(self::FAILED_REGISTRATION_STEP_USER_LOGIN_FAILED);
261 throw new Exception('FAILED_REGISTRATION_STEP_USER_LOGIN_FAILED');
262 }
263 }catch (Exception $exception) {
264 $extraData = array(
265 'params' => array('username' => $username, 'password' => 'XXXXX'),
266 'method' => 'POST',
267 'endpoint' => 'login'
268 );
269 $routeapp_public->routeapp_log($exception, $extraData);
270 return false;
271 }
272
273 if (self::register_user($response, true)) {
274 self::set_registration_failed_as(self::REGISTRATION_STEP_USER_LOGIN_SUCCESS);
275 self::clear_user_conflict_auto_redirect_flag();
276
277 self::set_as_installed();
278
279 return true;
280 }
281 }
282
283 /**
284 * @param $response
285 * @param $is_active
286 *
287 * @return bool
288 */
289 public static function register_user($response, $is_active = false)
290 {
291
292 self::set_user_key($response->token);
293 self::set_user_id($response->id);
294
295 if (!$is_active) {
296 self::active_account();
297 }
298
299 return self::create_merchant();
300 }
301
302 /**
303 * Activate User Account
304 *
305 * @return bool
306 */
307 public static function active_account()
308 {
309 $routeapp_public = self::get_route_public_instance();
310 try{
311 $response = $routeapp_public->routeapp_activate_account(self::get_current_email());
312
313 try{
314 if(!$response){
315 self::set_registration_failed_as(self::FAILED_REGISTRATION_STEP_USER_ACTIVATION);
316 throw new Exception('FAILED_REGISTRATION_STEP_USER_ACTIVATION');
317 }
318 } catch (Exception $exception) {
319 $extraData = array(
320 'params' => self::get_current_email(),
321 'method' => 'POST',
322 'endpoint' => 'activate_account'
323 );
324 $routeapp_public->routeapp_log($exception, $extraData);
325 return false;
326 }
327 self::set_activation_link($response->set_password_url);
328 }catch (Exception $e){
329 self::set_registration_failed_as(self::FAILED_REGISTRATION_STEP_USER_ACTIVATION);
330 $extraData = array(
331 'params' => self::get_current_email(),
332 'method' => 'POST',
333 'endpoint' => 'activate_account'
334 );
335 $routeapp_public->routeapp_log($e, $extraData);
336 return false;
337 }
338
339 self::set_registration_failed_as(self::REGISTRATION_FIXED);
340
341 return true;
342
343 }
344
345 public static function has_valid_merchant_tokens(){
346 $routeapp_public = self::get_route_public_instance();
347 if ( ! $routeapp_public ) {
348 return false;
349 }
350 return self::has_merchant_tokens() &&
351 ! empty( $routeapp_public->routeapp_api_client->get_merchant() );
352 }
353
354 public static function has_merchant_tokens(){
355 $routeapp_public = self::get_route_public_instance();
356 if ( ! $routeapp_public ) {
357 return false;
358 }
359 return
360 ! empty( $routeapp_public->routeapp_get_public_token() ) &&
361 ! empty( $routeapp_public->routeapp_get_secret_token() );
362 }
363
364 public static function get_route_public_instance(){
365 global $routeapp_public;
366 return $routeapp_public;
367 }
368
369 /**
370 * @param $store_domain
371 * @param $merchant_store_domain
372 * @return bool
373 */
374 public static function is_same_domain($store_domain, $merchant_store_domain)
375 {
376 if(strpos($store_domain,'www.') === 0 || strpos($merchant_store_domain,'www.') === 0){
377 return strpos($store_domain, $merchant_store_domain) !== false || strpos($merchant_store_domain, $store_domain) !== false;
378 }
379
380 return $merchant_store_domain === $store_domain;
381 }
382
383 /**
384 * Prepare merchant data, call merchant create request
385 * and handle response
386 *
387 * @return bool
388 */
389 public static function create_merchant()
390 {
391 $created_domains = [];
392 $responses = [];
393
394 foreach (self::get_all_sites() as $site){
395
396 //Avoid duplicated merchant to the same user account
397 if (in_array($site->domain, $created_domains)) {
398 continue;
399 }
400
401 $associated = false;
402 $responses = [];
403
404 $created_domains[$site->blog_id] = $site->domain;
405 $merchant_data = self::get_merchant_data($site);
406 $routeapp_public = self::get_route_public_instance();
407 $merchant_creation_response = $routeapp_public->routeapp_create_merchant($merchant_data);
408
409 if($merchant_creation_response){
410 $responses[$site->blog_id] = $merchant_creation_response;
411 }
412
413 try{
414 if ($routeapp_public->routeapp_last_request_has_failed()) {
415
416
417 $merchants_by_user = $routeapp_public->routeapp_get_merchants();
418
419 if(!empty($merchants_by_user) && is_array($merchants_by_user)){
420 foreach ($merchants_by_user as $merchant) {
421 if(self::is_same_domain($merchant->store_domain, $site->domain)){
422 $responses[$site->blog_id] = $merchant;
423 $associated = true;
424 }
425 }
426 }
427
428 if (empty($responses)) {
429 self::set_registration_failed_as(
430 $routeapp_public->routeapp_last_request_has_conflicted() ?
431 self::FAILED_REGISTRATION_STEP_MERCHANT_DUPLICATED:
432 self::FAILED_REGISTRATION_STEP_MERCHANT
433 );
434 throw new Exception($routeapp_public->routeapp_last_request_has_conflicted() ?
435 'FAILED_REGISTRATION_STEP_MERCHANT_DUPLICATED':
436 'FAILED_REGISTRATION_STEP_MERCHANT');
437 }
438 }
439 } catch (Exception $exception) {
440 $routeapp_public->routeapp_log($exception);
441 return false;
442 }
443
444
445
446 //TODO Currently when we try to create merchant that already exists it's returning 200 http code success
447 //TODO So we need try to update it with current platform
448 if (!$associated && $routeapp_public->routeapp_last_request_has_success()) {
449
450 try{
451 if (!self::merchant_can_be_updated($responses[$site->blog_id])) {
452 self::set_registration_failed_as(self::FAILED_REGISTRATION_STEP_MERCHANT);
453 throw new Exception('FAILED_REGISTRATION_STEP_MERCHANT');
454 }
455 } catch (Exception $exception) {
456 $routeapp_public->routeapp_log($exception);
457 return false;
458 }
459
460 //TODO This is happening when we try to update merchant that user doesn't own
461 //TODO So we need try to recreate it with additional slash at the store's domain end
462 $merchant_data['store_domain'] = $merchant_data['store_domain'] . '/';
463 $responses[$site->blog_id] = $routeapp_public->routeapp_create_merchant($merchant_data);
464
465 try{
466 if ($routeapp_public->routeapp_last_request_has_failed()) {
467 self::set_registration_failed_as(
468 $routeapp_public->routeapp_last_request_has_conflicted() ?
469 self::FAILED_REGISTRATION_STEP_MERCHANT_DUPLICATED :
470 self::FAILED_REGISTRATION_STEP_MERCHANT);
471 throw new Exception($routeapp_public->routeapp_last_request_has_conflicted() ?
472 'FAILED_REGISTRATION_STEP_MERCHANT_DUPLICATED' :
473 'FAILED_REGISTRATION_STEP_MERCHANT');
474 }
475 } catch (Exception $exception) {
476 $routeapp_public->routeapp_log($exception);
477 return false;
478 }
479 }
480
481 }
482
483 foreach ($responses as $blog_id => $response){
484 self::set_public_key($response->public_api_key, $blog_id);
485 self::set_secret_key($response->prod_api_secret, $blog_id);
486 }
487
488 return true;
489 }
490
491 /**
492 * @param $response
493 * @return bool
494 */
495 public static function merchant_can_be_updated($response)
496 {
497 return (isset($response->platform_id) && strtolower($response->platform_id) == 'email') ||
498 (isset($response->status) && strtolower($response->status) != 'active');
499 }
500
501 /**
502 * @param $data
503 * @return int Total of orders in last month
504 */
505 public static function get_calculate_dealsize()
506 {
507 $created_at_min = date( 'Y-m-d', strtotime( '-1 month' ) );
508
509 $ordersCollection = wc_get_orders(
510 array(
511 'limit' => -1,
512 'date_after' => $created_at_min,
513 'return' => 'ids',
514 )
515 );
516
517 return strval( count( $ordersCollection ) );
518 }
519
520 /**
521 * @param $data
522 * @return array
523 */
524 private static function get_user_data()
525 {
526 $userData = [];
527 $user = self::get_current_user();
528 $userData['name'] = $user->user_firstname;
529 $userData['password'] = self::generate_temp_pass();
530 $userData['platform_id'] = self::WOOCOMMERCE;
531 $userData['phone'] = '';
532 $userData['primary_email'] = self::get_current_email();
533 return $userData;
534 }
535
536 /**
537 * @return string
538 */
539 private static function get_current_email()
540 {
541 return get_bloginfo('admin_email');
542 }
543
544 /**
545 * @param $data
546 * @return array
547 */
548 private static function get_merchant_data($site)
549 {
550 $merchantData = [];
551 $merchantData['platform_id'] = self::WOOCOMMERCE;
552 $merchantData['store_domain'] = $site->domain;
553 $merchantData['store_name'] = self::get_blog_name($site);
554 $merchantData['deal_size_order_count'] = self::get_calculate_dealsize();
555 $merchantData['country'] = self::get_store_country($site);
556 $merchantData['currency'] = self::get_currency($site);
557 $merchantData['source'] = self::WOOCOMMERCE;
558 $merchantData['status'] = self::ACTIVE;
559 return $merchantData;
560 }
561
562 /**
563 * Prepare Merchant Data
564 * @param $store
565 * @return mixed
566 */
567 public function prepare_compatibility_data()
568 {
569 $widgetPhpFlag = $this->get_setup_check_widget(self::SETUP_CHECK_WIDGET_PHP);
570 $widgetJsFlag = $this->get_setup_check_widget(self::SETUP_CHECK_WIDGET_JS);
571 $creationDate = $this->get_setup_check_widget(self::SETUP_CHECK_WIDGET_INSTALLATION_DATE);
572
573 $successInstallation = $widgetPhpFlag && $widgetJsFlag;
574 $data = [];
575 $data['php_flag'] = $widgetPhpFlag ? $widgetPhpFlag : 0;
576 $data['js_flag'] = $widgetJsFlag ? $widgetJsFlag : 0;
577 $data['success_installation'] = $successInstallation;
578 $data['install_date'] = $creationDate;
579 $data['store_domain'] = parse_url(get_site_url(get_current_blog_id()), PHP_URL_HOST);
580 $data['platform_id'] = self::WOOCOMMERCE;
581 // If get_plugins() isn't available, require it
582 if ( ! function_exists( 'get_plugins' ) )
583 require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
584 $data['modules'] = get_plugins();
585 $data['subject'] = $successInstallation ? 'Success Installation' : 'Installation Issues';
586 return $data;
587 }
588
589 public function can_send_compatibility_report(){
590 $apiCalled = $this->get_setup_check_widget(self::SETUP_CHECK_WIDGET_API_CALL);
591 $creationDate = $this->get_setup_check_widget(self::SETUP_CHECK_WIDGET_INSTALLATION_DATE);
592
593 return (time() > $creationDate && !$apiCalled);
594 }
595
596
597 /**
598 * @return array|
599 */
600 public static function get_all_sites()
601 {
602 if(is_multisite()) {
603 return get_sites();
604 }
605 $site = new stdClass();
606 $site->domain = parse_url(get_site_url(), PHP_URL_HOST);
607 $site->blog_id = 1;
608 return [$site];
609 }
610
611 /**
612 * @param $site
613 * @return mixed
614 */
615 private static function get_blog_name($site)
616 {
617 if (is_multisite())
618 return get_blog_option($site->blog_id, 'blogname');
619 return get_option('blogname');
620 }
621
622 /**
623 * @param $site
624 * @return mixed
625 */
626 private static function get_currency($site)
627 {
628 if(is_multisite())
629 return get_blog_option($site->blog_id, 'woocommerce_currency');
630 return get_woocommerce_currency();
631 }
632
633 /**
634 * @param $site
635 * @return mixed|string
636 */
637 private static function get_store_country($site){
638 if(is_multisite()) {
639 $country = get_blog_option($site->blog_id, 'woocommerce_default_country');
640 if (strpos($country, ':') > 0) {
641 $countryState = explode(':', $country);
642 return isset($countryState[0]) ? $countryState[0] : $country;
643 }
644 return $country;
645 }
646 return get_woocommerce_currency();
647 }
648
649 private static function generate_temp_pass()
650 {
651 return "pass" . substr(hash('sha256', rand()), 0, 10);
652 }
653
654 private static function set_registration_failed_as($step){
655 update_option(self::FAILED_REGISTRATION , $step);
656 }
657
658 /**
659 * Reset so the next duplicate-user / login-conflict flow can auto-redirect once again.
660 */
661 public static function clear_user_conflict_auto_redirect_flag() {
662 delete_option( self::USER_CONFLICT_AUTO_REDIRECT_DONE_OPTION );
663 }
664
665 private static function get_registration_failed_as(){
666 return get_option(self::FAILED_REGISTRATION);
667 }
668
669 private static function set_secret_key($token, $blog_id = null){
670 if (isset($blog_id) && is_multisite()) {
671 return update_blog_option($blog_id, self::SECRET_TOKEN_OPTION, $token);
672 }
673 return update_option(self::SECRET_TOKEN_OPTION , $token);
674 }
675
676 private static function set_user_key($token, $blog_id = null){
677 if (isset($blog_id) && is_multisite()) {
678 return update_blog_option($blog_id, self::USER_TOKEN_OPTION, $token);
679 }
680 return update_option(self::USER_TOKEN_OPTION , $token);
681 }
682
683 private static function set_user_id($userId, $blog_id = null){
684 if (isset($blog_id) && is_multisite()) {
685 return update_blog_option($blog_id, self::USER_ID_OPTION, $userId);
686 }
687 return update_option(self::USER_ID_OPTION , $userId);
688 }
689
690 private static function set_public_key($token, $blog_id = null){
691 if (isset($blog_id) && is_multisite()) {
692 return update_blog_option($blog_id, self::PUBLIC_TOKEN_OPTION, $token);
693 }
694 return update_option(self::PUBLIC_TOKEN_OPTION , $token);
695 }
696
697 private static function get_activation_link(){
698 return get_option(self::ACTIVATION_LINK);
699 }
700
701 private static function set_as_installed(){
702 update_option(self::REGISTRATION_STEP, self::MODULE_INSTALLED);
703 self::set_setup_check_widget(self::SETUP_CHECK_WIDGET_INSTALLATION_DATE, time());
704 }
705
706 private static function set_activation_link($activation_link){
707 update_option(self::ACTIVATION_LINK, $activation_link);
708 }
709
710 private static function registration_step(){
711 return get_option(self::REGISTRATION_STEP);
712 }
713
714 public static function is_installed(){
715 return self::registration_step() == self::MODULE_INSTALLED;
716 }
717
718 public static function has_user_login_succeed(){
719 return self::get_registration_failed_as() == self::REGISTRATION_STEP_USER_LOGIN_SUCCESS;
720 }
721
722 /**
723 * Set setup check widget
724 *
725 * @param $config
726 * @param bool $value
727 */
728 public static function set_setup_check_widget($config, $value = false)
729 {
730 if (!get_option($config)) {
731 $value = $value ? $value : 1;
732 update_option($config, $value);
733 }
734 }
735
736 /**
737 * Get setup check widget config
738 *
739 * @param $config
740 * @return mixed
741 */
742 public function get_setup_check_widget($config)
743 {
744 return get_option($config);
745 }
746
747 /**
748 * @return WP_User
749 */
750 private static function get_current_user()
751 {
752 return wp_get_current_user();
753 }
754
755
756 public static function retry_user(){
757 self::create_user();
758 wp_redirect( self_admin_url( "plugins.php" ) );
759 die();
760 }
761
762 public static function user_login() {
763 self::register_user_login($_POST['username'],$_POST['password']);
764 wp_redirect( self_admin_url( "plugins.php" ) );
765 die();
766 }
767
768 public static function retry_merchant(){
769 self::create_merchant();
770 wp_redirect( self_admin_url( "plugins.php" ) );
771 die();
772 }
773
774 public static function get_route_redirect(){
775 if(self::has_user_login_succeed()){
776 $custom_env = getenv('ROUTEAPP_ENVIRONMENT_ENDPOINT');
777 if (is_null($custom_env) || !$custom_env) {
778 $custom_env = isset($_SERVER['ROUTEAPP_ENVIRONMENT_ENDPOINT']) ? $_SERVER['ROUTEAPP_ENVIRONMENT_ENDPOINT'] : '';
779 }
780 if ($custom_env == 'stage') {
781 return self::DASHBOARD_STAGE;
782 }
783 return self::DASHBOARD ;
784 }
785 return self::get_activation_link();
786 }
787
788 private static function update_merchant_status(){
789 $routeapp_public = self::get_route_public_instance();
790 $merchant = $routeapp_public->routeapp_api_client->get_merchant();
791 if (empty($merchant)) {
792 return;
793 }
794
795 if (property_exists($merchant, 'status')) {
796 $routeapp_public->routeapp_api_client->update_merchant_status('Active');
797 }
798 }
799
800 /**
801 * Route Dashboard base URL (no path), respecting stage env like other helpers.
802 *
803 * @return string
804 */
805 public static function get_dashboard_root_url() {
806 $custom_env = getenv( 'ROUTEAPP_ENVIRONMENT_ENDPOINT' );
807 if ( is_null( $custom_env ) || ! $custom_env ) {
808 $custom_env = isset( $_SERVER['ROUTEAPP_ENVIRONMENT_ENDPOINT'] ) ? $_SERVER['ROUTEAPP_ENVIRONMENT_ENDPOINT'] : '';
809 }
810 return ( 'stage' === $custom_env ) ? 'https://dashboard-stage.route.com' : 'https://dashboard.route.com';
811 }
812
813 /**
814 * Route Dashboard login URL with platformUrl pointing at this site's OTP callback.
815 *
816 * @return string
817 */
818 public static function get_dashboard_ownership_login_url() {
819 $platform_url = self::get_ownership_resolution_platform_url();
820 return self::get_dashboard_root_url() . '/login?platformUrl=' . rawurlencode( $platform_url );
821 }
822
823 /**
824 * Dashboard login URL for Auth0 / post-activation flows (not legacy create-account).
825 *
826 * @return string
827 */
828 public static function get_dashboard_login_url() {
829 return self::get_dashboard_root_url() . '/login';
830 }
831
832 /**
833 * Whether the OTP merchant record matches this WordPress site's host.
834 *
835 * @param string $shop_domain Host from get_site_url().
836 * @param array $merchant Result from verify_otp.
837 * @return bool
838 */
839 public static function merchant_store_domain_matches_otp_merchant( $shop_domain, array $merchant ) {
840 if ( empty( $merchant['store_domain'] ) || ! is_string( $merchant['store_domain'] ) ) {
841 return false;
842 }
843 $m_domain = $merchant['store_domain'];
844 if ( 0 === strpos( $shop_domain, 'www.' ) ) {
845 return false !== strpos( $shop_domain, $m_domain ) || false !== strpos( $m_domain, $shop_domain );
846 }
847 return $m_domain === $shop_domain;
848 }
849
850 /**
851 * Persist merchant credentials after successful OTP and mark onboarding complete.
852 *
853 * @param array $merchant Result from Routeapp_API_Client::verify_otp.
854 */
855 public static function apply_ownership_verified_merchant( array $merchant ) {
856 $blog_id = is_multisite() ? get_current_blog_id() : null;
857 self::set_public_key( $merchant['public_api_key'], $blog_id );
858 self::set_secret_key( $merchant['prod_api_secret'], $blog_id );
859 $client = Routeapp_API_Client::getInstance();
860 $client->set_public_token( $merchant['public_api_key'] );
861 $client->set_secret_token( $merchant['prod_api_secret'] );
862 $client->set_merchant_id( $merchant['id'], $blog_id );
863 self::set_registration_failed_as( self::REGISTRATION_FIXED );
864 self::clear_user_conflict_auto_redirect_flag();
865 self::set_as_installed();
866
867 global $routeapp_public;
868 if ( $routeapp_public && isset( $routeapp_public->routeapp_api_client ) ) {
869 $routeapp_public->routeapp_api_client = new Routeapp_API_Client( $merchant['public_api_key'], $merchant['prod_api_secret'] );
870 $routeapp_public->routeapp_api_client->set_merchant_id( $merchant['id'], $blog_id );
871 $m = $routeapp_public->routeapp_api_client->get_merchant();
872 if ( $m && is_object( $m ) && property_exists( $m, 'status' ) ) {
873 $routeapp_public->routeapp_api_client->update_merchant_status( 'Active' );
874 }
875 }
876 }
877
878 /**
879 * Shared handler: path {storeHash} must match DB; verifies OTP and links merchant.
880 *
881 * @param string $path_hash Segment from URL.
882 * @param string $token OTP from ?token= or ?oneTimeToken=.
883 */
884 public static function ownership_resolution_handle( $path_hash, $token ) {
885 $expected = self::get_store_hash();
886 if ( $expected === '' ) {
887 self::ensure_store_hash();
888 $expected = self::get_store_hash();
889 }
890 if ( empty( $path_hash ) || $path_hash !== $expected ) {
891 wp_safe_redirect(
892 add_query_arg(
893 'route_ownership_error',
894 rawurlencode( 'invalid_store' ),
895 self::get_dashboard_login_url()
896 )
897 );
898 exit;
899 }
900
901 if ( empty( $token ) ) {
902 wp_safe_redirect(
903 add_query_arg(
904 'route_ownership_error',
905 rawurlencode( 'missing_token' ),
906 self::get_dashboard_login_url()
907 )
908 );
909 exit;
910 }
911
912 $merchant = Routeapp_API_Client::getInstance()->verify_otp( $token );
913 if ( is_wp_error( $merchant ) ) {
914 wp_safe_redirect(
915 add_query_arg(
916 'route_ownership_error',
917 rawurlencode( 'otp_verify_failed' ),
918 self::get_dashboard_login_url()
919 )
920 );
921 exit;
922 }
923
924 $shop_host = parse_url( get_site_url(), PHP_URL_HOST );
925 if ( ! self::merchant_store_domain_matches_otp_merchant( $shop_host, $merchant ) ) {
926 wp_safe_redirect(
927 add_query_arg(
928 'route_ownership_error',
929 rawurlencode( 'domain_mismatch' ),
930 self::get_dashboard_login_url()
931 )
932 );
933 exit;
934 }
935
936 self::apply_ownership_verified_merchant( $merchant );
937
938 if ( ! class_exists( 'Routeapp_Webhooks', false ) ) {
939 require_once dirname( dirname( __FILE__ ) ) . '/admin/class-routeapp-webhooks.php';
940 }
941 if ( class_exists( 'Routeapp_Webhooks' ) ) {
942 $webhooks = new Routeapp_Webhooks();
943 $webhooks->upsert_webhooks();
944 }
945
946 wp_safe_redirect( self::get_dashboard_root_url() );
947 exit;
948 }
949 }
950