161 lines
5.5 KiB
PHP
161 lines
5.5 KiB
PHP
<?php
|
||
// app/controllers/License.php
|
||
|
||
require_once __DIR__ . '/../helpers/cors.php';
|
||
require_once __DIR__ . '/../helpers/http.php';
|
||
require_once __DIR__ . '/../models/license_store.php';
|
||
|
||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||
$path = $_GET['_action'] ?? 'verify'; // e.g., routed as /license/verify
|
||
|
||
if ($method === 'OPTIONS') { cors_preflight(); exit; }
|
||
|
||
switch ($path) {
|
||
case 'verify':
|
||
handle_verify();
|
||
break;
|
||
|
||
// You likely already have activate/deactivate; leaving them out here.
|
||
default:
|
||
json_error(404, 'not_found');
|
||
}
|
||
|
||
function handle_verify() {
|
||
cors_allow();
|
||
|
||
$input = json_decode(file_get_contents('php://input'), true) ?: [];
|
||
$key = trim((string)($input['key'] ?? ''));
|
||
$acct = trim((string)($input['account_id'] ?? ''));
|
||
|
||
if ($key === '') { json_error(400, 'missing_key'); }
|
||
|
||
$mode = cfg('gateway_mode', 'sandbox');
|
||
|
||
// Sandbox: accept anything
|
||
if ($mode !== 'live') {
|
||
$norm = [
|
||
'key' => $key,
|
||
'source' => preg_match('/^G|^GMR|^GUM/i', $key) ? 'gumroad' : (preg_match('/^M|^MYR/i',$key) ? 'mayar' : 'sandbox'),
|
||
'plan' => 'pro', // treat as Pro
|
||
'product_id' => null,
|
||
'valid' => true,
|
||
'expires_at' => null,
|
||
'meta' => ['mode'=>'sandbox']
|
||
];
|
||
$row = license_upsert_from_verification($norm);
|
||
json_ok(['source'=>$norm['source'],'plan'=>$norm['plan'],'license'=>$row]);
|
||
}
|
||
|
||
// LIVE mode: try Gumroad first, then Mayar
|
||
$gum = verify_gumroad($key);
|
||
if ($gum['ok']) {
|
||
$row = license_upsert_from_verification($gum['norm']);
|
||
json_ok(['source'=>'gumroad','plan'=>$gum['norm']['plan'],'license'=>$row]);
|
||
}
|
||
|
||
$may = verify_mayar($key);
|
||
if ($may['ok']) {
|
||
$row = license_upsert_from_verification($may['norm']);
|
||
json_ok(['source'=>'mayar','plan'=>$may['norm']['plan'],'license'=>$row]);
|
||
}
|
||
|
||
json_error(401, 'invalid_license', ['details'=>['gumroad'=>$gum['err']??null,'mayar'=>$may['err']??null]]);
|
||
}
|
||
|
||
// --- Providers ---
|
||
|
||
function verify_gumroad($licenseKey) {
|
||
$cfg = cfg('gumroad', []);
|
||
$url = $cfg['verify_url'] ?? 'https://api.gumroad.com/v2/licenses/verify';
|
||
$pids = $cfg['product_ids'] ?? [];
|
||
|
||
// Gumroad expects POST form with license_key (+ product_id if you want to restrict)
|
||
$fields = ['license_key' => $licenseKey];
|
||
if (!empty($pids)) {
|
||
// If you have multiple products, you can loop; for simplicity, try them in order
|
||
foreach ($pids as $pid) {
|
||
$try = $fields + ['product_id' => $pid];
|
||
[$code,$body,$err] = http_post_form($url, $try);
|
||
if ($code >= 200 && $code < 300) {
|
||
$json = json_decode($body, true);
|
||
if (isset($json['success']) && $json['success'] === true) {
|
||
// Normalize
|
||
$p = $json['purchase'] ?? [];
|
||
$isRecurring = !empty($p['recurrence']);
|
||
$plan = $isRecurring ? 'subscription' : 'lifetime';
|
||
$expires = null; // Gumroad verify doesn’t usually return expiry for subs; optional to compute
|
||
return ['ok'=>true,'norm'=>[
|
||
'key' => $licenseKey,
|
||
'source' => 'gumroad',
|
||
'plan' => 'pro', // Your product => Pro features; if you want to store type, add meta
|
||
'product_id' => $pid,
|
||
'valid' => true,
|
||
'expires_at' => $expires,
|
||
'meta' => ['raw'=>$json,'plan_type'=>$plan]
|
||
]];
|
||
}
|
||
}
|
||
}
|
||
return ['ok'=>false,'err'=>'gumroad_no_match'];
|
||
} else {
|
||
// No product restriction
|
||
[$code,$body,$err] = http_post_form($url, $fields);
|
||
if ($code >= 200 && $code < 300) {
|
||
$json = json_decode($body, true);
|
||
if (isset($json['success']) && $json['success'] === true) {
|
||
$p = $json['purchase'] ?? [];
|
||
$isRecurring = !empty($p['recurrence']);
|
||
$plan = $isRecurring ? 'subscription' : 'lifetime';
|
||
return ['ok'=>true,'norm'=>[
|
||
'key' => $licenseKey,
|
||
'source' => 'gumroad',
|
||
'plan' => 'pro',
|
||
'product_id' => $p['product_id'] ?? null,
|
||
'valid' => true,
|
||
'expires_at' => null,
|
||
'meta' => ['raw'=>$json,'plan_type'=>$plan]
|
||
]];
|
||
}
|
||
}
|
||
return ['ok'=>false,'err'=>'gumroad_verify_failed'];
|
||
}
|
||
}
|
||
|
||
function verify_mayar($licenseKey) {
|
||
$cfg = cfg('mayar', []);
|
||
$base = rtrim($cfg['api_base'] ?? '', '/');
|
||
$ep = $cfg['endpoint_verify'] ?? '/v1/license/verify';
|
||
$url = $base . $ep;
|
||
|
||
// Most vendors use JSON body; adjust per your doc.
|
||
// If Mayar requires Authorization, add it here.
|
||
$headers = [];
|
||
if (!empty($cfg['secret_key'])) {
|
||
$headers[] = 'Authorization: Bearer ' . $cfg['secret_key'];
|
||
}
|
||
|
||
[$code,$body,$err] = http_post_json($url, ['license_key'=>$licenseKey], $headers);
|
||
if (!($code >= 200 && $code < 300)) {
|
||
return ['ok'=>false,'err'=>'mayar_http_'.$code];
|
||
}
|
||
$json = json_decode($body, true);
|
||
|
||
// Normalize based on Mayar’s response shape
|
||
// Expect something like: { success:true, data:{ valid:true, product_id:'...', type:'lifetime|subscription', expires_at: '...' } }
|
||
$valid = (bool)($json['success'] ?? false);
|
||
if (!$valid) return ['ok'=>false,'err'=>'mayar_invalid'];
|
||
|
||
$data = $json['data'] ?? [];
|
||
$planType = strtolower((string)($data['type'] ?? 'lifetime')); // lifetime or subscription
|
||
$expiresAt = $data['expires_at'] ?? null;
|
||
|
||
return ['ok'=>true,'norm'=>[
|
||
'key' => $licenseKey,
|
||
'source' => 'mayar',
|
||
'plan' => 'pro',
|
||
'product_id' => $data['product_id'] ?? null,
|
||
'valid' => true,
|
||
'expires_at' => $expiresAt,
|
||
'meta' => ['raw'=>$json,'plan_type'=>$planType]
|
||
]];
|
||
} |