Skip to content

Kehn-Marv/Scrowpay

Repository files navigation

ScrowPay

AI-powered escrow for Nigerian peer-to-peer commerce.

Money sits in a holding account until both sides honour the deal. Risk stack: deterministic rules + Isolation Forest feed an umbrella score; in the current dashboard build the composite run is post-fund (non-blocking) and updates trust β€” see FRAUD_DETECTION_FLOW.md. Escrow releases and payouts still go through Squad-backed flows.

Built for Stack License

Problem Β· Solution Β· Squad APIs Β· AI / Data Β· User Flow Β· Quick Start Β· Impact Β· Docs


The Problem

Who is suffering? Millions of Nigerians buying and selling on WhatsApp, Twitter/X, Instagram, Jiji, and Facebook Marketplace. These platforms have no built-in payment protection.

Why it matters right now:

  • Nigeria's informal e-commerce is estimated at over $10 billion annually, and the majority of peer-to-peer transactions happen outside regulated marketplaces.
  • 1 in 3 online buyers in Nigeria has experienced a failed delivery or outright scam (NOIPolls / EFInA surveys).
  • Sellers face the reverse problem β€” chargebacks and fake "item not received" claims with no neutral arbiter.
  • Existing escrow solutions are either bank-grade (minimum ₦5M, corporate-only) or informal (a friend holds the cash). There is no consumer-grade, AI-protected escrow for everyday transactions.

The trust gap costs the ecosystem real money. Buyers overpay for "safe" channels (COD markups, marketplace fees), sellers lose honest customers who refuse to pay first, and repeat commerce never forms.


Target User

Tunde, 27, Lagos. Sells refurbished phones on Twitter/X. His buyers want delivery before payment; he wants payment before shipping. Neither side trusts the other. He loses ~3 deals per week to trust breakdown. ScrowPay lets both sides lock funds in escrow, verify each other's identity and reputation, and complete the deal with zero risk.

Amaka, 24, Abuja. Buys fashion items from Instagram vendors. She has been scammed twice β€” paid via bank transfer, got blocked. She now only buys from sellers who accept ScrowPay because she can see their Trust Score and knows her money is locked until she confirms delivery.

These are the thousands of everyday Nigerian buyers and sellers who trade informally online and need a simple, trustworthy middle-ground.


Solution Overview

ScrowPay is a peer-to-peer escrow platform where money is held in a Squad virtual account until both sides honour the deal. Around that core payment flow, we layer AI to prevent fraud before it happens and resolve disputes automatically when it does.

How it works in one sentence: Seller lists, buyer funds (money goes to a holding account via Squad), goods are delivered, buyer confirms, money releases to seller β€” and AI watches every step.

What we built

Layer What it does
Escrow engine State machine over Created β†’ Funded_Locked β†’ In_Transit β†’ (Completed | Disputed) plus Cancelled / Refunded terminal paths in the DB schema and TransactionService
Squad payment rails Virtual accounts for every user + a central holding account + NIP transfers for payouts
AI risk scoring AnomalyDetectionEngine: RiskEngineService (rules) + IsolationForestService (Flask Isolation Forest). Wired today: evaluate() runs after a successful fund and feeds TrustEngineService.onAnomalyEvaluated β€” non-blocking. Product goal: optional pre-fund gate.
AI dispute agent Gemini 2.0 Flash multimodal agent that reads complaints + photo evidence and issues binding verdicts
Face re-verification Gemini-powered face match for high-risk actions (large withdrawals) against signup reference photo
Trust engine Dynamic 0-100 reputation score with tiers, history, and Instant Release eligibility
Admin console Moderation dashboard for manual dispute review, face audit, and risky transaction monitoring

Squad API Integration

Squad is the complete financial backbone of ScrowPay. We use three product lines across eight API endpoints:

1. Virtual Account API β€” Identity + Funding

Method Endpoint What it does in ScrowPay
POST /virtual-account Creates a real NUBAN for every user. Squad validates BVN against NIBSS (name, DOB, gender, phone) β€” giving us bank-grade KYC at zero extra cost.
GET /virtual-account/customer/{id} Retrieves account details and balance for the dashboard.
GET /virtual-account/{identifier} Looks up a user by their unique identifier.

Why it's central: Every user gets a real Nigerian bank account. Buyers fund escrow by transferring Naira to the holding virtual account. No virtual accounts = no money movement = no product.

2. Transfer / Payout API β€” Escrow Release + Withdrawals

Method Endpoint What it does in ScrowPay
POST /payout/account/lookup Verifies destination bank + account name before any transfer β€” prevents misdirected payments.
POST /payout/transfer Releases escrow funds to the seller (or refunds the buyer) via NIP instant transfer.
POST /payout/requery Re-queries transfer status for confirmation β€” ensures idempotency.
GET /merchant/balance Fetches the Squad Merchant Ledger balance in real-time (30s polling).
GET /payout/list Lists all historical transfers for audit.

Why it's central: Every escrow release, every refund, every withdrawal flows through these endpoints. Supports 21+ Nigerian banks.

3. Merchant Balance API β€” Real-Time Dashboard

The dashboard polls Squad every 30 seconds for the live available balance, combining it with locked-in-escrow amounts from the database to show users their complete financial picture.

Implementation files:

  • frontend/squad-api-service.js β€” Virtual Account API client
  • frontend/SquadTransferService.js β€” Transfer/Payout API client
  • frontend/BalanceService.js β€” Balance orchestrator with 30s TTL cache

Full technical deep-dive: docs/SQUAD_API_INTEGRATION.md


AI / Data Intelligence

ScrowPay addresses two AI pillars: Fraud Prevention and Automated Dispute Resolution.

Pillar 1: Fraud / anomaly detection (rules + ML)

  1. RiskEngineService β€” deterministic, in-browser rules (counterparty, amount, time-of-day, etc.). No Gemini in this path today (legacy listing check was removed).
  2. IsolationForestService β€” browser client β†’ Python ai-engine Isolation Forest /api/v1/score.

Current product wiring (dashboard.html): after a buyer successfully funds, anomalyEngine.evaluate() runs in the background and trustEngine.onAnomalyEvaluated ingests the composite score. Funding itself is not blocked by this call in the present implementation (see FRAUD_DETECTION_FLOW.md). Verdicts and features are still logged for audit when the pipeline runs.

Design thresholds (umbrella): see AnomalyDetectionEngine.js (BLOCK_THRESHOLD / REVIEW_THRESHOLD) β€” distinct from any copy that still mentions β€œ>80 block” from older marketing drafts.

Pillar 2: Multimodal Dispute Resolution

When a buyer raises a dispute:

  1. The Gemini 2.0 Flash agent receives: transaction context, free-text complaint, up to 4 uploaded photos
  2. It returns a structured JSON verdict: favoredParty, confidence, payout, reasoning, evidenceCited
  3. High-confidence verdicts (>90%) auto-execute. Lower confidence routes to the admin console for human review.

Additional AI Features

  • Face re-verification β€” High-value withdrawals: Gemini multimodal compare vs. optional Cloudinary signup reference (FaceVerificationService)
  • Device fingerprint β€” DeviceFingerprintService (FingerprintJS) supplies visitor ID for ML features + audit; not a separate β€œbehavioural model” layer in AnomalyDetectionEngine v2.1 (behavioral_score is null)
  • Trust Score engine β€” TrustEngineService: deterministic 0–100 score from counters + last_anomaly_score; tiers Low / Building / Trusted / Elite; marketing copy on web.html maps these to three plain-language bands

Full technical deep-dive: docs/AI_INTELLIGENCE.md


User Flow

A step-by-step walkthrough of the complete product experience:

Account Creation (10 stages)

  1. Phone + email
  2. Email OTP (6-digit; demo fallback 123456 when engine unreachable)
  3. BVN
  4. Name + gender + DOB (must match BVN / NIBSS)
  5. Squad virtual account creation (NUBAN)
  6. Face intro
  7. Blink liveness (MediaPipe) + optional Cloudinary reference upload
  8. Address (state β†’ LGA β†’ ward)
  9. 6-digit PIN (hashed via PINService)
  10. Success β†’ sign in

Core Transaction Flow

  1. Seller creates listing β€” Description, price (₦100 – ₦10M), delivery timeline (inspection UI optional / legacy field)
  2. Buyer joins β€” Enters TXN-{uuid}; sees seller trust + listing context (pre-fund anomaly UI is limited in current wiring)
  3. Buyer funds escrow β€” Demo path: demo_balance debit + state β†’ Funded_Locked; Squad still powers VA, payouts, and releases in the wider system
  4. Seller ships β€” Marks as shipped; both parties notified
  5. Buyer inspects β€” Confirms receipt OR raises a dispute
  6. Money releases β€” Funds transfer to seller's bank via Squad NIP payout

Dispute Flow

  1. Buyer files dispute with description + photo evidence
  2. AI agent analyzes and returns verdict with confidence score
  3. High confidence β†’ auto-resolved. Low confidence β†’ admin queue
  4. Resolution triggers automatic fund movement (refund / release / split)

Full walkthrough with screenshots: docs/USER_FLOW.md


Impact Potential

Who can this reach:

  • Immediate: ~50M Nigerians active in informal online commerce (WhatsApp, social media, classifieds)
  • Year 1 target: 10,000 active users processing ₦500M+ in escrow transactions
  • Expansion: Every African market with similar trust gaps β€” Ghana, Kenya, South Africa

How quickly:

  • Zero-install (web app, no app store approval needed)
  • Integrates with existing commerce channels β€” sellers share a transaction link on WhatsApp/Twitter
  • Viral loop: every transaction exposes a new counterparty to ScrowPay

Scalability & Business Model

Revenue stream Model
Transaction fee 1.5% of escrow amount (capped at ₦5,000 per transaction)
Instant Release Premium feature for Elite-tier users (Trust Score β‰₯ 95)
API access B2B escrow-as-a-service for marketplaces and logistics platforms

Technical scalability:

  • Stateless frontend (vanilla JS + CDN) β€” scales horizontally behind any CDN/load balancer
  • Turso (distributed SQLite) β€” edge-replicated, handles millions of reads
  • Squad handles all payment infrastructure β€” no PCI compliance burden
  • AI engine is a stateless Flask container β€” auto-scales independently

Research & Validation

Claim Evidence
Trust is the #1 barrier to Nigerian e-commerce NOIPolls 2023: 67% of online shoppers cite fear of fraud as primary concern
Informal e-commerce exceeds $10B EFInA Access to Financial Services survey + CBN reports on informal sector
1 in 3 buyers has been scammed Consumer protection data from FCCPC + independent surveys
Escrow reduces dispute rates by 60-80% Payoneer & Escrow.com public case studies on marketplace trust
Isolation Forest is effective for transaction fraud IEEE 2022 survey on unsupervised anomaly detection in financial data
BVN verification deters fraud NIBSS data: BVN-linked accounts have 40% fewer fraud incidents
Real-time risk scoring improves catch rates Stripe Radar public benchmarks + academic literature on pre-authorization scoring

Full research citations: docs/RESEARCH_VALIDATION.md


πŸš€ Quick start β€” 5 minutes

Detailed step-by-step (with copy-paste commands and a comprehensive FAQ) is in SETUP_CHECKLIST.md.

Docker is the only supported way to run this project. No Python, no Node.js, no nginx β€” the bundled docker-compose.yml brings up the entire stack.

Prerequisites

1. Clone and configure

git clone <repository-url> scrowpay
cd scrowpay
cp .env.example .env

Edit .env and fill in at minimum:

TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=eyJ...
SQUAD_SECRET_KEY=sandbox_sk_...
SQUAD_PUBLIC_KEY=sandbox_pk_...

2. Browser-side configs (three files, all gitignored)

Copy the three templates and paste in your real values. None of these are auto-generated β€” each one has a *.example you copy from:

# Windows
Copy-Item frontend\env.js.example            frontend\env.js
Copy-Item frontend\gemini-config.example.js  frontend\gemini-config.js
Copy-Item frontend\cloudinary-config.example.js frontend\cloudinary-config.js

# Mac/Linux
cp frontend/env.js.example            frontend/env.js
cp frontend/gemini-config.example.js  frontend/gemini-config.js
cp frontend/cloudinary-config.example.js frontend/cloudinary-config.js

Each file's header explains exactly what to paste.

  • frontend/env.js β€” Turso + Squad keys (same values as .env, the browser needs its own copy)
  • frontend/gemini-config.js β€” Google AI Studio key (unlocks dispute AI + face re-verification)
  • frontend/cloudinary-config.js β€” Cloudinary cloud name (unlocks real image storage)

If you skip the Gemini / Cloudinary ones, the app still runs β€” it just degrades cleanly:

Missing config Degraded behaviour
gemini-config.js Dispute auto-resolution disabled; face re-verification disabled. Rule-based risk only.
cloudinary-config.js Dispute photos + face references fall back to base64 inline storage (hackathon mode).
Resend key in .env Notifications still appear in the bell dropdown; no email is sent; signup OTP falls back to a dev code in the console.

3. Start everything

docker compose up -d
docker compose logs -f          # watch boot

4. Open the app

Service URL
Frontend (landing page) http://localhost:8080/web.html
Sign-in http://localhost:8080/sign-in.html
Dashboard (after sign-in) http://localhost:8080/dashboard.html
Admin console http://localhost:8080/admin.html (requires is_admin flag)
AI risk engine http://localhost:5000
AI engine health http://localhost:5000/health

That's it. Create an account, fund a transaction, raise a dispute, watch the AI agent rule on it.


🎬 Demo flow for judges

This is the path to take to see everything working in ~5 minutes:

  1. Sign up two accounts (use two browsers / incognito windows).
    • Both go through phone β†’ OTP (123456) β†’ BVN β†’ name β†’ liveness blink β†’ address β†’ PIN
    • Both end up with a Squad virtual account
  2. Seller creates an escrow listing (₦10,000 iPhone case), copies the transaction ID.
  3. Buyer joins the transaction with the ID.
    • Watch the AI risk score appear; with a new account it flags "new account" but passes.
  4. Buyer funds it β†’ money moves into the holding virtual account β†’ notifications fire (bell + email).
  5. Seller marks as shipped β†’ both get notified.
  6. Buyer raises a dispute with a description and a photo of the (allegedly) damaged item.
    • The dashboard uploads the photo to Cloudinary β†’ calls the Gemini dispute agent β†’ gets a verdict.
    • High confidence (>90%) β†’ auto-resolved with fund transfer.
    • Lower confidence β†’ falls into the admin console queue.
  7. Admin opens admin.html, sees the pending dispute, clicks it, reviews the photo + AI recommendation, and resolves it. Funds move; both parties get notified.
  8. Try a large withdrawal (β‰₯ ₦500,000) β†’ the face re-verification modal opens, captures a fresh frame, sends both your stored signup photo + the new frame to Gemini, and only releases the withdrawal on a "same person" verdict.

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              BROWSER                                  β”‚
β”‚                                                                       β”‚
β”‚   web.html  ─►  account-creation.html  ─►  sign-in.html               β”‚
β”‚                                                       β”‚               β”‚
β”‚                                                       β–Ό               β”‚
β”‚                                                  dashboard.html       β”‚
β”‚                                                       β”‚               β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚   β”‚  30+ vanilla-JS services on `window.*`:                         β”‚ β”‚
β”‚   β”‚  β€’ TransactionService     β€’ StateMachineService                 β”‚ β”‚
β”‚   β”‚  β€’ BalanceService         β€’ TrustEngineService                  β”‚ β”‚
β”‚   β”‚  β€’ DisputeService         β€’ DisputeAgentService (Gemini)        β”‚ β”‚
β”‚   β”‚  β€’ IsolationForestService β€’ RiskEngineService                   β”‚ β”‚
β”‚   β”‚  β€’ AnomalyDetectionEngine β€’ DeviceFingerprintService             β”‚ β”‚
β”‚   β”‚  β€’ FaceVerificationService (Phase F, Gemini multimodal)         β”‚ β”‚
β”‚   β”‚  β€’ CloudinaryService      β€’ NotificationService                 β”‚ β”‚
β”‚   β”‚  β€’ EmailOTPService        β€’ DeviceFingerprintService            β”‚ β”‚
β”‚   β”‚  β€’ SessionService         β€’ SquadAPIService                     β”‚ β”‚
β”‚   β”‚  ...                                                            β”‚ β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜ β”‚
β”‚             β”‚                  β”‚                 β”‚               β”‚    β”‚
β”‚   admin.html (Phase G):  separate page for manual dispute        β”‚    β”‚
β”‚   review, face audit, risky-txn monitor, user directory          β”‚    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”˜
              β–Ό                  β–Ό                 β–Ό               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚  Turso DB  β”‚    β”‚  Squad API   β”‚  β”‚  AI Engine   β”‚  β”‚ Cloudinaryβ”‚
       β”‚  (libSQL,  β”‚    β”‚ (virtual a/c β”‚  β”‚  (Flask +    β”‚  β”‚ (photo +  β”‚
       β”‚   HTTP)    β”‚    β”‚  + transfers)β”‚  β”‚  IsoForest)  β”‚  β”‚  face refs)β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                  β–²                β–²
                                                  β”‚                β”‚
                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
                                          β”‚  Gemini API  β”‚         β”‚
                                          β”‚ (dispute     β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                          β”‚  agent +     β”‚  β”‚  Resend  β”‚
                                          β”‚  face verify β”‚  β”‚  (email) β”‚
                                          β”‚  + risk hint)β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Three deployable units:

Unit Tech Port Purpose
frontend/ Vanilla JS + nginx 8080 Static files; all UI + business logic
ai-engine/ Python Flask + scikit-learn 5000 Anomaly scoring + email proxy
External services Turso, Squad, Gemini, Cloudinary, Resend n/a DB, payments, AI, storage, mail

For a top-to-bottom walkthrough of every service, table, and flow, see APP_GUIDE.md (~500 lines, deliberately exhaustive).


✨ Features

Core escrow flow

  • 10-stage account creation (phone + email, email OTP, BVN, Squad VA, liveness, address, 6-digit PIN) with BVN verification via Squad
  • MediaPipe blink liveness check, with the captured frame uploaded to Cloudinary as a face reference (non-blocking if upload fails)
  • Buyer-funded transactions: demo build debits demo_balance then locks state; Squad virtual accounts + holding/payout paths power real money movement in integrated environments
  • Deterministic state machine: Created β†’ Funded_Locked β†’ In_Transit β†’ Completed (plus Disputed, Cancelled, Refunded)
  • Auto-release when delivery / inspection timers fire (StateMachineService)
  • Trust score with tiers (Low / Building / Trusted / Elite) and Instant Release eligibility

AI safety net

  • Anomaly pipeline (AnomalyDetectionEngine) β€” RiskEngineService (rules) + IsolationForestService (Flask ML). Current dashboard wiring: runs evaluate() post-fund and feeds TrustEngineService (non-blocking). Optional Gemini on listings is not enabled in code today.
  • Multimodal dispute resolution agent β€” Gemini 2.0 Flash reads the complaint + up to 4 photos and returns a structured JSON verdict (favoredParty, confidence, payout, reasoning, evidenceCited)
  • Face re-verification gate β€” on high-value withdrawals, compares a fresh capture against the signup reference photo using Gemini multimodal; results are persisted to face_verifications for audit

Notifications & communication

  • Per-user notification feed with category filters and unread counts (bell icon)
  • Transactional emails via Resend (funding confirmed, shipment, dispute opened, dispute resolved, face check failed)
  • Email at signup with OTP delivery via the AI-engine email proxy

Admin console (admin.html)

  • Pending dispute queue with photo evidence, AI recommendation, and one-click resolution (refund / release / split)
  • Face verification audit log filterable by verdict
  • Risky transaction monitor (risk score β‰₯ 50)
  • User directory with search

πŸ› οΈ Tech stack

Layer Technology Why
Frontend Vanilla JS, HTML5, Tailwind CSS (CDN) Zero build step, deploys anywhere static
Database Turso (libSQL over HTTP) Distributed SQLite; free tier is generous; HTTP API works from the browser without a backend
Payments Squad API Nigerian payment rails; virtual accounts; BVN verification
Risk ML Python 3.11, Flask, scikit-learn (Isolation Forest) Industry-standard unsupervised anomaly detection
Dispute AI Google Gemini 2.0 Flash Multimodal (text + image), JSON-mode output, low latency
Image storage Cloudinary Unsigned upload presets β€” no backend needed
Email Resend Modern email API with a clean dashboard
Face liveness MediaPipe Face Mesh Runs entirely in browser; no PII leaves the device for liveness
Device ID FingerprintJS open-source v4 Stable visitor IDs for sock-puppet detection
Container Docker + Docker Compose One-command local + production deploys

πŸ“‚ Project structure

scrowpay/
β”œβ”€β”€ README.md                        ← you are here (answers: what, how, run it)
β”œβ”€β”€ APP_GUIDE.md                     ← exhaustive technical deep dive
β”œβ”€β”€ DEPLOYMENT.md                    ← production deployment recipes
β”œβ”€β”€ SETUP_CHECKLIST.md               ← copy-paste reproducibility script
β”œβ”€β”€ CONTRIBUTING.md                  ← contribution guidelines
β”œβ”€β”€ LICENSE                          ← MIT
β”œβ”€β”€ .env.example                     ← environment variable template
β”œβ”€β”€ .gitignore
β”œβ”€β”€ docker-compose.yml               ← the only thing you run β€” brings up ai-engine + frontend
β”œβ”€β”€ nginx.conf                       ← nginx config for the frontend container
β”œβ”€β”€ start-dev.sh / start-dev.bat     ← thin convenience wrapper around `docker compose up`
β”‚
β”œβ”€β”€ docs/                            ← detailed documentation for judges
β”‚   β”œβ”€β”€ SQUAD_API_INTEGRATION.md     ← deep dive into all Squad API usage
β”‚   β”œβ”€β”€ AI_INTELLIGENCE.md           ← AI/ML architecture and data pipeline
β”‚   β”œβ”€β”€ USER_FLOW.md                 ← step-by-step product walkthrough
β”‚   └── RESEARCH_VALIDATION.md       ← evidence and citations for claims
β”‚
β”œβ”€β”€ screenshots/                     ← product screenshots for submission
β”‚   └── README.md                    ← screenshot index
β”‚
β”œβ”€β”€ frontend/                        ← all UI + business logic (vanilla JS)
β”‚   β”œβ”€β”€ README.md                    ← per-file service index
β”‚   β”œβ”€β”€ web.html                     ← landing page
β”‚   β”œβ”€β”€ account-creation.html        ← 10-stage signup
β”‚   β”œβ”€β”€ sign-in.html                 ← phone + 6-digit PIN login
β”‚   β”œβ”€β”€ dashboard.html               ← main app (the big one)
β”‚   β”œβ”€β”€ admin.html                   ← Phase G admin console
β”‚   β”œβ”€β”€ *.js                         ← 30+ services; one class per file on window.*
β”‚   β”œβ”€β”€ env.js.example               ← copy to env.js (gitignored)
β”‚   β”œβ”€β”€ cloudinary-config.example.js ← copy to cloudinary-config.js (gitignored)
β”‚   β”œβ”€β”€ gemini-config.example.js     ← copy to gemini-config.js (gitignored)
β”‚   └── state-lga-area.json          ← Nigerian states / LGAs / wards
β”‚
β”œβ”€β”€ ai-engine/                       ← Python Flask microservice
β”‚   β”œβ”€β”€ README.md
β”‚   β”œβ”€β”€ app.py                       ← Flask app: /api/v1/score, /notify/*, /health
β”‚   β”œβ”€β”€ train_model.py               ← Isolation Forest training script
β”‚   β”œβ”€β”€ generate_synthetic_data.py   ← synthetic transaction generator
β”‚   β”œβ”€β”€ requirements.txt
β”‚   β”œβ”€β”€ Dockerfile
β”‚   └── models/                      ← isolation_forest_model.pkl (committed)
β”‚
└── scripts/                         ← utility scripts
    β”œβ”€β”€ README.md
    β”œβ”€β”€ make-admin.ps1               ← grant admin access (Windows)
    β”œβ”€β”€ make-admin.sh                ← grant admin access (Mac/Linux)
    └── make-admin.bat               ← grant admin access (Windows CMD)

πŸ” Accessing the admin console

The admin console at /admin.html is gated by a database flag β€” there's intentionally no UI to grant yourself access.

Grant admin to a user

Sign up normally, then run the helper script (it reads your Turso credentials from .env, so you never have to paste them):

# Windows - by user id (most reliable):
.\scripts\make-admin.ps1 -UserId 1

# Or by phone number:
.\scripts\make-admin.ps1 -PhoneNumber "+2348012345678"
# macOS / Linux / WSL
./scripts/make-admin.sh --id 1
# or
./scripts/make-admin.sh +2348012345678

If phone lookup fails even though the user exists, the script lists recent users with their ids β€” re-run with -UserId <n> / --id <n> to bypass phone matching (hidden whitespace in the stored phone can defeat exact equality).

Or for the manual route, run this SQL against your Turso database (Turso CLI, dashboard, or any libSQL client):

UPDATE users SET is_admin = 1 WHERE id = 1;
-- or
UPDATE users SET is_admin = 1 WHERE phone_number = '+2348012345678';

Either way, the user now sees an "Admin Console" link in their profile panel, and admin.html will load for them. See scripts/README.md for full options.

What admins can do

  • View all pending disputes (transactions stuck in Disputed state)
  • See the AI agent's recommendation + confidence for each
  • Resolve manually with notes (refund buyer / release to seller / split 50/50)
  • Audit the face verification log filtered by verdict
  • Monitor high-risk transactions
  • Search the user directory

Non-admin users hitting /admin.html see a 403 splash with a link back to the dashboard. The page makes zero non-public queries until the is_admin = 1 check passes.


πŸ—„οΈ Database

Schema is auto-created and auto-migrated on first app load via TursoDBService.initializeSchema(). There are no separate migration files β€” every CREATE TABLE / ALTER TABLE lives in turso-db-service.js and is idempotent (IF NOT EXISTS / column existence checks).

Tables: users, transactions, transaction_state_history, disputes, trust_scores, trust_score_history, device_fingerprints, ai_risk_logs, security_logs, notifications, email_otps, face_verifications.

Full table-by-table description in APP_GUIDE.md Β§ 10.


πŸ§ͺ Testing

Try the AI risk engine directly

curl -X POST http://localhost:5000/api/v1/score \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "test_user",
    "transaction_amount": 50000,
    "transaction_velocity": 3,
    "account_age_days": 45,
    "device_fingerprint": 5432,
    "time_of_day": 14,
    "counterparty_trust_score": 75
  }'

Expected: {"risk_score": <0..100>, "verdict": "pass" | "fail", "anomaly_indicators": [...]}

Frontend manual test flow

See Demo flow above.

Python AI engine unit tests

The AI engine ships a small pytest suite. Run it inside the container so you don't need a local Python install:

docker compose exec ai-engine pytest test_api.py test_model.py

The frontend services don't have unit tests yet (see Known limitations).


πŸ“š Documentation map

File What it covers
README.md Problem, solution, Squad APIs, AI, user flow, impact, quick start (this file)
docs/SQUAD_API_INTEGRATION.md Deep dive into all 8 Squad API endpoints and how they power the product
docs/AI_INTELLIGENCE.md AI/ML architecture: Isolation Forest, Gemini dispute agent, face verification
docs/USER_FLOW.md Step-by-step product walkthrough with UI descriptions
docs/RESEARCH_VALIDATION.md Evidence, citations, and validation for all claims
APP_GUIDE.md ~500-line exhaustive walkthrough β€” every service, every table, every flow
SETUP_CHECKLIST.md Copy-paste reproduction script for a fresh clone
DEPLOYMENT.md Production deploys to Vercel / Netlify / VPS, Squad sandbox→production swap
CONTRIBUTING.md Contribution guidelines (code style, JSDoc, commit format)
frontend/README.md Per-service index of every JS file in frontend/
ai-engine/README.md AI engine API spec, model training, performance benchmarks
screenshots/README.md Product screenshot index

🚧 Known limitations / roadmap

We're explicit about what's not production-grade. None of these affect the demo flow:

Area Status Workaround / plan
OTP at signup Hardcoded to 123456 β€” no SMS provider wired Termii or Twilio integration is a one-file change
Manual rate limiting App-level only (rate-limiting-integration-example.js) Move to nginx / Cloudflare in production
Push notifications Email only; no web-push or mobile push Service worker + FCM/APNs
Frontend unit tests None Vitest + jsdom
Disputes ai_reasoning column Not persisted (only the verdict + confidence are) Add column; admin console will display it

🀝 Contributing

See CONTRIBUTING.md. Short version:

  • One class per file, attached to window.*
  • JSDoc every public method
  • Follow the existing service-load order in dashboard.html
  • Never commit .env, frontend/env.js, frontend/gemini-config.js, or frontend/cloudinary-config.js

πŸ“„ License

MIT β€” see file for details.


πŸ™ Acknowledgments

Built for the Squad Hackathon. Special thanks to:

  • Squad for excellent payment APIs and sandbox tooling
  • Turso for a libSQL HTTP API that lets the browser talk to SQLite directly
  • Google AI Studio for accessible multimodal Gemini access
  • Cloudinary for unsigned upload presets that work without a backend
  • Resend for the cleanest transactional email API around

Questions? Issues? Open a GitHub issue or check the troubleshooting section in DEPLOYMENT.md.

Built with care for Nigerian commerce.

About

AI-powered escrow for Nigerian peer-to-peer commerce. Money sits in a holding account until both sides honour the deal. Multimodal Gemini agent resolves disputes with photo evidence. Built on Squad payment APIs, Turso database, and MediaPipe liveness checks.

Topics

Resources

License

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors