whoami7 - Manager
:
/
home
/
simspala
/
yoursimzone.com
/
Upload File:
files >> /home/simspala/yoursimzone.com/testing.php
<?php /** * RACT Plan ID Discovery — MTN NXG (DataPlan) Scanner (Serial) * ------------------------------------------------------------ * - Grabs BEARER from DB: SELECT server_token FROM servers WHERE phone_number = :msisdn * - Falls back to env RACT_MASTER_TOKEN if DB token not found. * - Scans RACT_NG_Data_{start..end} strictly in order (serial), never parallel. * - Continues on all errors; never aborts mid-range. * * CLI: * php find_ract_plans.php beneficiary=+23480XXXXXXX start=2500 end=40000 price=null * * ENV (recommended): * export RACT_DB_HOST=localhost * export RACT_DB_NAME=simspala_aedatahub * export RACT_DB_USER=simspala_aedatahub * export RACT_DB_PASS=simspala_aedatahub * export RACT_MASTER_MSISDN=+2348166665409 * export RACT_MASTER_TOKEN='YOUR_BEARER_TOKEN' # used only as fallback */ @ignore_user_abort(true); @set_time_limit(0); @ini_set('memory_limit', '-1'); /////// CONFIG (ENV first, else defaults) /////// $DB_HOST = getenv('RACT_DB_HOST') ?: 'localhost'; $DB_NAME = getenv('RACT_DB_NAME') ?: 'simspala_yoursim'; $DB_USER = getenv('RACT_DB_USER') ?: 'simspala_yoursim'; $DB_PASS = getenv('RACT_DB_PASS') ?: 'simspala_yoursim'; $DB_CHAR = 'utf8mb4'; $MASTER_MSISDN = getenv('RACT_MASTER_MSISDN') ?: '+2348166665409'; $FALLBACK_TOKEN= getenv('RACT_MASTER_TOKEN') ?: ''; const API_URL = 'https://mtn-dxl-transaction-core.mymtnnxgeaprod.mtnnigeria.net/api/v3/subscription'; const API_CHANNEL = 'MTNAPPNXG'; $LOG_FILE = __DIR__ . '/ract_plan_scan.log'; ///////////////////////////////////////////////// // ---------- UTIL ---------- function log_line($msg, $ctx = []) { global $LOG_FILE; $line = '['.date('c').'] '.$msg.(empty($ctx) ? '' : ' '.json_encode($ctx, JSON_UNESCAPED_SLASHES)); @file_put_contents($LOG_FILE, $line.PHP_EOL, FILE_APPEND); echo $line.PHP_EOL; } function pdo_factory($host, $db, $user, $pass, $charset='utf8mb4') { static $pdo = null; if ($pdo) return $pdo; $dsn = "mysql:host={$host};dbname={$db};charset={$charset}"; $pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_PERSISTENT => false, ]); return $pdo; } // Minimal table to match your columns; JSON fallback to LONGTEXT function ensureTable($pdo) { $sql = <<<SQL CREATE TABLE IF NOT EXISTS `found_ract_plans` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `sku` VARCHAR(64) NOT NULL, `status_code` VARCHAR(32) DEFAULT NULL, `http_code` INT DEFAULT NULL, `response_json` JSON DEFAULT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `product_id` VARCHAR(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_sku` (`sku`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; SQL; try { $pdo->exec($sql); } catch (Throwable $e) { $pdo->exec(str_replace('JSON', 'LONGTEXT', $sql)); } } function alreadyRecorded($pdo, $sku) { try { $st = $pdo->prepare('SELECT 1 FROM found_ract_plans WHERE sku = ? LIMIT 1'); $st->execute([$sku]); return (bool)$st->fetchColumn(); } catch (Throwable $e) { log_line('DB check error (skip and continue)', ['sku'=>$sku,'err'=>$e->getMessage()]); return false; // don't block scan } } function saveFound($pdo, $sku, $statusCode, $httpCode, $json) { $productId = $json['subscriptionId'] ?? $json['product_id'] ?? $sku; $sql = 'INSERT IGNORE INTO found_ract_plans (sku, status_code, http_code, response_json, product_id) VALUES (?,?,?,?,?)'; for ($attempt=1; $attempt<=3; $attempt++) { try { $st = $pdo->prepare($sql); $st->execute([$sku, $statusCode, $httpCode, json_encode($json), $productId]); return; } catch (Throwable $e) { log_line('DB insert error', ['sku'=>$sku,'attempt'=>$attempt,'err'=>$e->getMessage()]); usleep(400000 * $attempt); } } log_line('DB insert failed permanently; continuing', ['sku'=>$sku]); } /** Classifier: * FOUND if: * - success (statusCode === "0000"), OR * - text/description mentions insufficient/eligibility/error, OR * - any non-empty subscriptionDescription (unless clearly invalid product). * INVALID if clearly invalid/non-existent offer/plan. */ function classify_response($httpCode, $json, $bodyRaw) { $status = $json['statusCode'] ?? null; $parts = []; foreach (['message','error','developerMessage','msg','description','title','subscriptionDescription'] as $k) { if (!empty($json[$k]) && is_string($json[$k])) $parts[] = $json[$k]; } $joined = trim(implode(' | ', $parts)); if ($joined === '' && is_string($bodyRaw)) $joined = $bodyRaw; $txt = mb_strtolower($joined); $is_success = ($httpCode >= 200 && $httpCode < 300) && ($status === '0000'); $exist_but_failed_markers = [ 'insufficient','insufficent','insufficient funds','low balance','balance too low', 'not eligible','ineligible','eligibility','not allowed','not permitted', 'cannot purchase','cannot be purchased','not active on this offer','failed','failure', 'declined','denied','barred','restricted','suspend','suspended','expired','blocked', 'limit','quota','cap reached','maximum','min spend','daily limit','weekly limit', 'error','invalid state','processing issue','temporarily unavailable','try again later', 'insufficient airtime','insufficient data' ]; $is_exist_but_failed = false; foreach ($exist_but_failed_markers as $m) { if ($txt !== '' && mb_strpos($txt, $m) !== false) { $is_exist_but_failed = true; break; } } $invalid_markers = [ 'invalid product','product not found','invalid product id','unknown product', 'not a valid product','no product found','offer not found','plan not found', 'invalid plan','invalid offer','product id does not exist','offer does not exist' ]; $is_invalid_product = false; foreach ($invalid_markers as $m) { if ($txt !== '' && mb_strpos($txt, $m) !== false) { $is_invalid_product = true; break; } } $has_sub_desc = !empty($json['subscriptionDescription']) && is_string($json['subscriptionDescription']); if ($has_sub_desc && !$is_invalid_product) $is_exist_but_failed = true; $found = $is_success || $is_exist_but_failed; return ['found'=>$found,'invalid'=>$is_invalid_product,'status'=>$status, 'msg'=>$joined]; } /** Fetch bearer from servers for the master MSISDN. */ function fetch_master_token(PDO $pdo, string $msisdn): ?string { // try with and without '+' (db might store either) $plain = ltrim($msisdn, '+'); $cands = [$msisdn, $plain, '+'.$plain]; // Prefer exact phone_number matches $sql = "SELECT server_token FROM servers WHERE phone_number IN (?,?,?) ORDER BY id DESC LIMIT 1"; try { $st = $pdo->prepare($sql); $st->execute($cands); $row = $st->fetch(); if (!empty($row['server_token'])) return trim($row['server_token']); } catch (Throwable $e) { log_line('WARN: token fetch failed', ['err'=>$e->getMessage()]); } // Optionally try alternate column names (uncomment if needed) /* foreach (['msisdn','phone','sim_number'] as $col) { try { $st = $pdo->prepare("SELECT server_token FROM servers WHERE {$col} IN (?,?,?) ORDER BY id DESC LIMIT 1"); $st->execute($cands); $row = $st->fetch(); if (!empty($row['server_token'])) return trim($row['server_token']); } catch (Throwable $e) { } } */ return null; } // ---------- INPUT ---------- $args = []; if (PHP_SAPI === 'cli') { global $argv; foreach ($argv as $i => $a) { if ($i && strpos($a,'=')!==false) { [$k,$v]=explode('=',$a,2); $args[$k]=$v; } } } else { $args = $_GET; } $beneficiary = $args['beneficiary'] ?? '+2347031212750'; $start = isset($args['start']) ? (int)$args['start'] : 37535; // default start $end = isset($args['end']) ? (int)$args['end'] : 40000; // default end if ($end < 1 || $end > 40000) $end = 40000; $price_input = array_key_exists('price', $args) ? $args['price'] : '0'; $price = ($price_input === '' || strtolower($price_input)==='null') ? null : $price_input; if (!$beneficiary || !preg_match('/^\+?\d+$/', $beneficiary)) { log_line('Invalid beneficiary; using default +2347031212750'); $beneficiary = '+2347031212750'; } if ($beneficiary === $MASTER_MSISDN) { log_line('Note: beneficiary equals MASTER_MSISDN; purchase may not behave as expected.'); } // Known SKUs to skip (optional) $known = [ 'RACT_NG_Data_1684','RACT_NG_Data_2135','RACT_NG_Data_194','RACT_NG_Data_743', 'RACT_NG_Data_1683','RACT_NG_Data_744','RACT_NG_Data_745','RACT_NG_Data_196', 'RACT_NG_Data_197','RACT_NG_Data_212','RACT_NG_Data_198', ]; // ---------- BOOT ---------- $pdo = pdo_factory($DB_HOST, $DB_NAME, $DB_USER, $DB_PASS, $DB_CHAR); ensureTable($pdo); // Get bearer token from DB (servers) using MASTER_MSISDN $dbToken = fetch_master_token($pdo, $MASTER_MSISDN); $MASTER_TOKEN = $dbToken ?: $FALLBACK_TOKEN; if ($dbToken) { log_line('Using server_token from servers table for MASTER_MSISDN'); } else { log_line('WARN: No server_token found for MASTER_MSISDN; using fallback env token'); } log_line('--- RACT scan start (serial) ---', [ 'beneficiary'=>$beneficiary, 'range'=>[$start,$end], 'price'=>($price===null?'omit':$price), ]); $primaryMsisdn = ltrim($MASTER_MSISDN, '+'); $beneficiary_id = ltrim($beneficiary, '+'); // ---------- CORE LOOP (STRICTLY SERIAL, never parallel) ---------- for ($i = $start; $i <= $end; $i++) { $sku = "RACT_NG_Data_{$i}"; try { // Ensure we never accidentally skip indices: // (Checks below only "continue" after we log the index.) if (in_array($sku, $known, true)) { log_line('SKIP known SKU', ['sku'=>$sku]); continue; } if (alreadyRecorded($pdo, $sku)) { log_line('SKIP already recorded', ['sku'=>$sku]); continue; } $payload = [ 'primaryMsisdn' => $primaryMsisdn, 'payment_source' => 'CIS', 'product_id' => $sku, 'product_type' => 'DataPlan', 'beneficiary_id' => $beneficiary_id, 'payment_method' => 'Airtime', 'renewal' => false, 'cvmoffer' => false, ]; if ($price !== null) $payload['price'] = $price; $headers = [ 'Authorization: Bearer ' . $MASTER_TOKEN, 'Channel: ' . API_CHANNEL, 'Content-Type: application/json', ]; // Serial HTTP attempt with small retry/backoff for transient issues $maxHttpAttempts = 4; $attempt = 0; $body = null; $httpCode = 0; $errno = 0; $err = ''; while ($attempt++ < $maxHttpAttempts) { $ch = curl_init(API_URL); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 45, CURLOPT_POSTFIELDS => json_encode($payload), ]); $body = curl_exec($ch); $errno = curl_errno($ch); $err = curl_error($ch); $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($errno) { log_line('HTTP ERR', ['sku'=>$sku,'attempt'=>$attempt,'errno'=>$errno,'error'=>$err]); usleep(400000 * $attempt); continue; } if ($httpCode === 429 || ($httpCode >= 500 && $httpCode <= 599)) { log_line('Transient HTTP', ['sku'=>$sku,'attempt'=>$attempt,'http'=>$httpCode]); usleep(300000 * $attempt); continue; } break; // success or non-retriable } $json = json_decode($body, true) ?: []; $class = classify_response($httpCode, $json, $body); log_line('TRY', [ 'sku'=>$sku,'http'=>$httpCode,'statusCode'=>$class['status'], 'found'=>$class['found']?1:0,'invalid'=>$class['invalid']?1:0 ]); if ($class['invalid']) { usleep(100000); continue; } if ($class['found']) { saveFound($pdo, $sku, $class['status'], $httpCode, $json); log_line('RECORDED FOUND', ['sku'=>$sku]); usleep(140000); } else { log_line('FAIL RESP (logged only)', ['sku'=>$sku,'snippet'=>mb_substr($body ?? '', 1000)]); usleep(100000); } if (($i % 200) === 0) log_line('HEARTBEAT', ['progress'=>$i.'/'.$end]); } catch (Throwable $e) { // Always continue to the very end (serial, no gaps) log_line('ITERATION ERROR (continuing)', ['sku'=>$sku,'err'=>$e->getMessage()]); usleep(150000); continue; } } log_line('--- RACT scan done ---', ['range'=>[$start,$end]]);
Copyright ©2021 || Defacer Indonesia