Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
HiiveProductVerifier
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
156
0.00% covered (danger)
0.00%
0 / 1
 verify_product_access
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
156
1<?php
2
3namespace BLU\Validation;
4
5use NewfoldLabs\WP\Module\Data\HiiveConnection;
6use stdClass;
7use WP_Error;
8
9/**
10 * Class HiiveProductVerifier
11 * Handles product verification via Hiive API.
12 *
13 * @package BLU\Validation
14 */
15class HiiveProductVerifier {
16    /**
17     * Hiive product verification endpoint.
18     *
19     * @var string
20     */
21    private const NFD_BLU_JWT_HIIVE_VERIFY_ENDPOINT = 'sites/v2/customer/products/verify';
22    /**
23     * Cache key for storing verified tokens.
24     *
25     * @var string
26     */
27    private const NFD_BLU_JWT_VERIFIED_TOKEN_CACHE_KEY = 'blu_jwt_product_verify';
28    /**
29     * Cache TTL for verified tokens in seconds.
30     *
31     * @var int
32     */
33    private const NFD_BLU_JWT_VERIFIED_TOKEN_CACHE_TTL = 600; // 10 minutes
34
35    /**
36     * Verifies product access for a user using a token.
37     *
38     * @param string   $token   The token to verify.
39     * @param string   $user_id  The user ID associated with the token.
40     * @param stdClass $decoded The decoded token payload.
41     *
42     * @return bool Returns true if verification is successful, or throws an exception if verification fails.
43     *
44     * @throws \Exception If verification fails or an error occurs.
45     */
46    public static function verify_product_access( string $token, string $user_id, stdClass $decoded ): bool {
47        // Return cached result when the same token was already verified (avoids hitting Hiive on every request).
48        $cached = get_transient( self::NFD_BLU_JWT_VERIFIED_TOKEN_CACHE_KEY . "_$user_id" );
49        if ( false !== $cached ) {
50            if ( isset( $cached['token'] ) && $cached['token'] === $token ) {
51                if ( isset( $cached['status'] ) && ( 'true' === $cached['status'] || true === $cached['status'] ) ) {
52                    return true;
53                } else {
54                    throw new \Exception( 'Product validation failed. The product access is invalid.' );
55                }
56            }
57        }
58
59        // HiiveConnection uses the appropriate Hiive environment (staging vs production) per site config.
60        $connection = new HiiveConnection();
61        $response   = $connection->hiive_request(
62            self::NFD_BLU_JWT_HIIVE_VERIFY_ENDPOINT,
63            array(
64                'userId' => $user_id,
65            )
66        );
67
68        if ( is_wp_error( $response ) ) {
69            throw new \Exception( 'Failed to verify product access: ' . esc_html( $response->get_error_message() ) );
70        }
71
72        $status_code = wp_remote_retrieve_response_code( $response );
73
74        if ( ! in_array( $status_code, array( 200, 201 ), true ) ) {
75            throw new \Exception( 'Failed to verify product access: ' . esc_html( wp_remote_retrieve_response_message( $response ) ) );
76        }
77
78        $data = json_decode( wp_remote_retrieve_body( $response ), true );
79
80        if ( isset( $data['response'] ) ) {
81            $value = array(
82                'token'  => $token,
83                'status' => $data['response'],
84            );
85            // Cache verification result so we don't call Hiive on every MCP request.
86            set_transient( self::NFD_BLU_JWT_VERIFIED_TOKEN_CACHE_KEY . "_$user_id", $value, apply_filters( 'blu_jwt_product_verify_cache_ttl', self::NFD_BLU_JWT_VERIFIED_TOKEN_CACHE_TTL ) );
87
88            if ( 'true' === $data['response'] || true === $data['response'] ) {
89                return true;
90            } else {
91                throw new \Exception( 'Product verification failed. The product access is invalid.' );
92            }
93        } else {
94            throw new \Exception( 'Product verification failed. No response from Hiive.' );
95        }
96    }
97}