Persiapan Server
API RubrikPulsa dijalankan di VPS Alibaba Cloud dengan IP publik 149.129.241.211. Ikuti langkah berikut untuk menyiapkan server:
1. Prasyarat
- Python 3.8+
- PostgreSQL
- Akses SSH ke VPS (
ssh user@149.129.241.211) - Alibaba Cloud Console untuk aturan keamanan
2. Instalasi Dependensi
Masuk ke VPS dan instal dependensi:
ssh user@149.129.241.211
cd /home/user/rubrikpulsa-api
pip3 install fastapi uvicorn python-jose[cryptography] pydantic bcrypt psycopg2-binary python-telegram-bot requests tenacity python-dotenv
3. Konfigurasi Lingkungan
Buat file .env di /home/user/rubrikpulsa-api:
TELEGRAM_BOT_TOKEN=your_bot_token
POSTGRES_DB=rubrikpulsa
POSTGRES_USER=your_user
POSTGRES_PASSWORD=your_password
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
API_SECRET_KEY=your_secret_key
OWNER_USER_ID=your_owner_user_id
DIGIFLAZZ_USERNAME=your_digiflazz_username
DIGIFLAZZ_API_KEY=your_digiflazz_api_key
BANK_ACCOUNT="BCA 1234567890"
WHATSAPP_NUMBER="081111222333"
Ganti your_* dengan nilai sebenarnya. Buat API_SECRET_KEY:
python3 -c "import secrets; print(secrets.token_hex(32))"
4. Siapkan PostgreSQL
Instal dan konfigurasikan database:
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresql
sudo -u postgres psql
CREATE USER your_postgres_user WITH PASSWORD 'your_postgres_password';
CREATE DATABASE rubrikpulsa;
GRANT ALL PRIVILEGES ON DATABASE rubrikpulsa TO your_postgres_user;
\q
psql -U your_postgres_user -d rubrikpulsa
CREATE TABLE agents (
id SERIAL PRIMARY KEY,
phone_number VARCHAR(15) UNIQUE NOT NULL,
telegram_id VARCHAR(50),
email VARCHAR(100) UNIQUE,
name VARCHAR(100) NOT NULL,
address VARCHAR(255),
pin VARCHAR(255) NOT NULL,
password_hash VARCHAR(255),
balance DECIMAL(15,2) DEFAULT 0,
upline_id INTEGER REFERENCES agents(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE transactions (
id SERIAL PRIMARY KEY,
agent_id INTEGER REFERENCES agents(id),
type VARCHAR(50) NOT NULL,
amount DECIMAL(15,2) NOT NULL,
status VARCHAR(20) NOT NULL,
digiflazz_ref_id VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE commissions (
id SERIAL PRIMARY KEY,
agent_id INTEGER REFERENCES agents(id),
downline_id INTEGER REFERENCES agents(id),
transaction_id INTEGER REFERENCES transactions(id),
amount DECIMAL(15,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE products (
sku_code VARCHAR(50) PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(15,2) NOT NULL,
category VARCHAR(50),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
5. Jalankan Server
Jalankan FastAPI:
cd /home/user/rubrikpulsa-api
uvicorn main:app --host 0.0.0.0 --port 8000
Buka port 8000 di Alibaba Cloud Console (ECS > Security Groups > Tambah aturan TCP 8000).
Gunakan supervisor untuk produksi:
sudo apt install supervisor
sudo nano /etc/supervisor/conf.d/rubrikpulsa.conf
[program:rubrikpulsa]
directory=/home/user/rubrikpulsa-api
command=/usr/bin/python3 -m uvicorn main:app --host 0.0.0.0 --port 8000
autostart=true
autorestart=true
stderr_logfile=/var/log/rubrikpulsa.err.log
stdout_logfile=/var/log/rubrikpulsa.out.log
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start rubrikpulsa
Akses dokumentasi di http://149.129.241.211:8000/docs.
6. Aktifkan HTTPS
Instal Nginx dan SSL:
sudo apt install nginx certbot python3-certbot-nginx
sudo nano /etc/nginx/sites-available/rubrikpulsa
server {
listen 80;
server_name 149.129.241.211;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
sudo ln -s /etc/nginx/sites-available/rubrikpulsa /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
sudo certbot --nginx -d api.rubrikpulsa.com
Daftarkan domain api.rubrikpulsa.com untuk menggantikan 149.129.241.211.
Daftar Endpoint
API diakses melalui http://149.129.241.211:8000/api (nanti https://api.rubrikpulsa.com/api setelah SSL). Berikut semua endpoint:
1. Pendaftaran (/api/register)
Metode: POST
Deskripsi: Mendaftarkan agen baru.
Body:
{
"name": "Asep",
"phone_number": "082321214369",
"email": "asep@example.com",
"address": "Subang",
"password": "password123",
"pin": "123456",
"referral_code": null
}
Respons Sukses (200):
{"message": "Pendaftaran berhasil"}
Respons Error:
400: {"detail": "Nomor telepon atau email sudah terdaftar"}
400: {"detail": "Nomor telepon tidak valid"}
400: {"detail": "PIN harus 6 digit"}
400: {"detail": "Kata sandi minimal 8 karakter"}
400: {"detail": "Kode referral tidak valid"}
500: {"detail": "Internal server error"}
2. Login (/api/login)
Metode: POST
Deskripsi: Autentikasi agen untuk mendapatkan token JWT.
Body:
{
"identifier": "082321214369",
"password": "password123"
}
Respons Sukses (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
Respons Error:
400: {"detail": "Silakan atur kata sandi di aplikasi"}
401: {"detail": "Kata sandi salah"}
404: {"detail": "Agen tidak ditemukan"}
500: {"detail": "Internal server error"}
3. Pengaturan Kata Sandi (/api/set_password)
Metode: POST
Deskripsi: Mengatur kata sandi baru dengan verifikasi PIN.
Body:
{
"phone_number": "082321214369",
"password": "newpassword123",
"pin": "123456"
}
Respons Sukses (200):
{"message": "Kata sandi berhasil diatur"}
Respons Error:
401: {"detail": "PIN salah"}
404: {"detail": "Agen tidak ditemukan"}
500: {"detail": "Internal server error"}
4. Cek Saldo (/api/ceksaldo)
Metode: GET
Deskripsi: Mengambil saldo agen.
Header: Authorization: Bearer <access_token>
Respons Sukses (200):
{"balance": 100000.0}
Respons Error:
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
5. Deposit (/api/deposit)
Metode: POST
Deskripsi: Mengajukan permintaan deposit.
Header: Authorization: Bearer <access_token>
Body:
{
"amount": 50000,
"pin": "123456"
}
Respons Sukses (200):
{"message": "Silakan transfer Rp50000 ke BCA 1234567890..."}
Respons Error:
400: {"detail": "Jumlah minimal Rp50.000"}
401: {"detail": "PIN salah"}
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
6. Daftar Harga (/api/pricelist)
Metode: GET
Deskripsi: Mengambil daftar produk.
Header: Authorization: Bearer <access_token>
Respons Sukses (200):
{
"data": [
{"sku_code": "TL5", "product_name": "Telkomsel 5K", "price": 5500.0, "category": "Pulsa"}
]
}
Respons Error:
400: {"detail": "Gagal mengambil daftar harga"}
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
7. Top-up (/api/topup)
Metode: POST
Deskripsi: Melakukan transaksi top-up.
Header: Authorization: Bearer <access_token>
Body:
{
"customer_no": "081234567890",
"product_code": "TL5",
"pin": "123456"
}
Respons Sukses (200):
{"ref_id": "REF123", "new_balance": 94500.0}
Respons Error:
400: {"detail": "Saldo tidak cukup"}
400: {"detail": "Produk tidak ditemukan"}
401: {"detail": "PIN salah"}
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
8. Laporan Transaksi (/api/report)
Metode: GET
Deskripsi: Mengambil riwayat transaksi.
Header: Authorization: Bearer <access_token>
Respons Sukses (200):
{
"data": [
{"id": 1, "type": "TOPUP", "amount": 5500.0, "status": "SUCCESS", "digiflazz_ref_id": "REF123", "created_at": "2025-05-20T12:00:00"}
]
}
Respons Error:
400: {"detail": "Gagal mengambil laporan"}
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
9. Downline (/api/downline)
Metode: GET
Deskripsi: Mengambil daftar downline dan total komisi.
Header: Authorization: Bearer <access_token>
Respons Sukses (200):
{
"downlines": [
{"phone_number": "081234567891", "name": "Budi", "balance": 20000.0}
],
"total_commission": 5000.0
}
Respons Error:
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
10. Reset PIN (/api/resetpin)
Metode: POST
Deskripsi: Mengganti PIN.
Header: Authorization: Bearer <access_token>
Body:
{
"old_pin": "123456",
"new_pin": "654321"
}
Respons Sukses (200):
{"message": "PIN berhasil diubah"}
Respons Error:
400: {"detail": "PIN baru harus 6 digit"}
401: {"detail": "PIN lama salah"}
401: {"detail": "Invalid token"}
500: {"detail": "Internal server error"}
11. Tambah Saldo (/api/addbalance)
Metode: POST
Deskripsi: Menambah saldo agen (khusus pemilik).
Header: owner_id: <OWNER_USER_ID>
Body:
{
"phone_number": "082321214369",
"amount": 100000
}
Respons Sukses (200):
{"message": "Balance added successfully", "new_balance": 200000.0}
Respons Error:
403: {"detail": "Unauthorized"}
404: {"detail": "Agen tidak ditemukan"}
500: {"detail": "Internal server error"}
Catatan: Endpoint ini hanya untuk pemilik (memerlukan owner_id dari .env).
Integrasi ke Aplikasi Android
API di http://149.129.241.211:8000/ diintegrasikan menggunakan Retrofit. Berikut panduan untuk menghubungkan endpoint ke aplikasi Android.
1. Tambahkan Dependensi
Di build.gradle:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
2. Buat Model Data
Untuk /api/login dan /api/ceksaldo:
data class LoginRequest(
val identifier: String,
val password: String
)
data class LoginResponse(
val access_token: String,
val token_type: String
)
data class BalanceResponse(
val balance: Float
)
3. Buat Interface Retrofit
Definisikan endpoint:
interface RubrikPulsaApi {
@POST("api/login")
suspend fun login(@Body request: LoginRequest): LoginResponse
@GET("api/ceksaldo")
suspend fun cekSaldo(@Header("Authorization") token: String): BalanceResponse
}
4. Inisialisasi Retrofit
Konfigurasikan Retrofit:
object ApiClient {
private const val BASE_URL = "http://149.129.241.211:8000/"
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api: RubrikPulsaApi = retrofit.create(RubrikPulsaApi::class.java)
suspend fun login(identifier: String, password: String): LoginResponse? {
return try {
api.login(LoginRequest(identifier, password))
} catch (e: Exception) {
null
}
}
}
5. Panggil Endpoint Login
Contoh pemanggilan /api/login dari Activity atau ViewModel:
suspend fun login(identifier: String, password: String): LoginResponse? {
val response = ApiClient.login(identifier, password)
if (response != null) {
val sharedPreferences = context.getSharedPreferences("RubrikPulsa", Context.MODE_PRIVATE)
sharedPreferences.edit().putString("access_token", response.access_token).apply()
}
return response
}
Gunakan Coroutines untuk memanggil fungsi ini:
lifecycleScope.launch {
val response = login("082321214369", "password123")
if (response != null) {
// Pindah ke halaman utama
} else {
// Tampilkan pesan error
}
}
6. Tangani Token JWT
Gunakan token untuk endpoint autentikasi:
val sharedPreferences = context.getSharedPreferences("RubrikPulsa", Context.MODE_PRIVATE)
val token = sharedPreferences.getString("access_token", "") ?: ""
val response = ApiClient.api.cekSaldo("Bearer $token")
7. Tangani Error
Parsir respons error:
if (response.isSuccessful) {
// Proses data
} else {
val error = response.errorBody()?.string()
// Tampilkan pesan seperti "Kata sandi salah" atau "Agen tidak ditemukan"
}
8. Izinkan HTTP (Sementara)
Karena belum HTTPS, tambahkan ke res/xml/network_security_config.xml:
149.129.241.211
Di AndroidManifest.xml:
Praktik Keamanan
- HTTPS: Aktifkan SSL untuk
149.129.241.211menggunakan Certbot. - Simpan Token: Gunakan
EncryptedSharedPreferencesuntuk menyimpan token. - Validasi Input: API memvalidasi input, tetapi aplikasi Android juga harus memeriksa masukan pengguna.
- Refresh Token: Pertimbangkan menambahkan endpoint
/refresh_token. - Rate Limiting: Gunakan
slowapiuntuk membatasi permintaan berlebihan.