diff --git a/system/paymentgateway/index.html b/system/paymentgateway/index.html new file mode 100644 index 0000000..06d7405 Binary files /dev/null and b/system/paymentgateway/index.html differ diff --git a/system/paymentgateway/iotec.OLD b/system/paymentgateway/iotec.OLD new file mode 100644 index 0000000..f296de5 --- /dev/null +++ b/system/paymentgateway/iotec.OLD @@ -0,0 +1,254 @@ +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/'; + } +} \ No newline at end of file diff --git a/system/paymentgateway/iotec.json b/system/paymentgateway/iotec.json new file mode 100644 index 0000000..21a9af4 --- /dev/null +++ b/system/paymentgateway/iotec.json @@ -0,0 +1,5 @@ +{ + "sandbox": { + "name": "iotecpay-sandbox" + } +} \ No newline at end of file diff --git a/system/paymentgateway/iotec.php b/system/paymentgateway/iotec.php new file mode 100644 index 0000000..91a46c1 --- /dev/null +++ b/system/paymentgateway/iotec.php @@ -0,0 +1,163 @@ + 'error', 'message' => 'ioTec Pay not configured']); + exit; + } +} + +function iotec_show_config() +{ + global $ui; + $ui->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'); + + $settings = [ + 'iotec_client_id' => $iotec_client_id, + 'iotec_client_secret' => $iotec_client_secret, + 'iotec_wallet_id' => $iotec_wallet_id, + 'iotec_env' => $iotec_env + ]; + + foreach ($settings as $key => $value) { + $d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one(); + if ($d) { + $d->value = $value; + $d->save(); + } else { + $d = ORM::for_table('tbl_appconfig')->create(); + $d->setting = $key; + $d->value = $value; + $d->save(); + } + } + + _log('[' . $admin['username'] . ']: ioTec Pay ' . $_L['Settings_Saved_Successfully'], 'Admin', $admin['id']); + header('Content-Type: application/json'); + echo json_encode(['status' => 'success', 'message' => $_L['Settings_Saved_Successfully']]); + exit; +} + +function iotec_create_transaction_json($trx, $user) +{ + global $config; + header('Content-Type: application/json'); + + // Validate required inputs + if (empty($trx['price']) || empty($user['phonenumber']) || empty($user['username']) || empty($trx['plan_name'])) { + http_response_code(400); + echo json_encode([ + 'status' => 'error', + 'message' => 'Missing required input: price, phonenumber, username, or plan_name' + ]); + exit; + } + + $externalId = uniqid('bill_'); + + $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'])) { + http_response_code(500); + echo json_encode([ + 'status' => 'error', + 'message' => 'Failed to authenticate with ioTec', + 'response' => $tokenResponse + ]); + exit; + } + + $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'])) { + http_response_code(502); + echo json_encode([ + 'status' => 'error', + 'message' => 'Failed to create transaction', + 'response' => $result + ]); + exit; + } + + $d = ORM::for_table('tbl_payment_gateway') + ->where('username', $user['username']) + ->where('status', 1) + ->find_one(); + + if (!$d) { + http_response_code(404); + echo json_encode([ + 'status' => 'error', + 'message' => 'Active payment gateway record not found for user' + ]); + exit; + } + + $d->gateway_trx_id = $result['id']; + $d->pg_url_payment = 'N/A'; + $d->pg_request = json_encode($result); + $d->expired_date = date('Y-m-d H:i:s', strtotime("+6 HOUR")); + $d->save(); + + echo json_encode([ + 'status' => 'success', + 'message' => 'Transaction created. Authorize on phone.', + 'transaction_id' => $result['id'], + 'external_id' => $externalId + ]); + exit; +} + +// Other functions (iotec_payment_notification, iotec_get_status, iotec_get_server, etc.) remain unchanged. diff --git a/system/paymentgateway/iotecold.json b/system/paymentgateway/iotecold.json new file mode 100644 index 0000000..d3b4462 --- /dev/null +++ b/system/paymentgateway/iotecold.json @@ -0,0 +1,6 @@ +[ + { + "id": "MobileMoney", + "name": "Mobile Money" + } +] \ No newline at end of file