assign('_title', 'ioTec Pay - Payment Gateway'); $ui->assign('env', [ ['id' => 'Sandbox', 'name' => 'Sandbox (Testing)'], ['id' => 'Live', 'name' => 'Live (Production)'] ]); $ui->display('iotec.tpl'); } function iotec_save_config() { global $admin, $_L; $iotec_client_id = _post('iotec_client_id'); $iotec_client_secret = _post('iotec_client_secret'); $iotec_wallet_id = _post('iotec_wallet_id'); $iotec_env = _post('iotec_env'); $d = ORM::for_table('tbl_appconfig')->where('setting', 'iotec_client_id')->find_one(); if ($d) { $d->value = $iotec_client_id; $d->save(); } else { $d = ORM::for_table('tbl_appconfig')->create(); $d->setting = 'iotec_client_id'; $d->value = $iotec_client_id; $d->save(); } $d = ORM::for_table('tbl_appconfig')->where('setting', 'iotec_client_secret')->find_one(); if ($d) { $d->value = $iotec_client_secret; $d->save(); } else { $d = ORM::for_table('tbl_appconfig')->create(); $d->setting = 'iotec_client_secret'; $d->value = $iotec_client_secret; $d->save(); } $d = ORM::for_table('tbl_appconfig')->where('setting', 'iotec_wallet_id')->find_one(); if ($d) { $d->value = $iotec_wallet_id; $d->save(); } else { $d = ORM::for_table('tbl_appconfig')->create(); $d->setting = 'iotec_wallet_id'; $d->value = $iotec_wallet_id; $d->save(); } $d = ORM::for_table('tbl_appconfig')->where('setting', 'iotec_env')->find_one(); if ($d) { $d->value = $iotec_env; $d->save(); } else { $d = ORM::for_table('tbl_appconfig')->create(); $d->setting = 'iotec_env'; $d->value = $iotec_env; $d->save(); } _log('[' . $admin['username'] . ']: ioTec Pay ' . $_L['Settings_Saved_Successfully'], 'Admin', $admin['id']); r2(U . 'paymentgateway/iotec', 's', $_L['Settings_Saved_Successfully']); } function iotec_create_transaction($trx, $user) { global $config; $externalId = uniqid('bill_'); // Obtain access token $tokenResponse = json_decode(Http::postData(iotec_get_server('auth') . 'connect/token', [ 'client_id' => $config['iotec_client_id'], 'client_secret' => $config['iotec_client_secret'], 'grant_type' => 'client_credentials' ], [ 'Content-Type: application/x-www-form-urlencoded' ]), true); if (empty($tokenResponse['access_token'])) { Message::sendTelegram("ioTec payment failed: Failed to obtain access token\n\n" . json_encode($tokenResponse, JSON_PRETTY_PRINT)); r2(U . 'order/package', 'e', Lang::T("Failed to authenticate with ioTec.")); } $json = [ 'category' => 'MobileMoney', 'currency' => 'ITX', 'walletId' => $config['iotec_wallet_id'], 'externalId' => $externalId, 'payer' => $user['phonenumber'], 'amount' => $trx['price'], 'payerNote' => 'Payment for ' . $trx['plan_name'], 'payeeNote' => 'Hotspot billing', 'transactionChargesCategory' => 'ChargeCustomer' ]; $result = json_decode(Http::postJsonData(iotec_get_server('api') . 'api/collections/collect', $json, [ 'Authorization: Bearer ' . $tokenResponse['access_token'], 'Content-Type: application/json' ]), true); if (empty($result['id'])) { Message::sendTelegram("ioTec payment failed\n\n" . json_encode($result, JSON_PRETTY_PRINT)); r2(U . 'order/package', 'e', Lang::T("Failed to create transaction.\n" . ($result['message'] ?? 'Unknown error'))); } $d = ORM::for_table('tbl_payment_gateway') ->where('username', $user['username']) ->where('status', 1) ->find_one(); $d->gateway_trx_id = $result['id']; $d->pg_url_payment = 'N/A'; // ioTec doesn't provide a payment URL $d->pg_request = json_encode($result); $d->expired_date = date('Y-m-d H:i:s', strtotime("+6 HOUR")); $d->save(); r2(U . "order/view/" . $d['id'], 's', Lang::T("Transaction created. Please authorize the payment on your phone.")); } function iotec_payment_notification() { global $config; $headers = getallheaders(); $securityKey = $headers['X-Callback-Security-Key'] ?? ''; $expectedKey = $config['iotec_callback_security_key'] ?? 'your_callback_security_key'; if ($securityKey !== $expectedKey) { Message::sendTelegram("ioTec callback failed: Invalid security key\n\n" . json_encode($headers, JSON_PRETTY_PRINT)); http_response_code(401); exit(json_encode(['status' => 'error', 'message' => 'Invalid security key'])); } $data = json_decode(file_get_contents('php://input'), true); if (empty($data['id']) || empty($data['status'])) { Message::sendTelegram("ioTec callback failed: Invalid data\n\n" . json_encode($data, JSON_PRETTY_PRINT)); http_response_code(400); exit(json_encode(['status' => 'error', 'message' => 'Invalid callback data'])); } $transactionId = $data['id']; $status = $data['status']; $externalId = $data['externalId'] ?? ''; $amountPaid = $data['amount'] ?? 0; $username = $data['payer'] ?? ''; // Map to username via lookup if needed $trxid = $data['externalId'] ?? ''; // Map to transaction ID $d = ORM::for_table('tbl_payment_gateway') ->where('gateway_trx_id', $transactionId) ->where('status', 1) ->find_one(); if (!$d) { Message::sendTelegram("ioTec callback failed: Transaction not found\n\n" . json_encode($data, JSON_PRETTY_PRINT)); http_response_code(404); exit(json_encode(['status' => 'error', 'message' => 'Transaction not found'])); } if ($status === 'Success') { $d->gateway_trx_id = $transactionId; $d->save(); r2(U . 'order/view/' . $d['id'] . '/check', 's', Lang::T("ioTec Payment Completed.")); } elseif ($status === 'Failed') { Message::sendTelegram("ioTec Payment Failed: \n\n" . json_encode($data, JSON_PRETTY_PRINT)); r2(U . 'order/package', 'e', Lang::T("ioTec Payment Failed.")); } else { Message::sendTelegram("ioTec Payment Pending: \n\n" . json_encode($data, JSON_PRETTY_PRINT)); r2(U . 'order/package', 'w', Lang::T("ioTec Payment Pending.")); } } function iotec_get_status($trx, $user) { global $config; $trans_id = $trx['gateway_trx_id']; // Obtain access token $tokenResponse = json_decode(Http::postData(iotec_get_server('auth') . 'connect/token', [ 'client_id' => $config['iotec_client_id'], 'client_secret' => $config['iotec_client_secret'], 'grant_type' => 'client_credentials' ], [ 'Content-Type: application/x-www-form-urlencoded' ]), true); if (empty($tokenResponse['access_token'])) { Message::sendTelegram("ioTec status check failed: Failed to obtain access token\n\n" . json_encode($tokenResponse, JSON_PRETTY_PRINT)); r2(U . "order/view/" . $trx['id'], 'w', Lang::T("Failed to authenticate with ioTec.")); } $result = json_decode(Http::getData(iotec_get_server('api') . 'api/collections/status/' . $trans_id, [ 'Authorization: Bearer ' . $tokenResponse['access_token'], 'Content-Type: application/json' ]), true); if (empty($result['status'])) { Message::sendTelegram("ioTec status check failed\n\n" . json_encode($result, JSON_PRETTY_PRINT)); r2(U . "order/view/" . $trx['id'], 'w', Lang::T("Transaction still unpaid.")); } elseif ($result['status'] === 'Success' && $trx['status'] != 2) { if (!Package::rechargeUser($user['id'], $trx['routers'], $trx['plan_id'], $trx['gateway'], 'ioTec')) { r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Failed to activate your Package, please try again later.")); } $trx->pg_paid_response = json_encode($result); $trx->payment_method = 'ioTec'; $trx->payment_channel = 'MobileMoney'; $trx->paid_date = date('Y-m-d H:i:s'); $trx->status = 2; $trx->save(); r2(U . "order/view/" . $trx['id'], 's', Lang::T("Transaction successful.")); } elseif ($result['status'] === 'Failed') { $trx->pg_paid_response = json_encode($result); $trx->status = 3; $trx->save(); r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Transaction failed.")); } elseif ($trx['status'] == 2) { r2(U . "order/view/" . $trx['id'], 'd', Lang::T("Transaction has been paid.")); } else { Message::sendTelegram("ioTec get_status: unknown result\n\n" . json_encode($result, JSON_PRETTY_PRINT)); r2(U . "order/view/" . $trx['id'], 'w', Lang::T("Transaction still pending.")); } } function iotec_get_server($type = 'api') { global $_app_stage; if ($_app_stage == 'Live' && $type == 'auth') { return 'https://id.iotec.io/'; } elseif ($_app_stage == 'Live' && $type == 'api') { return 'https://pay.iotec.io/'; } else { return $type == 'auth' ? 'https://id.iotec.io/' : 'https://pay.iotec.io/'; } }