// ENTER CREDENTIALS TO CONTINUE
ACCESS DENIED — Invalid credentials
// SYSTEM ARCHITECTURE v1.0 // TECHNICAL DOCS MARCH 2026
| Layer | Technology | Purpose |
|---|---|---|
| Static Hosting | GitHub Pages | Serve HTML/CSS/JS — auto-deploy on push to repo |
| Domain | popsorte.vip (CNAME) | Custom domain pointing to GitHub Pages |
| Backend API | Cloudflare Workers ×2 | Member API + Agent Bulk API (edge serverless) |
| Data Storage | Google Sheets ×15+ | Primary database — CSV export for frontend, Sheets API for writes |
| Data Processing | Google Apps Script ×2 | Member ticket handler + Agent bulk writer (doGet/doPost endpoints) |
| KV Store | Cloudflare KV | AGENT_TOKENS — 30-min session tokens for bulk registration |
| Queue | Cloudflare Queue | popsorte-bulk-queue — async batch ticket writes + DLQ |
| Notifications | Telegram Bot API | Kirim notifikasi setiap ada ticket baru masuk |
| Future Target | Supabase PostgreSQL | Planned migration dari Google Sheets (see Tab 9) |
| Platform | System ID | Code (Worker) | Lottery | Prize Pool |
|---|---|---|---|---|
| POPN1 | 1123 | 800 | Quina (5 from 1-80) | R$900 |
| POPLUZ | 1125 | 723 | Quina (5 from 1-80) | R$900 |
| POPZOE | 1128 | 2382 | Quina (5 from 1-80) | R$900 |
| POPSUR | N/A | N/A | Quina (5 from 1-80) | R$900 |
| POPBEA | N/A | N/A | Quina (5 from 1-80) | R$900 |
| POPFOI | 1135 | 2470 | Quina (5 from 1-80) | R$900 |
POPSUR dan POPBEA sudah aktif di frontend publik, VLD, agent lookup, dan validasi admin. POPFOI adalah platform ke-6 (Sistem ID: 1135, Code: 2470). Nilai System ID / worker code untuk POPSUR dan POPBEA belum terdokumentasi secara eksplisit pada snapshot repo saat ini.
Draw schedule: Senin–Sabtu (no draw on Sundays). Cutoff: 20:00 BRT (normally), 17:00 BRT on Dec 24/31. No draw: Dec 25, Jan 1.
| Service | URL |
|---|---|
| Website (Public) | https://popsorte.vip |
| Agent Page | https://popsorte.vip/agente |
| Admin Dashboard | https://popsorte.vip/admin (geo-blocked) |
| Worker — Member API | https://popsorte-api.danilla-vargas1923.workers.dev |
| Worker — Agent API | https://popsorte-agent.danilla-vargas1923.workers.dev |
| Worker — Staging | https://popsorte-staging.danilla-vargas1923.workers.dev |
Deployed: popsorte-api.danilla-vargas1923.workers.dev 724 lines export default { fetch }
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | / or /health | Public | Health check — returns version, timestamp |
| POST | /api/tickets/submit/api/tickets/create | Public | Submit lottery ticket → write to BACKUP_SHEET via Sheets API → send Telegram notification |
| POST | /api/auth/login | Public | Admin login — validate against ADMIN_ACCOUNTS_JSON secret + auth CSV fallback → return base64 token (12h TTL) |
| GET | /api/admin/entries | Skip* | Fetch all SORTE entries from PRIVATE_SHEET via Sheets API → CSV |
| GET | /api/admin/results | Skip* | Fetch results CSV from PUBLIC_SHEET |
| GET | /api/admin/recharge | Admin | Fetch + combine recharge CSVs from 5 platform sheets |
| GET | /api/admin/sorte | Admin | Fetch SORTE tab via Sheets API (JSON or CSV) |
| POST | /api/admin/winners-summary | Admin | Proxy to GAS getWinnersSummary action with retry (2×) |
| POST | /api/admin/cache/clear | Admin | Purge Cloudflare Cache API entries |
| OPTIONS | * | Public | CORS preflight handler |
* /api/admin/entries dan /api/admin/results skip auth check dalam code (noted in source).
spreadsheets. Token di-cache in-memory per isolate dengan 60s safety margin. Kalau expired (401), auto-clear cache dan retry 1×.BACKUP_SHEET_ID (bukan primary!) via Sheets API append method.caches.default (Cloudflare Cache API) dengan TTL 30–60 detik.retryWithBackoff — 2 attempts pada 401 token expiry; clear token cache lalu retry.Deployed: popsorte-agent.danilla-vargas1923.workers.dev 531 lines export default { fetch, queue }
Config file: wrangler-agent.toml
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | / or /health | Public | Health check |
| POST | /api/agent/token/create | Public | Create 30-min session token di KV — validasi remaining tickets dari Sheet B |
| GET | /api/agent/token/validate | Token | Validate KV token + return live remaining count |
| POST | /api/agent/bulk/generate | Token | Generate batch tickets (5/25/50/100), enqueue untuk Sheet write via Queue |
BULK_QUEUEAGENT_APPS_SCRIPT_URL dengan { action: "saveBulkBilhetes", tickets: [...] }popsorte-bulk-dlq)| Binding | Type | ID / Name |
|---|---|---|
AGENT_TOKENS | KV Namespace | 717d1e5b950049b98b8ea1ccbb414641 |
BULK_QUEUE | Queue Producer | popsorte-bulk-queue |
| (consumer) | Queue Consumer | max_batch=10, max_retries=3, DLQ=popsorte-bulk-dlq |
AGT{last3GameId}-{PlatformShort}{Concurso}-{3random}-{day}{monthInitial}1tt2XYr1...) via public gviz CSV, column DNilai CONCURSO_REFERENCE = { number: 6955, date: '2026-02-18' } HARUS sama persis di pop-sorte.js DAN worker-agent.js. Kalau beda → concurso number salah → tiket masuk ke draw yang salah. Update kedua file bersamaan!
Target Sheet: 1mcOH3L0w_Gq_si3BhTOw7OcAHqerRlPoI2JEkFvWUvE (NEW POP SORTE, tab SORTE)
Backup Sheet: 1h5yMB5CliN9ITkwcG3vrzySb963Rq9hlzACffOlSZyk (auto-failover)
| Action | Trigger | Description |
|---|---|---|
saveAndGetBilhete | doGet | Validate fields → appendRow() ke primary sheet → auto-failover ke backup sheet kalau primary penuh (900K rows) atau error. Returns bilhete data as JSON. |
getWinnersSummary | doGet | Read dari BOTH primary + backup sheets, filter by drawDate/concurso, match numbers, hitung per-platform winners (R$900 pool masing-masing) |
| (legacy) | doPost | Direct append ke primary sheet (legacy, masih aktif) |
| Column | A | B | C | D | E | F | G | H | I |
|---|---|---|---|---|---|---|---|---|---|
| Field | Data/Hora | Platform | Game ID | Números | Data Sorteio | Concurso | Bilhete # | Status |
Kalau primary sheet punya ≥ 900,000 rows atau appendRow() throws error → otomatis failover ke BACKUP_SHEET_ID. Ini silent — tidak ada alert. Cek kedua sheet kalau data "hilang".
Target Sheet: 1neXG1rE-3i9MuIrE_v9eF8_mqKF3hz7btc7Q7mvCej8 (Bulk Agent Registrations, tab SORTE)
| Action | Trigger | Description |
|---|---|---|
| Health check | doGet | Returns status OK |
saveBulkBilhetes | doPost | Receive ticket array dari Queue consumer → appendRow() setiap tiket ke bulk sheet. Same column mapping (A–I). |
https://script.google.com/macros/s/{DEPLOYMENT_ID}/execAPPS_SCRIPT_URL (member) atau AGENT_APPS_SCRIPT_URL (agent)| Sheet Name | Sheet ID | Access | Writer | Reader |
|---|---|---|---|---|
| NEW POP SORTE Database master semua tiket | 1mcOH3L0w_Gq_si3BhTOw7OcAHqerRlPoI2JEkFvWUvE | Service Account | GAS (member), Manual (validasi status) | IMPORTRANGE → OLD POP SORTE |
| Backup Sheet Failover write target | 1h5yMB5CliN9ITkwcG3vrzySb963Rq9hlzACffOlSZyk | Service Account | Worker (Sheets API append), GAS failover | GAS (getWinnersSummary) |
| OLD POP SORTE Display di website (WA masked) | 1OttNYHiecAuGG6IRX7lW6lkG5ciEcL8gp3g6lNrN9H8 | Public Export | IMPORTRANGE (auto) | Frontend CSV fetch (VLD ALL) |
| WA NO Masking Sistem hide nomor WhatsApp | 1Reh89WBwn34tEMw60wUe6QU-IcNWg23mqpDrmrMFiro | IMPORTRANGE | Formula auto | IMPORTRANGE → OLD POP SORTE |
| DATA PARTICIPANT Admin export untuk validasi | 1PDu2_XMnAOIg2Xfb7l6z1qkGbiws00MuRrGO5OhmwX4 | Manual | Manual paste (dari admin CSV export) | Sheet4 formula (status matching) |
| Sheet Name | Sheet ID | Access | CSV Schema |
|---|---|---|---|
| Recharge POPN1 | 1c6gnCngs2wFOvVayd5XpM9D3LOlKUxtSjl7gfszXcMg | Public Export | DATE | TIME | Member ID | Order Number | Change Amount | Balance After |
| Recharge POPLUZ | 12GcjRtG23ro4aQ5N-Psh9G0lr0dZ2-qS6C129gGEoQo | Public Export | |
| Recharge POPZOE | 1teEHuYWSCK89Fd1nAWu8b9rosE7F87aetnVnxBdquAg | Public Export | |
| Recharge POPSUR | 184VgNiY76XEJ3Mb_8wQnDU34NWRDEYIQTXcGFowpvlM | Public Export | |
| Recharge POPBEA | 1mARjkx55oNcTI_vMvJxRqrwasw31g9Xz49vDUrJewJs | Public Export | |
| Recharge POPFOI | 1kwwyXcoUSxS1FF4h68Y2EbJbR_s68QIowiEszRZvjQM | Public Export | |
| N1 ADMIN | 1KcIhrL3EvgdkgHAD-5E2jSK2W1mEZlJ9-D5DBGdxXRU | IMPORTRANGE | IMPORTRANGE dari Recharge sheets. Monitoring data per-platform. |
| LUZ ADMIN | 1H68xaO7xjR-o7ECklQT1oZkT7lkMj5FNydq3nVPimgM | IMPORTRANGE | |
| ZOE ADMIN | 1teEHuYWSCK89Fd1nAWu8b9rosE7F87aetnVnxBdquAg | IMPORTRANGE |
POPSUR, POPBEA dan POPFOI saat ini tervalidasi langsung dari recharge sheet dedicated. Tidak ada separate admin mirror sheet yang terdokumentasi di repo snapshot ini.
| Sheet Name | Sheet ID | Purpose |
|---|---|---|
| Agent Display | 1iY9CeUKilVSftMYRjeEZ1yvDhw3DdqdD1UfBHCyJF6I | Public display card data per platform. Read by agente/index.html via gviz CSV. |
| Agent Ticket Tracking READ ONLY | 1tt2XYr1_PY4MYKRFYEhH3yIZIVvqy_uRI_tjU0uAVpQ | Col A: Game ID, Col B: total tickets, Col C: registered (formula), Col D: remaining (B−C). Worker reads col D. |
| Bulk Agent Registrations | 1neXG1rE-3i9MuIrE_v9eF8_mqKF3hz7btc7Q7mvCej8 | Tab SORTE — setiap bulk generate masuk sini via GAS agent. Platform GIDs: POPN1=0, POPLUZ=1439646488, POPZOE=1494410655, POPSUR=2068012496, POPBEA=391959141, POPFOI=89902206 |
| Historical Data | 1xoThWSyoqskJGx1z_f8R3MGQC6CXCg0Ebvsqsho8nKM | Archive agent referrals. Tabs: DATA INDICATE, ALL ID REPORTED per platform. |
| FREE BILHETE | 1TmgN6eMw-SxATY3wnvVrLp14SbiKfCDdMX-HYy8u2A0 | Data event join group (bot @Popsortebot). Diproses manual lalu dipaste ke Recharge Sheet. |
| Sheet Name | Sheet ID | Usage |
|---|---|---|
| VLD per-platform | 1b_VAYANY_XUsO0... | Pages: luz.html, n1.html, zoe.html, sur.html, bea.html. Sources: sheet=LUZ, sheet=N1, gid=1985526052, gid=13116568, gid=1337969693. |
| VLD ALL | 1OttNYHiecAuGG6... | Combined export (gid=0) — used by index.html VLD with platform selector |
| Auth Credentials | 1PK0qI9PRWaleD6... | Admin login CSV (gid=1360466037) — fetched by auth.js in browser + Worker fallback |
| Old Dashboard Data | 14f_ipSqAq8KCP7... | Entries (gid=0) + Results (gid=1178367669) — used by DATA ADMIN DASHBOARD LAMA |
| Historical Analytics | 1a1M0fAAao7s28V... | Monthly sheets (Dec25-Mar26) — used by script.js historical data section |
Tiket dianggap VALID jika: Platform + Game ID cocok dengan recharge data, recharge terjadi dalam 2-day eligible window sebelum draw, sebelum cutoff time, dan 1 recharge hanya bisa bind ke 1 tiket.
| Lines | Block | Purpose |
|---|---|---|
1–45 | Config + Retry | API_BASE_URL, retryWithBackoff() — exponential backoff, 3 attempts (1s/2s/3s delays) |
48–330 | Concurso/Date System | CONCURSO_REFERENCE = { number: 6955, date: '2026-02-18' }. Brazil timezone (BRT UTC-3) helpers, draw schedule calculation, concurso number computation, holiday handling. |
375–880 | Results Display | fetchAndPopulateResults() — fetch results CSV from public sheet, render draw results + winners announcements. |
882–1050 | CSV Parsing + Latest 5 | CSV parsing helpers, initLatestFiveWidget() — shows 5 most recent tickets. |
1053–1350 | Form/UI Logic | Game ID validation (10 digits), WhatsApp validation/masking, platform radio select, number grid (1–80 balls), "Surpresinha" random pick, submit button state. |
1350–1650 | Submission Flow | confirmEntry() — validates everything, POSTs to Worker, handles retries, redirects to bilhete.html with URL params. |
1650–1750 | Countdown Timer | initCountdown() — live countdown ke draw cutoff (20:00 BRT, 17:00 on Dec 24/31). |
1750–2050 | VLD Ticket Consultation (IIFE) | Self-contained: fetch per-platform Google Sheet CSVs, render card grid, search (Game ID / Ticket ID), filter (All/Valid/Invalid/Pending), pagination, auto-refresh every 30s. |
2050–2160 | Mobile Nav (IIFE) | Bottom navigation — switches sections: home / rules / search / help / popluz. References .vld-section class. |
2160–2230 | POPLUZ Slider (IIFE) | Image slider with dots, auto-advance interval. |
2230–2300 | Anti-Debugging (IIFE) | Blocks F12, right-click, Ctrl+Shift+I. Secret override: hold Q+2 for 2 seconds |
:root — purple/cyan/pink/gold theme.vc-* classes), POPLUZ template sections, toast notifications, popups, bilhete display| File | Lines | Purpose | Key Features |
|---|---|---|---|
| index.html | ~2,548 | Main lottery page (all platforms) | Platform selector, number picker, VLD with 5 platform pills, POPLUZ design section |
| luz.html | ~1,576 | POPLUZ-specific | "Golden Gala" theme, VLD filtered to POPLUZ only |
| n1.html | ~1,575 | POPN1-specific | Same Golden Gala theme, VLD filtered to POPN1 |
| zoe.html | ~1,577 | POPZOE-specific | Same theme, VLD filtered to POPZOE |
| sur.html | ~1,577 | POPSUR-specific | Same theme, VLD filtered to POPSUR |
| bea.html | ~1,577 | POPBEA-specific | Same theme, VLD filtered to POPBEA |
| foi.html | ~1,577 | POPFOI-specific | Same theme, VLD filtered to POPFOI |
| bilhete.html | ~948 | Ticket confirmation | Read URL params, display visual, html2canvas → auto-download PNG, countdown |
| 404.html | ~221 | 404 / geo-block landing | Animated purple background, "page not found" |
| agente/index.html | ~1,353 | Agent lookup | Input Game ID + platform, fetch 2 sheets, show bulk button if ≥5 remaining |
| agente/bulk.html | ~992 | Agent bulk registration | Token-gated, WhatsApp input, batch selector (5/25/50/100), generate + auto-download PNG |
| admin/index.html | ~1,103 | Admin dashboard | Sidebar nav, Chart.js, geo-blocked, entries/results/winners/historical sections |
| admin/login.html | ~63 | Admin login | Fetch credentials from auth sheet CSV, set sessionStorage |
| Page | CSV Source | Purpose |
|---|---|---|
| index.html (VLD) | 1OttNYHiecAuGG6.../export?format=csv&gid=0 | All platforms combined — VLD with platform selector |
| luz.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&sheet=LUZ | POPLUZ entries only |
| n1.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&sheet=N1 | POPN1 entries only |
| zoe.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&gid=1985526052 | POPZOE entries only |
| sur.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&gid=13116568 | POPSUR entries only |
| bea.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&gid=1337969693 | POPBEA entries only |
| foi.html (VLD) | 1b_VAYANY_.../gviz/tq?tqx=out:csv&gid=1814105408 | POPFOI entries only |
| Results section | Via Worker API or PUBLIC_SHEET (gid=300277644) | Draw results display |
| agente/index.html | Sheet A (1iY9CeUKilVSftM...) + Sheet B (1tt2XYr1...) | Agent daily info + remaining tickets |
| Property | Value |
|---|---|
| Login endpoint | POST /api/auth/login (worker-member.js) |
| Credential sources | 1. ADMIN_ACCOUNTS_JSON secret (primary)2. Auth Sheet CSV fallback ( AUTH_SHEET_ID) |
| Token format | btoa(account + ":" + timestamp) — base64 encoded |
| TTL | 12 hours |
| Transport | Authorization: Bearer <token> header |
| Validation logic | Decode token → extract account name → check exists → check age < 12h |
| Property | Value |
|---|---|
| Login page | admin/login.html |
| Credential source | Auth Sheet CSV fetched directly in browser (1PK0qI9PRWaleD6... gid=1360466037) |
| Session storage | sessionStorage key: ps_admin_session |
| TTL | 12 hours (checked by ensureAuthenticated()) |
| Security note | CLIENT-SIDE ONLY — no server-side session. Credentials are in a public Google Sheet. |
| Property | Value |
|---|---|
| Create endpoint | POST /api/agent/token/create |
| Token format | UUID v4 — stored in Cloudflare KV namespace AGENT_TOKENS |
| TTL | 30 minutes |
| KV value | { gameId, platform, remaining, createdAt } |
| Transport | URL query param or request body |
| On expiry | KV returns null → 401 → redirect to agente/index.html |
File: geo-block.js (loaded by admin dashboard)
API: ipapi.co — lookup visitor IP → get country code
Blocked countries:
Purpose: Admin dashboard is geo-blocked dari South America. Hanya bisa diakses dari luar (contoh: Asia).
Defined in pop-sorte.js (IIFE, lines ~2230–2300):
| Variable | Type | Value / Purpose |
|---|---|---|
PRIVATE_SHEET_ID | env | 1mcOH3L... — Primary entries sheet (NEW POP SORTE) |
BACKUP_SHEET_ID | env | 1h5yMB5... — Backup sheet (WRITE TARGET for tickets) |
PUBLIC_SHEET_ID | env | 1yy-G41... — Public data (entries CSV gid=0, results CSV gid=300277644) |
AUTH_SHEET_ID | env | 1PK0qI9... — Admin credentials CSV (gid=1360466037) |
RECHARGE_SHEET_ID | env | 1c6gnCn... — POPN1 recharge |
RECHARGE_POPLUZ_SHEET_ID | env | 12GcjRt... — POPLUZ recharge |
RECHARGE_POPZOE_SHEET_ID | env | 1teEHuY... — POPZOE recharge |
RECHARGE_POPSUR_SHEET_ID | env | 184VgNi... — POPSUR recharge |
RECHARGE_POPBEA_SHEET_ID | env | 1mARjkx... — POPBEA recharge |
RECHARGE_POPFOI_SHEET_ID | env | 1kwwyXc... — POPFOI recharge |
APPS_SCRIPT_URL | env | GAS Web App URL for member handler |
TG_CHAT_ID | env/secret | Telegram chat ID for notifications |
| Secret | Format | Purpose |
|---|---|---|
GSERVICE_ACCOUNT_JSON | JSON string | Google Service Account credentials — used for JWT → OAuth2 token. Must have Sheets API scope. If missing → error "Service account JSON missing" |
ADMIN_ACCOUNTS_JSON | JSON string{"admin":"pass"} | Admin login credentials. Parsed as object: key = account name, value = password. |
TG_BOT_TOKEN | String | Telegram Bot API token for sending notifications |
Config defined in wrangler-agent.toml:
| Variable / Binding | Type | Value / Purpose |
|---|---|---|
AGENT_TOKENS | KV Namespace | ID: 717d1e5b950049b98b8ea1ccbb414641 |
BULK_QUEUE | Queue Producer | Name: popsorte-bulk-queue |
AGENT_APPS_SCRIPT_URL | Secret | GAS Web App URL for agent bulk writer. Set via wrangler secret put |
Kalau CONCURSO_REFERENCE berbeda antara pop-sorte.js dan worker-agent.js, tiket member dan tiket agent akan punya concurso number yang berbeda → masuk draw yang salah.
Where: worker-member.js — any Sheets API call
Cause: Cached OAuth2 token expired (usually after ~1 hour)
Auto-remedy: Worker auto-clears token cache and retries once with fresh JWT
If persists:
GSERVICE_ACCOUNT_JSON secret — is it valid JSON?Where: worker-member.js — startup / first Sheets API call
Cause: GSERVICE_ACCOUNT_JSON secret not set or empty
Fix:
Where: google-apps-script.gs — saveAndGetBilhete
Trigger: Primary sheet has ≥ 900,000 rows OR appendRow() throws error
Behavior: Silent failover — writes to BACKUP_SHEET_ID instead. No alert, no log visible to user.
How to detect:
getWinnersSummary reads BOTH sheets — jadi winner calc masih benarWhere: worker-member.js — /api/admin/winners-summary route
Cause: GAS execution timeout (max 6 min per execution) or server error
Auto-remedy: Worker retries 2× with 1.5s delay between attempts
If persists:
Where: worker-agent.js — /api/agent/bulk/generate or /api/agent/token/validate
Cause: KV token has expired (TTL: 30 min) or token UUID not found in KV
Fix: Agent harus kembali ke /agente/, re-enter Game ID, dan create token baru
Where: worker-agent.js — queue handler
Cause: GAS agent endpoint down, network error, or GAS quota exceeded
Behavior: message.retry() up to 3× → after 3 failures → message goes to popsorte-bulk-dlq
How to check:
Where: pop-sorte.js — confirmEntry()
Behavior: retryWithBackoff() — 3 attempts with 1s/2s/3s delays
If all 3 fail: Error toast shown to user
Debug:
GET /healthWhere: pop-sorte.js + worker-agent.js — concurso date calculation
Behavior: Auto-skips to next valid draw day (searches up to 14 days ahead)
No-draw days: Sundays, December 25, January 1
Special cutoffs: December 24 and 31 → cutoff at 17:00 BRT (instead of 20:00)
If concurso seems wrong: Check CONCURSO_REFERENCE value in both files — must be identical.
Where: Any sheet that uses IMPORTRANGE (OLD POP SORTE, Admin Sheets, etc.)
Causes:
Fix:
=IMPORTRANGE("URL", "range")Common causes:
node --check worker-member.jswrangler loginwrangler whoamiwrangler kv:namespace listMigration ke Supabase PostgreSQL sudah di-design tapi belum diimplementasi. Dokumentasi lengkap ada di GUIDELINES/PLAN MIGRASI.md (~2,345 lines) dan GUIDELINES/POPSORTE_DATABASE_SCHEMA.md (~943 lines).
| Table | Purpose | Key Columns |
|---|---|---|
platforms | Platform master data | id, name (POPN1/POPLUZ/POPZOE/POPSUR/POPBEA/POPFOI), system_id, code, prize_pool |
tickets | All lottery tickets | id, platform_id, game_id, whatsapp, numbers[], draw_date, concurso, bilhete_number, status, created_at |
recharges | Recharge/deposit records | id, platform_id, member_id, order_number, amount, balance_after, record_time |
draws | Draw results | id, concurso, draw_date, winning_numbers[], created_at |
winners | Calculated winners | id, ticket_id, draw_id, matching_numbers, tier, prize_amount |
concurso_calendar | Draw schedule | concurso_number, draw_date, is_active |
holidays | Configurable no-draw dates | date, description, special_cutoff |
validation_logs | Audit trail | id, ticket_id, recharge_id, result, validated_at |
Tiket dianggap VALID kalau memenuhi SEMUA kondisi:
| Acertos | Tier | Prize |
|---|---|---|
| 5 angka benar | GRAND PRIZE | Full pool R$900 (split if multiple winners) |
| 4 angka benar | 2nd Prize | Pool R$900 (only if no 5-acerto winner) |
| 3 angka benar | 3rd Prize | Pool R$900 (cascading down) |
| 2 angka benar | Consolation | Pool R$900 (cascading down) |
| 1 atau 0 | No Prize | — |
Cascading logic: Prize pool R$900 per platform. Awarded to winners at the highest matching tier. If 2 orang sama-sama 5 acertos → split R$900/2 = R$450 each. Kalau tidak ada 5 acertos → cek 4 acertos, dst.
holidays table replaces hardcoded Dec 25 / Jan 1 checksvalidation_logs table untuk debugging validation issues