function electricity_purchase_provider(mysqli $con, array $payload): array { $logFile = __DIR__.'/logs/electricity_provider.log'; $log = function($tag, $data=null) use ($logFile){ $msg="[".date('Y-m-d H:i:s')."] {$tag}"; if($data!==null){ $msg.=" ".(is_string($data)?$data:json_encode($data,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)); } @file_put_contents($logFile,$msg.PHP_EOL,FILE_APPEND); }; $openStderr = function(string $path){ $h = @fopen($path,'a'); return (is_resource($h) ? $h : null); }; $http = function(string $url, array $headers, array $body, string $method='POST') use ($log, $openStderr, $logFile){ $log('HTTP_REQ', ['url'=>$url,'method'=>$method,'headers'=>$headers,'body'=>$body]); $ch = curl_init($url); $stderr = $openStderr($logFile); $opts = [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60, CURLOPT_HTTPHEADER => $headers, ]; if (strtoupper($method)==='POST') { $opts[CURLOPT_POST] = true; $opts[CURLOPT_POSTFIELDS] = json_encode($body); } if ($stderr) { $opts[CURLOPT_VERBOSE] = true; $opts[CURLOPT_STDERR] = $stderr; } curl_setopt_array($ch, $opts); $raw = curl_exec($ch); $info = curl_getinfo($ch); $err = curl_error($ch); curl_close($ch); if ($stderr) fclose($stderr); $log('HTTP_RES', ['http'=>$info['http_code']??0,'raw'=>is_string($raw)?substr($raw,0,4000):'', 'err'=>$err?:null]); if ($raw === false) return ['http'=>$info['http_code']??0,'error'=>$err?:'cURL error','raw'=>'','json'=>null]; return ['http'=>$info['http_code']??0,'error'=>null,'raw'=>$raw,'json'=>json_decode($raw,true)]; }; $log("ENTER_BACKEND_PAYLOAD",$payload); try { /* ---- Load provider config ---- We will pick the first match among common electricity names. */ $res = $con->query(" SELECT apikey, apilink FROM api WHERE name IN ('electricity','electricity bill','electricity purchase') ORDER BY FIELD(name,'electricity','electricity bill','electricity purchase') LIMIT 1 "); if (!$res || $res->num_rows === 0) { $log("CONFIG_ERROR","No electricity API config found"); return ['ok'=>false,'message'=>'Electricity API config missing']; } $cfg = $res->fetch_assoc(); $apiKey = trim((string)($cfg['apikey'] ?? '')); $apiUrl = trim((string)($cfg['apilink'] ?? '')); if ($apiUrl === '') $apiUrl = 'https://n3tdata.com/api/bill'; // default fallback $apiUrl = rtrim($apiUrl,'/'); $log("CONFIG", ['endpoint'=>$apiUrl,'has_apiKey'=> $apiKey !== '' ]); // Pull inbound fields (from your controller) $disco = (string)($payload['disco'] ?? ''); // For AWK this is service_id (e.g., AEDC) $meterType = strtolower((string)($payload['meter_type'] ?? 'prepaid')); // prepaid|postpaid $meterNumber = (string)($payload['meter_number'] ?? ''); $amountNaira = (int)($payload['amount'] ?? 0); $requestId = (string)($payload['request-id'] ?? ('Bill_'.mt_rand(100000000,999999999))); /* ========================= * AmeenWebKey BRANCH * ========================= */ if (stripos($apiUrl, 'ameenwebkey.ng/api/v1/purchase/electricity') !== false) { // Normalize base to .../api/v1 $apiBase = preg_replace('#/purchase/electricity/?$#i','', $apiUrl); if (!preg_match('#/api/v\d+$#i', $apiBase)) { $apiBase = rtrim($apiBase,'/').'/api/v1'; } $authUrl = rtrim($apiBase,'/').'/auth/login'; $purchaseUrl = rtrim($apiBase,'/').'/purchase/electricity'; // Determine Bearer: // - If apikey looks like JSON creds: {"phone_number":"..","password":".."} or {"email":"..","password":".."} // - Or "phone:password" // - Else use apikey directly as Bearer $bearer = null; $login = json_decode($apiKey, true); $hasCreds = is_array($login) && !empty($login['password']) && (!empty($login['phone_number']) || !empty($login['email'])); if (!$hasCreds && strpos($apiKey, ':') !== false) { [$user,$pass] = explode(':', $apiKey, 2); $login = ['phone_number'=>trim($user), 'password'=>trim($pass)]; $hasCreds = true; } if ($hasCreds) { $userField = !empty($login['phone_number']) ? 'phone_number' : 'email'; $authBody = [ $userField => $login[$userField], 'password' => $login['password'] ]; $authHdrs = [ "Content-Type: application/json", "Accept: application/json" ]; $log('AWK_AUTH','Authenticating to AmeenWebKey',['url'=>$authUrl,'body'=>$authBody]); $authRes = $http($authUrl, $authHdrs, $authBody, 'POST'); $awkToken = null; if (is_array($authRes['json'] ?? null)) { $s = $authRes['json']; $st = $s['status'] ?? $s['Status'] ?? null; $ok = ($st === true) || (is_string($st) && in_array(strtolower($st), ['success','successful','ok','true','1'], true)); if ($ok && !empty($s['access_token'])) $awkToken = $s['access_token']; elseif (!empty($s['data']['access_token'])) $awkToken = $s['data']['access_token']; elseif (!empty($s['token'])) $awkToken = $s['token']; elseif (!empty($s['data']['token'])) $awkToken = $s['data']['token']; } if (!$awkToken) { return ['ok'=>false,'message'=>'AmeenWebKey auth failed','http'=>$authRes['http']??null,'raw'=>$authRes['raw']??'']; } $bearer = $awkToken; } else { // use raw apikey as Bearer $bearer = preg_replace('/^Bearer\s+/i','', $apiKey); } // Build EXACT AWK purchase body & headers $provBody = [ 'service_id' => (string)$disco, // e.g. AEDC 'meter_type' => $meterType, // 'prepaid'|'postpaid' 'amount' => (string)(int)$amountNaira, // string amount 'number' => $meterNumber // meter number ]; $headers = [ "Authorization: Bearer {$bearer}", "Content-Type: application/json", "Accept: application/json" ]; $log('AWK_PURCHASE','Calling purchase/electricity',['url'=>$purchaseUrl,'body'=>$provBody]); $res = $http($purchaseUrl, $headers, $provBody, 'POST'); if ($res['error']) { return ['ok'=>false,'message'=>"Curl error (provider): ".$res['error'],'http'=>$res['http']??null]; } $resp = $res['json']; if (!is_array($resp)) { return ['ok'=>false,'message'=>'Invalid provider response','http'=>$res['http']??null,'raw'=>$res['raw']??'']; } // Normalize per AWK spec // { "status":"success", "message":"Electricity purchase successful", "transaction_reference":"AD246810121" } $status = strtolower((string)($resp['status'] ?? '')); $ok = in_array($status, ['success','successful','ok','true','1'], true); $msg = $resp['message'] ?? ($ok ? 'Electricity purchase successful' : 'Electricity purchase failed'); $ref = $resp['transaction_reference'] ?? ($resp['data']['transaction_reference'] ?? ($resp['reference'] ?? ($resp['data']['reference'] ?? null))); return [ 'ok' => $ok, 'message' => $msg, 'http' => $res['http'] ?? null, 'raw' => $res['raw'] ?? '', 'reference' => $ref, 'provider' => $resp ]; } /* ========================= * DEFAULT (existing Token providers like n3tdata) * ========================= */ $provBody = [ 'disco' => (int)$disco, 'meter_type' => $meterType, 'meter_number' => is_numeric($meterNumber) ? (int)$meterNumber : $meterNumber, 'amount' => (int)$amountNaira, 'bypass' => (bool)($payload['bypass'] ?? false), 'request-id' => $requestId, ]; $log("PROVIDER_BODY_BUILT",$provBody); $headers = ['Content-Type: application/json']; if ($apiKey !== '') $headers[] = "Authorization: Token {$apiKey}"; $res = $http($apiUrl, $headers, $provBody, 'POST'); if ($res['error']) { return ['ok'=>false,'message'=>"Curl error (provider): ".$res['error'],'http'=>$res['http']??null]; } $resp = $res['json']; if (!is_array($resp)) { return ['ok'=>false,'message'=>'Invalid provider response','http'=>$res['http']??null,'raw'=>$res['raw']??'']; } // Your legacy normalization $status = strtolower((string)($resp['status'] ?? '')); $ok = ($status === 'success'); $msg = $resp['message'] ?? ($ok ? 'Transaction successful' : 'Transaction failed'); return [ 'ok' => $ok, 'message' => $msg, 'http' => $res['http'] ?? null, 'raw' => $res['raw'] ?? '', 'provider' => $resp ]; } catch (Throwable $e) { $log("EXCEPTION", ['msg'=>$e->getMessage(),'file'=>$e->getFile(),'line'=>$e->getLine()]); return ['ok'=>false,'message'=>'Exception: '.$e->getMessage()]; } }