Release App & Host the API
Go-live plan
Step 1: Create the database
- 1hPanel → Databases → MySQL Databases
- 2Database name
karteasy, userkart, a strong password → Create. Hostinger adds a prefix:u123456789_karteasy,u123456789_kart - 3Click Enter phpMyAdmin for that database
- 4Import → choose
database.sql→ Go → 14 tables appear
Step 2: Upload the backend
- 1hPanel → File Manager →
public_html - 2Create the folder
karteasy - 3Upload a ZIP of the
backendfolder contents and click Extract (faster than many single files) - 4Make sure hidden files like
.htaccessare there (File Manager → Settings → Show hidden files)
📁 public_html/ 📁 karteasy/ .htaccess database.sql (protected by .htaccess – you can also delete it now) 📁 api/ admin/ includes/ uploads/ tests/
Step 3: Edit config.php
Open karteasy/includes/config.php in File Manager and replace the default values with your database details and your domain:
// includes/config.php on Hostinger (example values – use yours from hPanel)
define('DB_HOST', getenv('KE_DB_HOST') ?: 'localhost');
define('DB_PORT', getenv('KE_DB_PORT') ?: '3306');
define('DB_NAME', getenv('KE_DB_NAME') ?: 'u123456789_karteasy');
define('DB_USER', getenv('KE_DB_USER') ?: 'u123456789_kart');
define('DB_PASS', getenv('KE_DB_PASS') ?: 'Your-Strong-DB-Password');
define('BASE_URL', rtrim(getenv('KE_BASE_URL') ?: 'https://yourdomain.com/karteasy', '/'));
define('DEBUG', getenv('KE_DEBUG') === '1'); // stays false onlinehPanel → Security → SSL → install the free certificate and enable Force HTTPS. The app's release build only talks to https.
Step 4: Check it online
- 1Open
https://yourdomain.com/karteasy/api/→"database": "connected","products": 16 - 2Open an image:
…/karteasy/uploads/products/p7.png - 3Open
…/karteasy/includes/config.php→ must be 403 Forbidden - 4Log in to
…/karteasy/admin/and change the admin password - 5From your computer:
php tests/smoke-test.php https://yourdomain.com/karteasy
Step 5: Point the app to the live API
Only the release block changes. Keep the trailing slash — Retrofit needs it.
// app/build.gradle.kts – release block
release {
buildConfigField("String", "BASE_URL", "\"https://yourdomain.com/karteasy/\"")
resValue("string", "app_name", "KartEasy")
isMinifyEnabled = true
isShrinkResources = true
…
}
defaultConfig {
versionCode = 1 // +1 for every Play Store upload
versionName = "1.0" // what users see
}Before publishing, remove the Use demo account button from fragment_login.xml and LoginFragment.kt, and replace the demo shop data with your real products in the admin panel.
Step 6: Signing key and release build
Play Store apps must be signed. Create your upload key once: Android Studio → Build → Generate Signed App Bundle / APK → Android App Bundle → Create new… (save the .jks file safely). Then let Gradle use it without putting passwords in the project:
# ~/.gradle/gradle.properties (in your user folder – NOT inside the project, never commit it) KARTEASY_STORE_FILE=/home/you/keys/karteasy-upload.jks KARTEASY_STORE_PASSWORD=your-store-password KARTEASY_KEY_ALIAS=upload KARTEASY_KEY_PASSWORD=your-key-password
The signingConfigs block in app/build.gradle.kts (Setup lesson) reads these four values. Build the bundle:
# Windows: gradlew.bat bundleRelease ./gradlew bundleRelease # Result: app/build/outputs/bundle/release/app-release.aab
APK
- installs directly on a phone
- good for sharing a test build
- Build → Build APK(s)
AAB (App Bundle)
- required by Google Play
- Play makes small APKs per device
- bundleRelease
R8 and ProGuard rules
isMinifyEnabled = true lets R8 remove unused code and rename classes — the app becomes smaller and harder to reverse-engineer. But Gson reads field names with reflection, so we must keep our model classes as they are:
# Gson creates the model classes with reflection, so R8 must keep their fields.
-keep class com.codingeasily.karteasy.data.model.** { *; }
# Retrofit + coroutines: keep generic signatures of suspend functions.
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-dontwarn okhttp3.internal.platform.**Install the release build on a phone before uploading. A missing keep rule shows up as empty lists or all-zero prices only in release, never in debug.
Step 7: Publish on Google Play
- 1Create a Google Play developer account (one-time fee) and a new app KartEasy
- 2Store listing: short and full description, 512 × 512 icon, 1024 × 500 feature graphic, at least 2 phone screenshots
- 3App content: privacy policy URL (you collect name, email, phone and address), data safety form, content rating, target audience
- 4Testing → Internal testing: upload the .aab, add testers, install from the Play link
- 5New personal developer accounts must run a closed test with real testers before production — follow the checklist Play Console shows
- 6Production → create release → upload the same or a newer .aab → rollout
The Android course has a full lesson on this: Release on Play Store.
Updating later
| Change | What to do |
|---|---|
| Products, prices, banners, coupons | admin panel only — no app update |
| A bug in an API file | upload the fixed PHP file — every phone gets the fix at once |
| New screen or app bug | versionCode + 1, new .aab, new release in Play Console |
| New API field | add it to PHP first (old apps ignore unknown fields), then use it in the app |
The backend README in the download repeats these steps:
# KartEasy Backend (PHP + MySQL REST API + Admin Panel)
Backend for the **KartEasy** Android shopping app from the CodingEasily tutorial
(https://codingeasily.in/tutorial/android-ecommerce/).
Works on **PHP 7.2 or newer** (tested on PHP 8.3) and **MySQL 5.7+ / MariaDB 10.3+**.
No Composer, no framework: upload and it runs on any shared hosting (Hostinger, cPanel, XAMPP).
## Folder structure
```
karteasy/
├── .htaccess passes the Authorization header, hides database.sql
├── database.sql tables + sample data (16 products, 6 categories, 3 banners, 2 coupons)
├── includes/ config, DB connection, helpers (blocked from the browser)
├── api/ JSON endpoints used by the app
│ ├── home.php categories.php banners.php products.php product.php
│ ├── auth/ register login logout me update
│ ├── cart/ list add update remove
│ ├── wishlist/ list toggle
│ ├── address/ list save delete
│ ├── coupons/ list apply
│ ├── orders/ place list detail cancel
│ └── reviews/ list add
├── admin/ admin panel (login, dashboard, orders, products, categories, banners, coupons, customers)
├── uploads/ product / category / banner images (PHP execution disabled)
└── tests/smoke-test.php 26 automatic API checks (run from the command line)
```
## Install on Hostinger (or any cPanel hosting)
1. **Create a database**: hPanel → Databases → MySQL Databases. Note the database name, user and password.
2. **Import tables**: open phpMyAdmin for that database → Import → choose `database.sql` → Go.
3. **Upload files**: File Manager → `public_html` → create folder `karteasy` → upload everything from this folder into it.
4. **Edit `includes/config.php`**: set `DB_NAME`, `DB_USER`, `DB_PASS` and `BASE_URL`
(for example `https://yourdomain.com/karteasy`).
5. **Test**: open `https://yourdomain.com/karteasy/api/` → you should see `"database":"connected"`.
6. **Admin panel**: `https://yourdomain.com/karteasy/admin/`
login `admin@karteasy.in` / `Admin@123` → **change this password** (see below).
Demo customer for the app: `test@karteasy.in` / `Test@123`.
### Change the admin password
Run this once in a PHP file or `php -r`, then put the output in phpMyAdmin → `admins.password_hash`:
```php
<?php echo password_hash('YourNewStrongPassword', PASSWORD_DEFAULT);
```
## Run locally (XAMPP / PHP built-in server)
```bash
mysql -u root -p -e "CREATE DATABASE karteasy CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
mysql -u root -p karteasy < database.sql
# edit includes/config.php (DB_USER=root, BASE_URL=http://10.0.2.2:8000) then:
php -S 0.0.0.0:8000
```
The Android emulator reaches your computer at `http://10.0.2.2:8000/`.
A real phone on the same Wi-Fi uses your computer's IP, e.g. `http://192.168.1.5:8000/`.
## Test the API
```bash
php tests/smoke-test.php http://localhost:8000
php tests/smoke-test.php https://yourdomain.com/karteasy
```
It registers a fresh test customer and runs a full flow (cart, wishlist, address, order, cancel, logout).
You should see `26 passed, 0 failed`.
## Response format
Every endpoint returns the same JSON shape:
```json
{ "success": true, "message": "Added to cart.", "data": { ... } }
```
Errors use a proper HTTP status (401 not logged in, 404 not found, 409 conflict such as stock,
422 validation) with `"success": false` and a readable `message`.
## Authentication
`auth/login.php` and `auth/register.php` return a 64-character `token`.
Send it on every protected request:
```
Authorization: Bearer <token>
```
Only the SHA-256 hash of the token is stored in `user_tokens`, tokens expire after 30 days,
and `auth/logout.php` deletes it.
## Security checklist (already done)
- Prepared statements everywhere (no SQL injection), whitelisted sort columns
- `password_hash()` / `password_verify()` for passwords
- Prices, coupons and totals are always calculated on the server
- Order placement runs in one transaction with `SELECT ... FOR UPDATE` (no overselling)
- Admin: session login, `session_regenerate_id`, CSRF token on every form, login rate limit,
`htmlspecialchars` on all output
- Uploads: real image check with `getimagesize`, 2 MB limit, random file names, no PHP in `uploads/`
- `includes/` and `database.sql` cannot be opened from the browser
Before going live: use HTTPS, change the admin password, keep `DEBUG` off.