Tambahkan upload bukti transfer dengan pagar yang jelas: bucket privat, validasi mime/ukuran di server, path buatan server, token lookup, dan status donasi tetap pending.
40m bacaanKuis WajibMicro Badge Lesson
Upload Bukti Transfer + Validasi File
Di Module 04, donasi berhasil masuk sebagai pending. Itu belum berarti uang sudah terverifikasi.
Untuk transfer manual, donatur perlu mengirim bukti: screenshot QRIS, bukti Virtual Account, atau PDF bukti pembayaran. Tapi begitu menerima file dari user, risiko naik:
file bisa terlalu besar;
tipe file bisa dipalsukan;
nama file bisa berisi informasi pribadi;
bukti bisa diupload ke donasi orang lain;
file bisa terbuka publik kalau bucket salah konfigurasi;
status donasi bisa keliru berubah jadi verified hanya karena file masuk.
Karena itu, upload bukti bukan sekadar form file. Ini fitur keamanan.
Targetnya: donatur yang memegang akses sah ke halaman konfirmasi bisa upload bukti untuk donasinya, file disimpan di bucket privat, metadata tersimpan, dan status donasi tetap pending.
Upload bukan verifikasi pembayaran
Ini prinsip pertama.
Saat bukti transfer berhasil diupload, status donasi tidak boleh langsung berubah menjadi verified. File hanya berkata:
text
donatur mengklaim sudah transfer dan mengirim bukti
Yang berkata “uang benar-benar masuk” adalah proses verifikasi admin di tahap berikutnya.
Kalau agent mengubah status jadi verified setelah upload, itu bug bisnis.
Bucket harus privat
Bukti transfer bisa memuat nama, nomor rekening, nominal, tanggal, atau informasi lain. Jangan simpan di bucket publik.
Migration yang kamu minta harus membuat bucket seperti ini:
sql
insert into storage.buckets (id, name, public)values ('transfer-proofs', 'transfer-proofs', false)on conflict (id) do update set public = false;
public = false penting. URL bukti tidak boleh bisa ditebak lalu dibuka siapa pun.
Kalau nanti admin perlu melihat bukti, buat signed URL dari server untuk durasi pendek, atau tampilkan lewat route server yang mengecek admin. Jangan membuat object publik hanya agar mudah ditampilkan.
Kolom metadata bukti
Tambahkan kolom ke donations:
sql
alter table public.donations add column if not exists proof_path text, add column if not exists proof_original_name text, add column if not exists proof_mime text, add column if not exists proof_size_bytes integer, add column if not exists proof_uploaded_at timestamptz;
Maknanya:
proof_path: lokasi object di storage;
proof_original_name: nama asli file, hanya metadata;
proof_mime: tipe file yang diterima;
proof_size_bytes: ukuran file;
proof_uploaded_at: kapan bukti masuk.
Jangan jadikan proof_original_name sebagai path object. Nama file dari client tidak boleh dipercaya.
Validasi client boleh ditambah untuk UX, tapi server tetap wajib. Jangan pernah mengandalkan atribut <input accept="..."> sebagai keamanan. User bisa mengirim request manual.
Akses upload harus terikat ke donasi
Untuk guest, halaman konfirmasi memakai token lookup. Upload bukti harus mengikuti pola yang sama:
text
donationNumber + token → hash token → cari donation dengan lookup_token_hash cocok
Lalu cek status:
text
status harus pending
Kalau status sudah verified, rejected, cancelled, atau refunded, upload bukti baru harus ditolak. Jangan membiarkan bukti berubah setelah keputusan final kecuali ada workflow revisi yang dirancang khusus.
Prompt untuk Antigravity
text
Mulai dari AGENTS.md, DESIGN.md, dan docs/README.md.Tugas: tambahkan upload bukti transfer untuk donasi pending.Baca:- src/app/donation/confirmation/[donationNumber]/page.tsx- src/app/campaigns/[slug]/donate/actions.ts- src/lib/donation/token.ts- supabase/migrations/20260829_vertical_slice.sqlRequirement bisnis:1. Upload bukti bukan verifikasi pembayaran. Setelah upload, donations.status tetap 'pending'.2. Upload hanya boleh untuk donation dengan status 'pending'.3. Guest harus membuktikan akses dengan token yang cocok dengan lookup_token_hash.4. Jangan membuka SELECT public ke donations.Requirement storage:1. Buat bucket Supabase Storage bernama transfer-proofs dengan public=false lewat migration.2. Tambahkan kolom proof_path, proof_original_name, proof_mime, proof_size_bytes, proof_uploaded_at pada donations.3. Upload dilakukan di server. Service role key hanya dipakai server dan tidak boleh muncul di client.4. File yang diterima hanya image/png, image/jpeg, application/pdf.5. Ukuran maksimal 5 MB.6. Object path dibuat server dari donationNumber + random UUID. Jangan gunakan file.name sebagai path.Requirement UI:1. Tambahkan form upload di halaman konfirmasi untuk donasi pending.2. Tampilkan pesan sukses setelah bukti terupload.3. Tampilkan pesan jelas untuk tipe file salah, ukuran terlalu besar, token salah, atau status bukan pending.4. Copy harus tenang, amanah, tidak manipulatif, dan tanpa dark pattern.Acceptance criteria:- PNG/JPG/PDF valid <=5 MB berhasil upload.- File selain whitelist ditolak 415.- File >5 MB ditolak 413.- Upload dengan token salah ditolak.- proof_path terisi, proof metadata terisi, status donasi tetap pending.- Bucket transfer-proofs privat.- npm run lint dan npm run build berhasil.Laporkan file yang diubah dan cara verifikasinya.
Prompt ini panjang karena risikonya nyata. Untuk upload file, “buatkan upload” terlalu kabur.
bucket privatakses upload terikat ke donation/tokenstatus harus pendingmime whitelistsize limitpath dibuat serverservice role hanya serverstatus tidak otomatis verified
Kalau satu pagar hilang, jangan klaim fitur upload sudah aman.
Checkpoint
Bucket transfer-proofs privat.
Kolom metadata bukti ada di donations.
File divalidasi di server: tipe dan ukuran.
Path object tidak memakai nama file client.
Upload hanya untuk donasi pending milik pemegang akses sah.
Upload sukses tidak mengubah status menjadi verified.
Alur positif dan negatif sudah diuji.
Bukti transfer sekarang punya tempat yang lebih aman: tidak publik, tidak asal upload, dan tidak otomatis dianggap pembayaran sah. Ini penting, karena amanah bukan cuma menerima file, tapi menjaga agar file itu tidak mengubah status uang sebelum manusia memverifikasi.
Checkpoint KuisTarget: 70%
Kuis 05.1 — Upload Bukti Transfer
Kuis ini wajib untuk sertifikat. Kamu tetap bisa lanjut ke pelajaran berikutnya dan mengerjakannya nanti.