| 1 |
<?php |
| 2 |
/** |
| 3 |
* Grandfather License Client |
| 4 |
* |
| 5 |
* HTTP client wrapper around `POST /v1/license/grandfather/avada` on |
| 6 |
* api.forge12.com. Used by {@see AvadaDeprecationNotice} when a site |
| 7 |
* admin clicks "Claim free grandfather license" on the migration notice. |
| 8 |
* |
| 9 |
* Intentionally thin — no retries, no caching beyond the immediate result. |
| 10 |
* Callers persist the returned license key; repeated calls for the same |
| 11 |
* site return `already_claimed` from the server and are safe. |
| 12 |
* |
| 13 |
* @package Forge12\DoubleOptIn\Migration |
| 14 |
* @since 4.99.0 |
| 15 |
*/ |
| 16 |
|
| 17 |
namespace Forge12\DoubleOptIn\Migration; |
| 18 |
|
| 19 |
if ( ! defined( 'ABSPATH' ) ) { |
| 20 |
exit; |
| 21 |
} |
| 22 |
|
| 23 |
final class GrandfatherLicenseClient { |
| 24 |
|
| 25 |
private const ENDPOINT_URL = 'https://api.forge12.com/v1/license/grandfather/avada'; |
| 26 |
private const TIMEOUT = 15; |
| 27 |
|
| 28 |
/** |
| 29 |
* Attempt to claim a grandfather license for this site. |
| 30 |
* |
| 31 |
* @param string $siteUrl Canonical site URL (`home_url()`). |
| 32 |
* @param string $adminEmail Site admin email for records. |
| 33 |
* @param array<string, mixed> $attestation Self-reported attestation block. |
| 34 |
* @return array{ |
| 35 |
* success: bool, |
| 36 |
* status: string, |
| 37 |
* licenseKey: string, |
| 38 |
* message: string |
| 39 |
* } |
| 40 |
*/ |
| 41 |
public function claimAvada( string $siteUrl, string $adminEmail, array $attestation ): array { |
| 42 |
$response = wp_remote_post( |
| 43 |
self::ENDPOINT_URL, |
| 44 |
array( |
| 45 |
'timeout' => self::TIMEOUT, |
| 46 |
'headers' => array( 'Content-Type' => 'application/json' ), |
| 47 |
'body' => wp_json_encode( |
| 48 |
array( |
| 49 |
'siteUrl' => $siteUrl, |
| 50 |
'adminEmail' => $adminEmail, |
| 51 |
'attestation' => $attestation, |
| 52 |
) |
| 53 |
), |
| 54 |
) |
| 55 |
); |
| 56 |
|
| 57 |
if ( is_wp_error( $response ) ) { |
| 58 |
return array( |
| 59 |
'success' => false, |
| 60 |
'status' => 'network_error', |
| 61 |
'licenseKey' => '', |
| 62 |
'message' => $response->get_error_message(), |
| 63 |
); |
| 64 |
} |
| 65 |
|
| 66 |
$statusCode = wp_remote_retrieve_response_code( $response ); |
| 67 |
$body = json_decode( wp_remote_retrieve_body( $response ), true ); |
| 68 |
|
| 69 |
if ( ! is_array( $body ) ) { |
| 70 |
return array( |
| 71 |
'success' => false, |
| 72 |
'status' => 'bad_response', |
| 73 |
'licenseKey' => '', |
| 74 |
'message' => __( 'Server returned an unreadable response.', 'double-opt-in' ), |
| 75 |
); |
| 76 |
} |
| 77 |
|
| 78 |
// Cutoff expired → HTTP 410 with { status: 'cutoff_expired', message } |
| 79 |
if ( $statusCode === 410 ) { |
| 80 |
return array( |
| 81 |
'success' => false, |
| 82 |
'status' => $body['status'] ?? 'cutoff_expired', |
| 83 |
'licenseKey' => '', |
| 84 |
'message' => $body['message'] ?? __( 'The claim window has closed.', 'double-opt-in' ), |
| 85 |
); |
| 86 |
} |
| 87 |
|
| 88 |
// Already claimed → HTTP 200 with { issued: false, status: 'already_claimed' } |
| 89 |
if ( isset( $body['status'] ) && $body['status'] === 'already_claimed' ) { |
| 90 |
return array( |
| 91 |
'success' => false, |
| 92 |
'status' => 'already_claimed', |
| 93 |
'licenseKey' => '', |
| 94 |
'message' => $body['message'] ?? __( 'A grandfather license was already issued for this site.', 'double-opt-in' ), |
| 95 |
); |
| 96 |
} |
| 97 |
|
| 98 |
// Happy path → { issued: true, licenseKey, licenseType, validUntil } |
| 99 |
if ( ! empty( $body['issued'] ) && ! empty( $body['licenseKey'] ) ) { |
| 100 |
return array( |
| 101 |
'success' => true, |
| 102 |
'status' => 'issued', |
| 103 |
'licenseKey' => (string) $body['licenseKey'], |
| 104 |
'message' => sprintf( |
| 105 |
/* translators: %s: ISO date */ |
| 106 |
__( 'Grandfather license issued, valid until %s.', 'double-opt-in' ), |
| 107 |
$body['validUntil'] ?? '2099-12-31' |
| 108 |
), |
| 109 |
); |
| 110 |
} |
| 111 |
|
| 112 |
// Generic server error (500-ish) with our error shape. |
| 113 |
return array( |
| 114 |
'success' => false, |
| 115 |
'status' => (string) ( $body['errorCode'] ?? 'unknown_error' ), |
| 116 |
'licenseKey' => '', |
| 117 |
'message' => (string) ( $body['message'] ?? __( 'Unknown server error.', 'double-opt-in' ) ), |
| 118 |
); |
| 119 |
} |
| 120 |
} |
| 121 |
|