iKhokha integration complete
This commit is contained in:
120
classes/iKhokhaClient.php
Normal file
120
classes/iKhokhaClient.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
class IkhokhaClient {
|
||||
private string $appId;
|
||||
private string $appSecret;
|
||||
private string $apiUrl;
|
||||
|
||||
public function __construct() {
|
||||
// Try getenv first, then fallback to $_ENV if available
|
||||
$this->appId = getenv('IKHOKHA_APP_ID') ?: ($_ENV['IKHOKHA_APP_ID'] ?? '');
|
||||
$this->appSecret = getenv('IKHOKHA_APP_SECRET') ?: ($_ENV['IKHOKHA_APP_SECRET'] ?? '');
|
||||
$this->apiUrl = getenv('IKHOKHA_API_URL') ?: ($_ENV['IKHOKHA_API_URL'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the iKhokha API. Signs the payload per API docs.
|
||||
* $endpoint should be the path portion starting with '/public-api/...'
|
||||
*/
|
||||
private function request(string $endpoint, array $data, string $method = 'POST') {
|
||||
// Validate apiUrl
|
||||
if (empty($this->apiUrl)) {
|
||||
return ['error' => true, 'errno' => 3, 'message' => 'IKHOKHA_API_URL is not configured in environment'];
|
||||
}
|
||||
|
||||
// If the configured API URL already contains the endpoint path, use it as-is.
|
||||
if ((function_exists('str_ends_with') && str_ends_with($this->apiUrl, $endpoint)) ||
|
||||
(substr_compare($this->apiUrl, $endpoint, -strlen($endpoint)) === 0)) {
|
||||
$url = $this->apiUrl;
|
||||
} else {
|
||||
$url = rtrim($this->apiUrl, '/') . $endpoint;
|
||||
}
|
||||
$body = json_encode($data);
|
||||
|
||||
// Build payload to sign: path + body and apply escape rules per iKhokha docs
|
||||
$parsed = parse_url($url);
|
||||
$path = $parsed['path'] ?? $endpoint;
|
||||
$payloadToSign = $path . $body;
|
||||
|
||||
// Escape function from iKhokha example
|
||||
$escapeString = function ($str) {
|
||||
$escaped = preg_replace(['/[\\\"\'\"]/u', '/\x00/'], ['\\\\$0', '\\0'], (string)$str);
|
||||
$cleaned = str_replace('\/', '/', $escaped);
|
||||
return $cleaned;
|
||||
};
|
||||
|
||||
$escapedPayload = $escapeString($payloadToSign);
|
||||
$signature = hash_hmac('sha256', $escapedPayload, $this->appSecret);
|
||||
|
||||
$ch = curl_init($url);
|
||||
|
||||
$headers = [
|
||||
'Content-Type: application/json',
|
||||
"IK-APPID: {$this->appId}",
|
||||
"IK-SIGN: {$signature}"
|
||||
];
|
||||
|
||||
// Optional debug logging to logs/ikhokha.log when IKHOKHA_DEBUG_LOG is true
|
||||
$debugLog = getenv('IKHOKHA_DEBUG_LOG') ?: ($_ENV['IKHOKHA_DEBUG_LOG'] ?? null);
|
||||
if ($debugLog) {
|
||||
$logPath = dirname(__DIR__) . '/logs/ikhokha.log';
|
||||
$logEntry = [
|
||||
'time' => date('c'),
|
||||
'url' => $url,
|
||||
'headers' => $headers,
|
||||
'body' => $data,
|
||||
'signature' => $signature
|
||||
];
|
||||
@file_put_contents($logPath, json_encode(['request' => $logEntry]) . PHP_EOL, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
if (strtoupper($method) === 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$error = curl_error($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
// Log response if debug enabled
|
||||
if (!empty($debugLog)) {
|
||||
$logPath = dirname(__DIR__) . '/logs/ikhokha.log';
|
||||
$respEntry = [
|
||||
'time' => date('c'),
|
||||
'http_code' => $httpCode,
|
||||
'errno' => $errno,
|
||||
'error' => $error,
|
||||
'response' => $response
|
||||
];
|
||||
@file_put_contents($logPath, json_encode(['response' => $respEntry]) . PHP_EOL, FILE_APPEND | LOCK_EX);
|
||||
}
|
||||
|
||||
if ($response === false) {
|
||||
return ['error' => true, 'message' => $error, 'errno' => $errno];
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a payment link using the iKhokha create payment endpoint.
|
||||
* $body must match iKhokha request schema (amount in smallest unit, urls, externalTransactionID, etc.)
|
||||
*/
|
||||
public function createPaymentLink(array $body) {
|
||||
return $this->request('/public-api/v1/api/payment', $body, 'POST');
|
||||
}
|
||||
|
||||
public function getPaymentStatus($paymentId) {
|
||||
// Use the GET status endpoint
|
||||
$endpoint = '/public-api/v1/api/getStatus/' . urlencode($paymentId);
|
||||
return $this->request($endpoint, [], 'GET');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user