Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
Send branded quotations, get paid online with Stripe, Paystack, Flutterwave or Bachs, and issue receipts automatically. Track your real costs and profit on every job.
FinanceJavaScript · PLpgSQLGitHub @AkosempireOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Branded quotations, invoices and receipts for freelancers and companies.
A business sends a quote → the customer opens a link, accepts and pays (Stripe, Paystack, Flutterwave, Bachs or bank transfer) → the quote becomes a paid invoice and a numbered receipt is issued. Every document carries the business's own logo, colour and details. Each quote can hold hidden cost lines (data, electricity, transport, software…) so the team sees real profit and margin; customers never see them.
Stack: Next.js 16 (App Router) · Supabase (Postgres, Auth, Storage) · Tailwind CSS 4 · pdf-lib. Design system: Kasuwa (Figtree, cool-grey canvas, one confident blue).
| Area | Where | Who |
|---|---|---|
| Sign up, onboarding (freelancer or company, branding, money, team) | /signup, /onboarding | Everyone |
| Dashboard: owed, collected, open quotes, profit, needs-attention | /app | Members |
| Quotes and invoices with hidden costs, margin and a pricing helper | /app/quotes, /app/invoices, /app/documents/[id] | Members (costs: allowed members only) |
| Customers, payments and receipts ledger | /app/customers, /app/payments | Members |
| Business and branding, payment setup guide, team and roles, plan and billing, cost presets | /app/settings/* | Owner / manager (billing: owner) |
| Customer's link: view, accept/decline, pay, download PDF | /d/[token] | Customers (no login) |
| Receipt | /r/[token] | Customers |
| Platform admin: plans and pricing, trial rules, businesses, subscription billing | /admin | You (platform owner) |
| Role | Can do |
|---|---|
| Owner | Everything, including billing. |
| Manager | Approves and sends, manages team (not other managers), branding and payments. |
| Finance officer | Creates, sends, records payments. Sees hidden costs. |
| Junior finance | Prepares drafts and submits them for approval. Sees hidden costs only if allowed. |
These rules are enforced in the database (row-level security and checked functions), not just in the UI.
supabase/migrations/ in order: first 20261024000100_paytrail.sql (every table, the security rules and the logos storage bucket), then 20261024000200_global_payments.sql (adds Stripe and Paystack), then 20261024000300_pricing.sql (default plans and a 14-day trial), then 20261024000400_privacy.sql (data download and deletion), 20261024000500_templates.sql (document templates and paper sizes) and 20261024000600_security_codes.sql (emailed security codes). All are safe to run again later.
https://paytrail.yourdomain.comhttps://paytrail.yourdomain.com/auth/callback (and http://localhost:3000/auth/callback for local work)smtp-relay.brevo.com, port 587, your Brevo SMTP login and SMTP key). The built-in sender is rate-limited.
supabase/email-templates/confirm-signup.html and set the subject to Your Usetally code: {{ .Token }}. New users type this 6-digit code to confirm their email (the link in the email works too).anon key and the service_role key for step 2.Copy .env.example to .env.local (locally) or add them in Vercel → Settings → Environment Variables:
| Variable | What it is |
|---|---|
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY | From Supabase. Keep the service role key secret. |
NEXT_PUBLIC_SITE_URL | Your app's public address, with no trailing slash. Used in payment links and webhook URLs. |
APP_ENCRYPTION_KEY | 32+ random characters. Encrypts every business's payment keys. Generate with node -e "console.log(require('crypto').randomBytes(32).toString('base64'))". Never change it later, or saved keys can't be read. |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM | Brevo: smtp-relay.brevo.com, 587, your Brevo SMTP login (e.g. [email protected]), your SMTP key (xsmtpsib-…), and a sender you verified in Brevo. Needed for security codes, emailing documents and admin alerts. Without it Usetally still runs, but security codes can't be sent, so that protection is off. |
ADMIN_ALERT_EMAILS | Optional. Who gets admin alerts (new businesses, payment key changes). Empty = every platform admin. |
CRON_SECRET | Random string. Protects the job that re-checks unconfirmed payments. |
LEGAL_COMPANY_NAME, LEGAL_ADDRESS, LEGAL_EMAIL | Shown on /terms, /privacy and /dpa. Use your registered business name and address. |
LEGAL_DATA_REGION, LEGAL_EMAIL_PROVIDER | Shown on /subprocessors, e.g. European Union (Frankfurt) and Brevo. |
PAYTRAIL_ALLOW_TEST_PAYMENTS | Leave unset in production. Set to 1 only on a staging site to try test-card payments end to end. |
vercel.json also runs /api/cron/reconcile once a day as a safety net (allowed on the free Hobby plan). On Vercel Pro you can make it every 15 minutes: change the schedule to */15 * * * *./terms and /privacy are ready-made starting points written for a Nigerian business serving customers worldwide. Have a lawyer review them before launch.Sign up in the app first, then in the Supabase SQL Editor run:
insert into public.platform_admins (user_id)
select id from auth.users where email = '[email protected]';
Refresh the app. Platform admin now appears in the sidebar (/admin).
Default plans (added by the pricing migration, all editable):
| Plan | Naira monthly / yearly | US dollar monthly / yearly | Documents a month | Users |
|---|---|---|---|---|
| Solo (freelancers) | ₦3,000 / ₦30,000 | $5 / $50 | 50 | 1 |
| Team | ₦7,500 / ₦75,000 | $15 / $150 | 200 | 5 |
| Business | ₦20,000 / ₦200,000 | $39 / $390 | Unlimited | 15 |
| Founding (hidden) | ₦60,000 a year, locked | — | 200 | 5 |
Businesses see plans in their own currency (dollars if there's no plan in theirs). There's no free-forever plan: after the 14-day trial a business keeps all its data but needs a paid plan to send new quotes and invoices. Assign Founding (Team features) to early customers from /admin/businesses → Manage; it isn't shown publicly and its price doesn't change when you raise public prices.
/admin/plans: edit or add plans: name, price, monthly or yearly, document limit, team seats, whether companies can choose it, and feature bullets. Prices show on the landing page and on every business's billing page immediately.
/admin/settings:
Built in:
/api/cron/reconcile) permanently removes it after 30 days, logo included. Usetally keeps only the record of plan payments made to it, with the business name, for tax./privacy, /terms, /dpa (Data Processing Agreement, covering businesses' customer data) and /subprocessors. Keep /subprocessors current (subprocessors() in lib/legal.js).Your side (not code): register with the Nigeria Data Protection Commission once you reach the threshold, file the yearly compliance audit return, name a Data Protection Officer if required, accept Supabase's and Vercel's DPAs in their dashboards, and keep a one-page breach plan (tell affected businesses within 48 hours, the regulator within 72). If you actively market to the EU or UK, appoint a representative there. Have a lawyer review the legal pages.
/print/..., /d/[token]/print, /r/[token]/print) and the PDF download follow the same template rules and paper sizes. Thermal rolls print as one page exactly as long as the receipt. In the print dialog choose the receipt printer, margins "None" and scale 100%.assets/fonts (PDF) and public/fonts (screen and print).ADMIN_ALERT_EMAILS (or every platform admin) when a business finishes sign-up, and when payment keys are connected, changed or removed. Keys themselves are never emailed./offline with links to the saved ones. Creating, sending and payments need a connection. Signing out clears saved pages from the device. The service worker (public/sw.js) only runs in production builds.Each business connects its own payment account in Settings → Payment setup, using the in-app guide. The screen suggests the best provider for the business's currency:
| Provider | Where it works | Webhook secret |
|---|---|---|
| Stripe | US, UK, EU, Canada, Australia and 40+ other countries | Signing secret (whsec_…) |
| Paystack | Nigeria, Ghana, Kenya, South Africa, Côte d'Ivoire | None (Paystack signs with the secret key) |
| Flutterwave | Across Africa | A secret hash you choose |
| Bachs | Where Bachs operates | Signing secret |
Setup steps:
A provider only shows Connected after the test payment is confirmed and its webhook has reached Usetally. Customers only see the Pay button for live-mode connections, so nobody can "pay" an invoice with a test card.
Every payment is re-verified with the provider's API before an invoice is marked paid. Amount, currency and reference must match exactly. A replayed webhook can't pay twice. If money arrives for a document that was already paid or cancelled, it's flagged as needs review in the document's activity.
Bank transfers, cash and POS are recorded by finance with Record payment, which issues a receipt the same way. Part payments are supported.
Stripe uses hosted Checkout Sessions and the
checkout.session.completedwebhook. Paystack uses Transaction Initialize/Verify and thex-paystack-signaturewebhook header.Bachs API note: the integration uses the Bachs endpoints from your existing CREATIVEYATI code (
/v1/checkout-sessions,/v1/balances, and HMAC-signed webhooks with thex-bachs-timestampandx-bachs-signatureheaders). Flutterwave uses the v3 Standard checkout andverif-hashwebhooks. Before going live, run one live test payment with each provider you enable.
npm install
cp .env.example .env.local # fill in a Supabase project (a free one is fine for development)
npm run dev # http://localhost:3000
Preview mode (no Supabase): sample data for every screen, local only. It is ignored in production builds.
PAYTRAIL_PREVIEW=1 PAYTRAIL_PREVIEW_ROLE=owner NEXT_PUBLIC_SITE_URL=http://localhost:3000 \
NEXT_PUBLIC_SUPABASE_URL=http://localhost:9 NEXT_PUBLIC_SUPABASE_ANON_KEY=x SUPABASE_SERVICE_ROLE_KEY=x \
APP_ENCRYPTION_KEY=preview-preview-preview-preview-xx npm run dev
Set PAYTRAIL_PREVIEW_ROLE to manager, finance or junior_finance to see what each role sees.
Tests
npm test # money maths, encryption, webhook signatures, provider responses
npm run test:db # 18 database security tests (needs a local PostgreSQL 16; see scripts/test-db.sh)
app/ pages and server actions (app/app = signed-in app, app/admin = platform owner, app/d + app/r = customer links)
components/ design-system components (ui.js), app shell, editor, document view
lib/ Supabase clients, permissions, money, PDFs, email, payments (stripe, paystack, flutterwave, bachs, webhooks)
supabase/ migration (schema + security) and database tests
assets/fonts/ Lato (open licence) for PDFs, which includes the ₦ sign
npm i --no-save playwright && npx playwright install chromium # once
npm run test:e2e # owner, manager, finance, junior finance
E2E_ROLES=junior_finance npm run test:e2e
Runs the app in preview mode with a simulated 100 ms delay on every Supabase call, then checks for each role: every allowed page opens without errors, refused pages redirect, the sidebar shows the right links, a click gets a visible response within 200 ms and the page is ready within 1.2 s, documents and the quote/invoice forms work, and the mobile menu works.