18

Arsitektur Teknis Bursa Sekunder & Escrow

Updated: Sep 2026Oleh: SystemModul Potensi Desa, Lahan & Crowdfunding

Arsitektur Teknis Sistem Bursa Sekunder & Escrow Kliring

Dokumen ini mendokumentasikan desain teknis perangkat lunak, relasi data, proteksi keamanan tingkat basis data (Row Level Security), serta alur eksekusi logika bisnis (Server Actions) untuk subsistem Bursa Sekunder Saham Desa dan Escrow Kliring BUMDes.


1. Diagram Alur Transaksi & Integrasi Layanan#

Memuat diagram alur...

2. Skema Basis Data & Integritas Relasional#

Subsistem Bursa Sekunder beroperasi di atas dua entitas tabel utama yang terhubung dengan tabel crowdfunding pasar perdana:

2.1. Tabel village_crowdfunding_secondary_listings

Menyimpan penawaran jual saham yang dibuka oleh investor:

KolomTipe DataConstraintKeterangan
idUUIDPRIMARY KEY, DEFAULT gen_random_uuid()Identifikator unik penawaran
investment_idUUIDREFERENCES village_crowdfunding_investments(id) ON DELETE CASCADERujukan investasi induk penjual
seller_idUUIDREFERENCES auth.users(id) ON DELETE RESTRICTID pengguna penjual
campaign_idUUIDREFERENCES village_crowdfunding_campaigns(id) ON DELETE CASCADERujukan proyek desa yang diperdagangkan
shares_offeredINTNOT NULL, CHECK (shares_offered > 0)Jumlah lembar saham yang ditawarkan
price_per_shareDECIMAL(15,2)NOT NULL, CHECK (price_per_share > 0)Harga per lembar saham
statusTEXTDEFAULT 'active', CHECK IN ('active', 'sold', 'canceled')Status penawaran bursa
created_atTIMESTAMPTZDEFAULT now()Waktu listing dibuat
updated_atTIMESTAMPTZDEFAULT now()Waktu perubahan data

2.2. Tabel village_crowdfunding_secondary_transactions

Menyimpan mutasi pembelian, bukti transfer escrow, serta hasil audit kliring:

KolomTipe DataConstraintKeterangan
idUUIDPRIMARY KEY, DEFAULT gen_random_uuid()Identifikator unik transaksi
listing_idUUIDREFERENCES village_crowdfunding_secondary_listings(id) ON DELETE CASCADERujukan ke penawaran bursa
buyer_idUUIDREFERENCES auth.users(id) ON DELETE RESTRICTID pengguna pembeli
shares_boughtINTNOT NULL, CHECK (shares_bought > 0)Jumlah lembar saham yang dibeli
price_per_shareDECIMAL(15,2)NOT NULL, CHECK (price_per_share > 0)Harga per lembar yang disepakati
total_amountDECIMAL(15,2)NOT NULL, CHECK (total_amount > 0)Total dana bruto yang disetor pembeli
admin_feeDECIMAL(15,2)NOT NULL, DEFAULT 0Fee platform BUMDes (3% dari total)
seller_net_amountDECIMAL(15,2)NOT NULL, DEFAULT 0Hak bersih penjual (97% dari total)
payment_proof_urlTEXTNULLABLETautan berkas bukti transfer di Supabase Storage
statusTEXTDEFAULT 'pending', CHECK IN ('pending', 'verified', 'rejected')Status verifikasi kliring
verified_byUUIDREFERENCES auth.users(id)ID admin yang memverifikasi
verified_atTIMESTAMPTZNULLABLEWaktu verifikasi dieksekusi
created_atTIMESTAMPTZDEFAULT now()Waktu transaksi diajukan

3. Keamanan Tingkat Basis Data (Row Level Security - RLS)#

Semua tabel bursa dilindungi oleh RLS aktif:

sql
-- Kebijakan Akses Listing
CREATE POLICY "Public can view active listings" 
 ON public.village_crowdfunding_secondary_listings FOR SELECT 
 USING (status = 'active');

CREATE POLICY "Users can create listings" 
 ON public.village_crowdfunding_secondary_listings FOR INSERT 
 WITH CHECK (auth.uid() = seller_id);

CREATE POLICY "Admin can view all listings" 
 ON public.village_crowdfunding_secondary_listings FOR ALL 
 USING (auth.uid() IN (SELECT id FROM profiles WHERE role IN ('super_admin', 'admin_unit', 'admin_media')));

-- Kebijakan Akses Transaksi
CREATE POLICY "Users can view own purchases" 
 ON public.village_crowdfunding_secondary_transactions FOR SELECT 
 USING (auth.uid() = buyer_id);

CREATE POLICY "Admin can manage all secondary transactions" 
 ON public.village_crowdfunding_secondary_transactions FOR ALL 
 USING (auth.uid() IN (SELECT id FROM profiles WHERE role IN ('super_admin', 'admin_unit', 'admin_media')));

4. Struktur Server Actions (lib/actions/secondary-market.ts)#

Fungsi Server ActionRole OtorisasiDeskripsi Operasi
getActiveSecondaryListings()PublikMengambil seluruh penawaran aktif dengan relasi data proyek dan foto aset.
getSecondaryListingDetail(id)PublikMengambil detail spesifik satu listing untuk halaman checkout pembelian.
buySecondaryListing(data)Autentikasi WargaMemvalidasi kuota lot yang tersedia, menghitung fee 3%, dan menyimpan data pembelian.
createSecondaryListing(data)Autentikasi PenjualMemverifikasi kepemilikan lembar saham dan mengunci kuota agar tidak terjual ganda.
cancelSecondaryListing(id)Pemilik Listing / AdminMembatalkan penawaran dan mengembalikan kuota saham ke status aktif di portofolio.
verifySecondaryTransaction(id)super_admin, admin_unitMengeksekusi pemindahan saham, pencatatan jurnal SAK EP, dan audit log secara atomic.
rejectSecondaryTransaction(id, reason)super_admin, admin_unitMenolak transaksi dan mengembalikan kuota saham ke penjual.
getSecondaryMarketStats()AdminMengagregasi 4 KPI: Volume Transaksi, Nilai Perdagangan, Escrow Pending, dan Pendapatan Fee.
getAllSecondaryTransactions()AdminMengambil riwayat seluruh mutasi kliring pasar sekunder.
getAllSecondaryListingsAdmin()AdminPengawasan pasar menyeluruh terhadap listing aktif, terjual, maupun dibatalkan.

5. Standarisasi Integrasi Jurnal SAK EP#

Penyelesaian kliring di verifySecondaryTransaction terintegrasi secara otomatis dengan Buku Besar menggunakan akun resmi:

  • 1-102.05: Bank BUMDes - Rekening Escrow Saham Desa (dengan fallback bertingkat ke 1-102 lalu 1-101).
  • 4-305: Pendapatan Jasa Platform Bursa & Escrow (dengan fallback ke 4-105).

Integrasi ini menjamin pembukuan akuntansi yang taat asas (compliant), tidak tumpang tindih dengan omzet unit perdagangan/ritel, serta selalu seimbang (balanced debit-kredit).

Apakah panduan ini membantu Anda?