Integration

Mobile & API integration guide

This page is the handoff pack for a mobile app (or any external client) that needs to talk to the live Payrano Solutions backend and match the web product one-to-one. It covers the architecture, every public endpoint with sample payloads, the data model, the settlement model, and the exact design tokens used on this site.

v1.4Last updated 10 August 2026· Written for external engineering teams

1. Backend & base URLs

Payrano runs on Supabase (Postgres, Auth, Storage) behind a TanStack Start server layer deployed on the edge. There is no Firebase, no separate Node service, and no self-managed database.

There are two API surfaces:

  1. Payrano REST API https://payranosolutions.com/api/public/v1/*. Plain JSON over HTTPS, CORS-enabled (Access-Control-Allow-Origin: *) so a mobile client can call it directly. This is the recommended surface: it never exposes tables and its shape is stable.
  2. Supabase Data API — the project URL plus its publishable key, used with @supabase/supabase-js for sign-in, session refresh and realtime. Every table is protected by row-level security scoped to auth.uid(), so a signed-in merchant can only ever read their own rows.

Stable environments: https://payranosolutions.com (production) and https://payrano-usdt-gateway.lovable.app (same build, useful as a fallback host). Use production for anything real.

Two values the mobile app must be configured with:

SUPABASE_URL=<project url>          # ask the Payrano owner
SUPABASE_ANON_KEY=<publishable key>  # begins with sb_publishable_
PAYRANO_API_BASE=https://payranosolutions.com/api/public/v1

Both Supabase values are public by design (they are shipped in the web bundle), but they are not printed on this page — the Payrano owner sends them from the project's backend settings. The service-role key is never shared and must never appear in a mobile binary.

2. Authentication

Users sign in today with email + password via Supabase Auth (with password reset by email). Google/social sign-in is not enabled yet. Payrano also acts as an OAuth identity provider for the Payrano Wallet app through a consent flow, so a wallet-style client can reuse the same accounts later.

Mobile should use the Supabase client and then reuse the access token:

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  auth: { persistSession: true, autoRefreshToken: true },
});

const { data, error } = await supabase.auth.signInWithPassword({
  email, password,
});

const token = data.session.access_token;  // send as Bearer to /wallet/* endpoints

Two distinct credential types — do not mix them up:

  • User session (Bearer token) — a signed-in person. Used by the /wallet/* endpoints and every Supabase table read.
  • Merchant API key pair — machine-to-machine, issued in the dashboard under API Keys. Sent as x-payrano-key-id and x-payrano-key-secret headers. Only active merchants with approved KYB can authenticate. Never embed a key pair in a consumer mobile app; use the user session for merchant-app screens.

3. Merchant REST API

GET/api/public/v1/keys/verify

Auth: API key pair

Confirms a key pair before saving it. Also accepts POST.

{
  "ok": true,
  "authenticated": true,
  "merchant": {
    "id": "9f1c…",
    "business_name": "Dice Cafe",
    "logo_url": null,
    "settlement_address_configured": true
  }
}

POST/api/public/v1/payments

Auth: API key pair

Creates a payment request (invoice) and returns a hosted checkout URL.

// request
{
  "amount": 149.5,
  "reference": "ORDER-1042",        // optional, auto-generated when omitted
  "description": "Table 4 — dinner", // optional
  "expires_in_minutes": 30           // optional, 5–1440
}

// 201 response
{
  "id": "6b0e2c7a-…",
  "amount": 149.5,
  "net_amount": 149.5,
  "fee_amount": 0,
  "fee_wallet": null,
  "description": "Table 4 — dinner",
  "reference": "ORDER-1042",
  "wallet_address": "TXk…",          // merchant's own TRC20 address
  "status": "waiting",
  "expires_at": "2026-08-17T07:05:00.000Z",
  "created_at": "2026-08-17T06:35:00.000Z",
  "checkout_url": "https://payranosolutions.com/pay/6b0e2c7a-…"
}

Errors: 401 invalid_api_key, 400 invalid_input, 409 settlement_address_missing.

GET/api/public/v1/payments?id=… | ?reference=…

Auth: API key pair

Looks up one payment owned by the authenticated merchant. Expired-but-still-waiting payments are flipped to expired on read. Calling it with no lookup key returns a 200 connection probe instead of a 404.

{
  "id": "6b0e2c7a-…",
  "amount": 149.5,
  "reference": "ORDER-1042",
  "wallet_address": "TXk…",
  "status": "completed",     // waiting | confirming | completed | expired
  "expires_at": "…",
  "confirmed_at": "…",
  "created_at": "…"
}

For dashboard lists (payments history, KPIs, transactions), read the tables through the Supabase client with the user session — RLS already scopes rows to the signed-in merchant, and realtime gives you live settlement updates without polling:

const { data } = await supabase
  .from("payments")
  .select("id, amount, reference, status, created_at, confirmed_at")
  .order("created_at", { ascending: false })
  .limit(50);

supabase.channel("payments")
  .on("postgres_changes",
      { event: "UPDATE", schema: "public", table: "payments" },
      (p) => refresh(p.new))
  .subscribe();

4. Wallet & payer API

These endpoints power the wallet/payer side. Bearer endpoints take the Supabase access token as Authorization: Bearer <token>; the rest are public.

GET/api/public/v1/wallet/me

Auth: Bearer token

{
  "user_id": "…",
  "email": "merchant@example.com",
  "handle": { "handle": "dicecafe", "chain": "tron", "address": "TXk…", "display_name": "Dice Cafe", "active": true },
  "merchant": { "id": "…", "business_name": "Dice Cafe", "kyc_status": "approved", "status": "active" }
}

GET / POST/api/public/v1/wallet/links

Auth: Bearer token

Lists or registers addresses this account owns. POST body: { chain, address, label?, message?, signature? } — supplying a signed Payrano ownership challenge marks the link verified (EIP-191 for EVM chains, TRON signature for TRON).

{ "links": [ { "id": "…", "chain": "tron", "address": "TXk…", "provider": "payrano", "label": "Payrano Wallet", "verified_at": "…", "created_at": "…" } ] }

POST/api/public/v1/wallet/handles

Auth: Bearer token

Claims or updates the caller's $handle. Body: { handle, chain, address, display_name? }. Handles match ^[a-z0-9][a-z0-9._-]{2,29}$. Errors: 409 handle_taken, 400 invalid_address.

GET/api/public/v1/resolve/{handle}

Auth: Public

Handle lookup for scan/pay flows — routing data only, never contact info.

{ "handle": "dicecafe", "display_name": "Dice Cafe", "chain": "tron", "address": "TXk…" }

GET/api/public/v1/wallet/invoices/{reference}

Auth: Public

The invoice behind a Payrano pay link, so a wallet can render it natively.

{
  "id": "…", "merchant_name": "Dice Cafe", "merchant_logo_url": null,
  "amount": 149.5, "net_amount": 149.5, "fee_amount": 0,
  "reference": "ORDER-1042", "wallet_address": "TXk…",
  "chain": "tron", "asset": "USDT",
  "status": "waiting", "expires_at": "…", "confirmed_at": null,
  "checkout_url": "https://payranosolutions.com/pay/…"
}

GET/api/public/v1/reputation/{address}

Auth: Public

Public payment reputation for a payer address (Labs feature).

{ "address": "TXk…", "count": 14, "volume": 4820.75, "merchants": 3, "streak": 2, "firstSeen": "…", "recent": [ { "id": "…", "amount": 149.5, "reference": "ORDER-1042", "created_at": "…" } ] }

Pay links follow a stable query contract the wallet builds against: /pay?chain=&address=&amount=&asset=&contract=&decimals=&ref=&callback=, plus the native scheme payrano://pay?…. Reuse it for scan-to-pay and deep-link handling in the app.

5. Database model

Postgres on Supabase, all in the public schema, all with RLS. The tables a mobile app will touch:

  • merchants — one row per account, keyed to the auth user id: business_name, email, logo_url, deposit_address (TRC20 settlement address), webhook_url, status, kyc_status, gasless_enabled.
  • payments — invoices: amount, net_amount, fee_amount, reference, description, wallet_address, chain, asset, status (waiting | confirming | completed | expired), expires_at, confirmed_at, plus optional pay_with_* swap fields.
  • transactions — on-chain legs matched to a payment: tx_hash, wallet, amount, status. Read-only for merchants.
  • api_keyslabel, key_id, revoked, last_used_at. The secret column is not readable by clients.
  • kyb_applications / kyb_documents — onboarding: legal name, licence details, director contact, status (draft → submitted → under_review → approved | rejected) and uploaded files in a private storage bucket.
  • wallet_links, payment_handles, merchant_assets — linked addresses (with signature verification), $handle routing, and per-chain settlement addresses.
  • plans, merchant_billing, platform_fees — the subscription model (flat monthly tiers, 0% per-transaction fee).
  • Labs tables: subscription_plans, subscription_mandates, payment_receipts, gas_sponsorships.
  • Platform: user_roles (admin | merchant, checked through a security-definer function), audit_logs, waitlist_leads, platform_settings (admin-only).

Generated TypeScript types for every table live in the web repo at src/integrations/supabase/types.ts — copy that file into the mobile project for full type safety instead of hand-writing models.

6. TRON / USDT settlement

Payrano is non-custodial. There is no third-party processor (no NOWPayments, no CoinPayments) and no Payrano-controlled hot wallet. The customer sends USDT directly to the merchant's own TRC20 address; Payrano records the request and reconciles it against the reference.

Chain metadata (TRON, Ethereum, BSC, Polygon, Bitcoin), address validation, explorer URLs and wallet deep links are implemented client-side in the web app and can be mirrored in the mobile app. TRON is the only settlement chain in production; the others exist for pay-with-anything (Labs).

Important gap to plan around:

Confirmation today is reference-driven rather than driven by a persistent node watcher. If the mobile app promises live "payment received" push notifications, a chain indexer has to exist first — the recommended shape is a TronGrid (or self-hosted full-node) poller that watches each merchant's deposit_address for USDT TRC20 transfers, writes a transactions row, flips the matching payment to completed, and then fans out push. No TronGrid API key is in use yet, so the mobile team should not assume one exists.

7. Design system (for pixel parity)

Payrano is dark-first with a light mode. Colours are OKLCH tokens; typography is Inter throughout. Use these values verbatim so the app and the site look like one product.

Dark (default)
  background        oklch(0.06 0 0)
  foreground        near-white
  primary           oklch(0.585 0.235 288)   /* purple */
  primary-glow      oklch(0.66 0.2 268)
  primary-foreground oklch(0.99 0 0)

Light
  background        oklch(0.99 0.002 288)
  primary           oklch(0.545 0.235 288)
  primary-glow      oklch(0.62 0.2 268)

Type    Inter (400 / 500 / 600), tight tracking on headings
Radius  cards 1rem–1.5rem, buttons fully rounded (pill)
Surface translucent "glass" panels: 1px border at ~60% opacity + soft shadow
Accents purple at 12% opacity for icon chips and badges

The full token set (surfaces, sidebar, borders, muted, destructive, both themes) lives in src/styles.css in the web repo. The logo is a transparent "P" mark — request the asset rather than screenshotting it, so corners stay clean on any background.

8. What to build first

Recommended first three surfaces, in order:

  1. Merchant dashboard — balances, today's volume, recent payments. Pure Supabase reads with the user session; nothing new needed on the backend.
  2. Create + share a payment link with QR — the highest-value mobile action (take a payment at the counter). One POST /payments call, then render checkout_url as a QR and a share sheet.
  3. Push on incoming payment — worth doing third because it depends on the chain watcher described in section 6. Until that exists, use Supabase realtime on the payments table so the app updates instantly the moment a payment is marked completed.

Scan-to-pay, $handle lookup and multi-chain wallet balances belong to the Payrano Wallet product rather than the merchant app — the endpoints in section 4 already support them when you get there.

9. OpenAPI / Postman

A machine-readable spec for every endpoint above is served at https://payranosolutions.com/api/public/v1/openapi.json. Import it straight into Postman, Insomnia or a codegen tool instead of copying payloads by hand.

Rest of the product documentation, including the payment lifecycle and security model.

Back to docs