Compare commits

...

2 Commits

3 changed files with 303 additions and 104 deletions

View File

@ -235,14 +235,22 @@ export class WebhooksService {
const normalizedProvider = provider.toLowerCase(); const normalizedProvider = provider.toLowerCase();
const metaSignature = this.readHeader(headers['x-hub-signature-256']); const metaSignature = this.readHeader(headers['x-hub-signature-256']);
const genericSecret = this.readHeader(headers['x-webhook-secret']); const genericSecret = this.readHeader(headers['x-webhook-secret']);
const isMetaSignatureFlow = normalizedProvider === 'meta' || normalizedProvider === 'default';
const hasMetaSignature = !!metaSignature;
if (normalizedProvider === 'meta' && config.appSecret) { if (isMetaSignatureFlow && config.appSecret && hasMetaSignature) {
if (!rawBody || !metaSignature) { if (!rawBody || !metaSignature) {
throw new UnauthorizedException('Missing meta webhook signature'); throw new UnauthorizedException('Missing meta webhook signature');
} }
verifyMetaSignature(rawBody, metaSignature, config.appSecret); verifyMetaSignature(rawBody, metaSignature, config.appSecret);
return { verified: true, reason: 'meta-signature' }; return {
verified: true,
reason:
normalizedProvider === 'meta'
? 'meta-signature'
: 'meta-signature-on-default-endpoint',
};
} }
if (genericSecret) { if (genericSecret) {
@ -253,7 +261,7 @@ export class WebhooksService {
return { verified: true, reason: 'shared-secret' }; return { verified: true, reason: 'shared-secret' };
} }
if (config.allowUnsigned) { if (config.allowUnsigned || !config.isProduction) {
return { verified: false, reason: 'unsigned-development-request' }; return { verified: false, reason: 'unsigned-development-request' };
} }
@ -281,6 +289,7 @@ export class WebhooksService {
? storedJson.appSecret ? storedJson.appSecret
: env.metaWebhookAppSecret, : env.metaWebhookAppSecret,
allowUnsigned: env.webhookAllowUnsigned, allowUnsigned: env.webhookAllowUnsigned,
isProduction: env.isProduction,
subscriptions: subscriptions:
Array.isArray(storedJson.subscriptions) && storedJson.subscriptions.length > 0 Array.isArray(storedJson.subscriptions) && storedJson.subscriptions.length > 0
? storedJson.subscriptions.filter((item): item is string => typeof item === 'string') ? storedJson.subscriptions.filter((item): item is string => typeof item === 'string')

View File

@ -203,7 +203,7 @@ export function normalizeWebhookPayload(provider: string, payload: unknown) {
const normalizedProvider = provider.toLowerCase(); const normalizedProvider = provider.toLowerCase();
if ( if (
normalizedProvider === 'meta' && (normalizedProvider === 'meta' || normalizedProvider === 'default') &&
readString(payloadRecord.object) === 'whatsapp_business_account' readString(payloadRecord.object) === 'whatsapp_business_account'
) { ) {
const metaEvents = buildMetaEvents(payloadRecord, normalizedProvider); const metaEvents = buildMetaEvents(payloadRecord, normalizedProvider);

View File

@ -1,136 +1,326 @@
# Codex Handoff # Codex Handoff
Snapshot tanggal: `2026-05-21` Snapshot tanggal: `2026-05-22`
Dokumen ini dipakai untuk mempercepat pindah sesi kerja Codex tanpa perlu audit ulang dari nol. Dokumen ini adalah ringkasan kondisi terakhir BizOne Portal supaya sesi Codex berikutnya bisa lanjut tanpa bongkar ulang dari nol.
## Ringkasan Cepat ## Ringkasan Cepat
- Repo: `bizone-portal` - Repo lokal: `/home/wira/work/codex/BizOne-portal`
- Branch aktif terakhir yang dicek: `main` - Repo server: `/srv/bizone-web`
- Worktree tidak bersih - Branch: `main`
- Backend build: sukses - Domain production: `https://portal.bizone.id`
- Frontend build: sukses, dengan warning CSS compatibility - Backend production: `127.0.0.1:3001` via systemd `bizone-backend`
- Production readiness: belum siap, masih banyak blocker operasional dan integrasi - Frontend production: `127.0.0.1:3000` via systemd `bizone-frontend`
- Meta webhook URL: `https://portal.bizone.id/api/webhooks/whatsapp`
- Midtrans notification URL: `https://portal.bizone.id/api/wallet/midtrans/notification`
## Worktree Saat Snapshot ## Commit Terakhir Lokal
Hasil `git status --short --branch` saat handoff dibuat:
```text ```text
## main...origin/main cc819ad Accept Midtrans dashboard notification tests
M deploy/debian12/app.env.example 96b326e Fix roles page locale label typing
?? public/ 5144207 Prepare BizOne portal production wallet and UI
36be860 Add Codex handoff and update public assets
46ea32c Refresh session in contacts API proxy routes
``` ```
File untracked yang terlihat: Catatan penting:
- `public/favicon.ico` - Push dari environment Codex lokal gagal karena remote HTTPS butuh credential interaktif.
- `public/bizone.png` - User perlu menjalankan `git push origin main` dari terminal interaktif yang punya akses Git.
- Server sudah pernah melihat commit `96b326e`, tapi commit `cc819ad` perlu dipastikan sudah masuk remote/server sebelum test ulang Midtrans dashboard.
Jangan asumsi file di atas aman untuk dihapus. Verifikasi dulu apakah memang asset baru yang ingin dipakai. ## Status Git Auth
## Struktur Kerja Utama Remote saat terakhir dicek:
- `backend/`: NestJS + TypeScript ```text
- `frontend/`: Next.js 15 app router origin https://git.iptek.co/wirabasalamah/BizOne-portal.git
- `prisma/`: schema dan migration ```
- `deploy/debian12/`: artefak deploy production Debian 12
- `PRODUCTION_CHECKLIST.md`: sumber status readiness production
## Status Build Terakhir Push dari Codex gagal dengan:
Perintah yang sudah diverifikasi: ```text
fatal: could not read Username for 'https://git.iptek.co': No such device or address
```
Solusi user:
```bash ```bash
cd backend && npm run build cd /home/wira/work/codex/BizOne-portal
cd frontend && npm run build git push origin main
``` ```
Hasil: Atau pakai username:
- Backend compile sukses
- Frontend compile sukses dan generate static pages sukses
Warning frontend yang masih ada berasal dari `autoprefixer` pada [frontend/src/app/globals.css](/Users/wirabasalamah/Documents/Codex/bizone-portal/frontend/src/app/globals.css):
- line `3868`
- line `4681`
- line `7788`
- line `8399`
- line `8755`
Masalahnya penggunaan nilai seperti `start` atau `end` pada properti yang lebih aman memakai `flex-start` atau `flex-end`.
## Status Production Checklist
Snapshot dari [PRODUCTION_CHECKLIST.md](/Users/wirabasalamah/Documents/Codex/bizone-portal/PRODUCTION_CHECKLIST.md):
- Selesai: `35`
- Parsial: `5`
- Belum selesai: `39`
Area blocker utama sebelum production:
- staging final belum ada
- test Meta end-to-end belum dilakukan
- audit permission belum selesai
- CI/CD deploy flow belum final
- monitoring dan alerting belum aktif
- backup dan restore drill belum dibuktikan
- full smoke test lintas modul belum selesai
## Dokumen yang Perlu Dibaca Dulu
Urutan baca yang paling efisien untuk sesi baru:
1. [PRODUCTION_CHECKLIST.md](/Users/wirabasalamah/Documents/Codex/bizone-portal/PRODUCTION_CHECKLIST.md)
2. [README.md](/Users/wirabasalamah/Documents/Codex/bizone-portal/README.md)
3. [deploy/debian12/README.md](/Users/wirabasalamah/Documents/Codex/bizone-portal/deploy/debian12/README.md)
4. [backend/package.json](/Users/wirabasalamah/Documents/Codex/bizone-portal/backend/package.json)
5. [frontend/package.json](/Users/wirabasalamah/Documents/Codex/bizone-portal/frontend/package.json)
## Command Cepat Untuk Re-Orientasi
```bash ```bash
git status --short --branch git push https://wira.irawan%40gmail.com@git.iptek.co/wirabasalamah/BizOne-portal.git main
sed -n '1,220p' PRODUCTION_CHECKLIST.md
cd backend && npm run build
cd frontend && npm run build
``` ```
Kalau perlu cek warning CSS: Jangan taruh token di command kalau tidak perlu, karena bisa masuk shell history.
## Fitur Besar Yang Sudah Masuk
- Redesign login, dashboard shell, sidebar, card spacing, dan halaman utama dashboard.
- Dual bahasa `EN/ID` diperluas ke banyak halaman.
- Global search di header.
- Notification center di header.
- Help page dari icon `?`.
- Profile menu dan halaman profile user.
- Wallet/saldo untuk broadcast.
- Minimum top up `Rp50.000`.
- Preset top up `50rb`, `100rb`, `250rb`, `500rb`, `1jt`.
- Harga broadcast sementara `Rp500` per pesan.
- Broadcast hanya cek saldo sebelum kirim, saldo dipotong setelah worker memproses pesan sukses.
- Integrasi Midtrans Snap API awal.
- Midtrans payment methods: `gopay`, `shopeepay`, `bank_transfer`, `credit_card`.
- Midtrans notification webhook.
- Production deploy docs untuk `portal.bizone.id`.
- Root `/` redirect ke `/login`, tidak lagi menampilkan starter landing page.
## Midtrans Status Terakhir
URL final yang harus dipakai di dashboard Midtrans:
```text
https://portal.bizone.id/api/wallet/midtrans/notification
```
Server internal test sudah pernah menghasilkan response ini setelah route aktif:
```json
{"message":"Invalid Midtrans notification signature.","error":"Bad Request","statusCode":400}
```
Itu normal untuk payload kosong.
Dashboard Midtrans test notification mengirim payload seperti:
```json
{
"transaction_status": "settlement",
"status_code": "200",
"signature_key": "...",
"payment_type": "gopay",
"order_id": "payment_notif_test_G311975080_...",
"merchant_id": "G311975080",
"gross_amount": "105000.00"
}
```
Karena `order_id` test tidak ada di tabel `payment_orders`, backend awalnya menolak. Commit `cc819ad` memperbaiki ini:
- Signature tetap divalidasi.
- Kalau `order_id` diawali `payment_notif_test_` dan `merchant_id` cocok dengan `MIDTRANS_MERCHANT_ID`, backend return `200`.
- Transaksi asli tetap wajib punya payment order.
Setelah commit `cc819ad` dipull ke server, jalankan:
```bash ```bash
nl -ba frontend/src/app/globals.css | rg '\b(start|end)\b' cd /srv/bizone-web
git pull
set -a
source .env
set +a
cd backend
NODE_ENV=development npm ci
npm run db:generate
npm run build
npm run db:migrate:deploy
sudo systemctl restart bizone-backend
``` ```
## Prioritas Kerja Berikutnya Lalu test:
Urutan yang paling masuk akal untuk dilanjutkan: ```bash
curl -i -X POST https://portal.bizone.id/api/wallet/midtrans/notification \
-H "Content-Type: application/json" \
-d '{}'
```
1. Rapikan warning CSS di `frontend/src/app/globals.css` lalu build ulang frontend. Payload kosong boleh tetap `400 Invalid Midtrans notification signature`; test dashboard Midtrans yang signed harus `200` setelah commit `cc819ad` aktif di server.
2. Audit perubahan di `deploy/debian12/app.env.example` dan putuskan apakah mau di-commit.
3. Putuskan status folder `public/` apakah asset final atau artefak lokal.
4. Pecah `PRODUCTION_CHECKLIST.md` menjadi task implementasi teknis yang bisa dikerjakan satu per satu.
5. Fokuskan sprint berikut ke salah satu jalur:
- jalur infra: staging, backup, monitoring, CI/CD
- jalur product integration: Meta webhook dan outbound live test
- jalur app hardening: permission audit dan smoke test
## Catatan Kerja ## Midtrans Env Production/Server
- Root `package.json` hanya dipakai untuk dependency Prisma bersama. User menunjukkan dashboard Midtrans `Environment Sandbox`, tapi key formatnya tetap:
- Backend local run yang disarankan oleh repo: `cd backend && npm run local`
- Frontend local run: `cd frontend && npm run dev`
- Deploy production yang didokumentasikan menargetkan `https://portal.bizone.id`
- Ada perbedaan konsep URL backend di dokumen deploy: browser-facing route memakai `/api`, sementara backend internal juga diekspos lewat `/backend-api`. Jangan ubah ini tanpa cek alur Next route handler lebih dulu.
## Definition Of Done Untuk Sesi Lanjutan ```text
Mid-client-...
Mid-server-...
```
Sesi baru sebaiknya selalu menutup kerja dengan: Jadi jangan lagi mengasumsikan sandbox pasti `SB-Mid-*` untuk akun ini. Yang penting key di `/srv/bizone-web/.env` sama dengan dashboard Midtrans yang dipakai.
- build ulang area yang diubah Contoh env server saat terakhir dibahas:
- update dokumen status bila ada perubahan readiness
- catat blocker nyata, bukan asumsi ```dotenv
- pastikan `git status` jelas sebelum handoff berikutnya MIDTRANS_ENV=sandbox
MIDTRANS_SERVER_KEY=Mid-server-...
MIDTRANS_CLIENT_KEY=Mid-client-...
MIDTRANS_MERCHANT_ID=G311975080
MIDTRANS_ALLOWED_PAYMENT_TYPES=gopay,shopeepay,bank_transfer,credit_card
```
Jangan commit `.env`.
## Server Deploy Notes
Jika backend build gagal dengan:
```text
sh: 1: tsc: not found
```
Penyebab: `NODE_ENV=production npm ci` tidak memasang devDependencies. Pakai:
```bash
NODE_ENV=development npm ci
```
Jika backend build gagal dengan banyak error Prisma seperti:
```text
Property 'sql' does not exist on type 'typeof Prisma'
Module '@prisma/client' has no exported member 'Campaign'
```
Penyebab: Prisma client stale setelah `npm ci`. Urutan benar:
```bash
cd /srv/bizone-web
npm install
cd backend
NODE_ENV=development npm ci
npm run db:generate
npm run build
```
Setelah build:
```bash
npm run db:migrate:deploy
sudo systemctl restart bizone-backend
```
Cek route wallet:
```bash
sudo journalctl -u bizone-backend -n 200 --no-pager | grep -i wallet
```
Harus ada:
```text
WalletController {/api/wallet}
Mapped {/api/wallet/topups/midtrans, POST}
WalletMidtransWebhookController {/api/wallet/midtrans}
Mapped {/api/wallet/midtrans/notification, POST}
```
## Nginx Production
Config nginx yang user kirim sudah benar untuk Midtrans dan Meta:
```nginx
location /api/webhooks/ {
proxy_pass http://127.0.0.1:3001/api/webhooks/;
}
location /api/wallet/midtrans/ {
proxy_pass http://127.0.0.1:3001/api/wallet/midtrans/;
}
location /backend-api/ {
proxy_pass http://127.0.0.1:3001/api/;
}
location / {
proxy_pass http://127.0.0.1:3000;
}
```
Kalau public endpoint `502`, cek backend/frontend service. Kalau internal backend `404`, berarti backend build belum memuat route baru.
## Credential Dev/Admin
Default seed admin:
```text
Email: admin@bizone.id
Password: ChangeMe123!
```
Jika server login gagal, reset seed:
```bash
cd /srv/bizone-web/backend
set -a
source ../.env
set +a
npm run seed:admin
```
Setelah production login pertama, ganti password dan aktifkan 2FA.
## Files Penting Yang Diubah
Backend:
- `backend/src/wallet/*`
- `backend/src/app.module.ts`
- `backend/src/campaigns/campaigns.service.ts`
- `backend/src/campaigns/campaigns.controller.ts`
- `backend/src/common/permission.guard.ts`
- `backend/src/auth/*`
- `prisma/schema.prisma`
- `prisma/migrations/0015_wallet/migration.sql`
Frontend:
- `frontend/src/app/dashboard/wallet/page.tsx`
- `frontend/src/components/wallet-topup-form.tsx`
- `frontend/src/components/dashboard-shell.tsx`
- `frontend/src/components/global-search-button.tsx`
- `frontend/src/components/notification-center.tsx`
- `frontend/src/components/profile-menu.tsx`
- `frontend/src/components/profile-forms.tsx`
- `frontend/src/app/dashboard/help/page.tsx`
- `frontend/src/app/dashboard/profile/page.tsx`
- `frontend/src/app/page.tsx`
- `frontend/src/app/globals.css`
- banyak halaman dashboard untuk spacing dan dual bahasa.
Deploy docs:
- `deploy/debian12/app.env.example`
- `deploy/debian12/nginx.portal.bizone.id.conf`
- `deploy/debian12/README.md`
- `PRODUCTION_CHECKLIST.md`
- `PRODUCTION_READINESS.md`
- `docs/production-server-checklist.md`
## Next Steps Paling Dekat
1. Push commit `cc819ad` ke remote.
2. Pull di server.
3. Rebuild backend dengan urutan Prisma yang benar.
4. Restart `bizone-backend`.
5. Test ulang Midtrans notification URL dari dashboard Midtrans.
6. Rebuild frontend jika ada perubahan UI baru.
7. Jalankan smoke test:
- buka `https://portal.bizone.id`
- login admin
- buka wallet
- buat top up Midtrans
- cek payment order dan saldo setelah notification sukses.
## Catatan Keamanan
- `.env` sudah di-ignore dan tidak ikut commit.
- Beberapa credential pernah muncul di chat/screenshot, jadi untuk production live sebaiknya rotate credential final.
- `deploy/debian12/app.env.example` sudah dibersihkan agar hanya berisi placeholder.