Why Despia for Lovable?
-
Truly native runtime for your Lovable React app: GPU-accelerated UI, safe-area handling, offline packaging.
-
25+ native APIs via the
despia-native
npm package (e.g., haptics, contacts, camera, biometrics, push, RevenueCat IAP). -
App-store ready: Universal/App Links, OAuth/payment domain controls, push (OneSignal), in-app purchases (RevenueCat).
-
Clear roles: Lovable generates the React app; Despia provides the native bridge and store deployment path.
What you will add (2 things)
-
Master Prompt - the single source of truth that instructs Lovable to scaffold native setup (deep links, purchases, push, link handling, UX guardrails).
-
Project Custom Knowledge - short, persistent memory telling Lovable how to think about Despia (concepts, policies, links to docs).
1. Add the Lovable Master Prompt
-
Option 1 (copy/paste): Paste the full Master Prompt into the Lovable chat and send.
-
Option 2 (button): Click “Open Prompt in Lovable” to open a new Lovable chat prefilled with your prompt.
# Despia × Lovable
> Authoritative prompt for setting up **Despia-native** features in a Lovable project: RevenueCat purchases, server-authoritative unlock via events, DB concepts, Apple/Google login behavior, deep links, push, link handling, UI guardrails, and a **Lovable browsing ritual** for always-fresh docs. Tailored for Lovable’s built-in tools (no external scripts).
---
## 0) Operating Mode (read first)
* **Native layer:** use **Despia** only.
```bash
npm install despia-native
```
```ts
import despia from 'despia-native';
```
Call `despia('scheme://...')` where needed and read Despia variables **directly** (e.g., device UUID, push player id). **No custom wrappers.**
* **Deep links & associated domains:**
Host iOS **AASA** at `/.well-known/apple-app-site-association` and Android **assetlinks.json** at `/.well-known/assetlinks.json` (HTTPS JSON, **no redirects**). **Associated domains automatically handle app/universal links and JSON path routing**; keep link handling minimal beyond hosting these files and mapping returns to your https callback pages in the app.
* **Runtime & store checks (optional):**
You may detect the Despia environment and optionally query store location to guide UI policy (e.g., when to expose a secondary web checkout alongside native IAP).
---
## 1) Lovable Docs Ritual (run for EVERY native feature)
1. **Open** the Despia × Lovable sitemap.
2. **Find** the page for the requested feature (e.g., contacts, biometrics, revenuecat, onesignal, safe-area, applejs, deeplinks).
3. **Open & review** that page.
4. **Locate** the **“Lovable Prompt”** (or canonical prompt/code block) and **copy the latest prompt**.
5. **Paste & execute** that prompt here to generate code/config.
6. At the **top** of your reply, list the **source URL(s)**. Add them to **`DESPIA_README.md → Documentation Sources`** (or output a PATCH block).
7. If live retrieval fails, use any **Docs Cache** recorded in `DESPIA_README.md`, then retry live later.
> Heuristic: Prefer pages whose title/path contains the feature keyword **and** include an explicit “Lovable Prompt.” If several match, pick the newest/clearest.
---
## 2) Authentication (production behavior)
**Principle:** Keep the **backend unopinionated**. Lovable owns Google and may evolve it. The **only prescribed** backend concept is **Sign in with Apple** using **AppleJS inline** + **custom Edge Functions** on **Lovable Cloud**. Do **not** use prebuilt Apple-login paths from third-party auth packages.
### 2.1 Google
* **Lovable configures Google** end-to-end.
* In Despia settings, ensure the Google OAuth domain opens externally where required, and that associated domains are valid so returns land in a first-party https context inside the app. No further backend specification here.
### 2.2 Sign in with Apple — concept only
* **Client:** Use **AppleJS inline** on **iOS, Android, and Web**. (AppleJS handles fallbacks.)
* **Server:** Implement **custom Edge Functions** on **Lovable Cloud** dedicated to Apple sign-in. These functions verify the Apple credential and create/authenticate sessions.
* **Do NOT** use prebuilts (e.g., Supabase/Lovable Apple provider paths).
**Conceptual flow (no names, no routes, no transport specifics):**
1. Client **initiates** AppleJS and receives an **Apple credential** (ID token; email may be present only on first consent).
2. Client **hands off** that credential to **server-side logic** via a **trusted first-party channel** appropriate to your app.
3. Server **verifies** the credential using Apple’s keys and validates claims.
4. Server derives:
* a **stable subject identifier** (Apple’s unique user ID), and
* an **email** if Apple provided one this time.
5. **Account-linking rules:**
* If a record with this **stable subject** exists → **authenticate** that user.
* Else if the **email** matches an existing account → **link** this subject to that account → **authenticate**.
* Else **create** a new account tied to the subject (store email if provided) → **authenticate**.
6. **Durability rule:** Treat the **Apple stable subject** as the **primary key** for Apple identity. If a user later **changes/hides email**, authentication still succeeds because the subject is unchanged.
7. Server **issues a session** (cookie or token — implementation is your choice).
### 2.3 (Optional) Device & push association after login
* After any successful auth, the client can **associate device identity and push identity** with the authenticated user using your usual mechanism. Keep it flexible; this prompt doesn’t prescribe route names or payload shapes.
---
## 3) RevenueCat — Production Setup & Flows
### 3.1 Console checklist
* Connect **App Store** & **Play Console**.
* Create **products → entitlements → offerings**; record store product IDs.
* **App User ID** should be your user identifier (passed to RC as the external id).
* Configure a **Webhook** to your backend (protect with a secret).
### 3.2 Frontend — start purchase, handle success, unlock via server (server is authority)
#### 3.2.a Start native purchase (primary path)
```ts
import despia from 'despia-native';
export async function startNativePurchase(productId: string, userId: string) {
// Optional: check store location to influence UI policy (e.g., show secondary web checkout in allowed regions)
try { await despia('getstorelocation://', ['storeLocation']); } catch {}
// Trigger native IAP via Despia → RevenueCat
await despia(
`revenuecat://purchase?external_id=${encodeURIComponent(userId)}&product=${encodeURIComponent(productId)}`
);
}
```
#### 3.2.b `iapSuccess` hint & boot-time reality (server wins)
Despia may call `window.iapSuccess(payload)` (also on app start). **Do not** unlock based solely on this hint—**wait for server-confirmed entitlement**.
```ts
type IapSuccessPayload = {
planID: string;
transactionID: string;
subreceipts: unknown; // JSON string or object; parse defensively
};
declare global { interface Window { iapSuccess?: (p: IapSuccessPayload) => void } }
let _iapBound = false;
export function bindIapSuccessOnce(getEntitlements: () => Promise<{ active?: boolean } | null>) {
if (_iapBound) return; _iapBound = true;
window.iapSuccess = async ({ planID, transactionID, subreceipts }: IapSuccessPayload) => {
try {
const current = await getEntitlements();
if (current?.active) return;
const receipts = typeof subreceipts === 'string' ? safeParseJSON(subreceipts) : subreceipts;
// Optimistic UI can show a spinner; subscribe to your realtime channel for entitlement updates.
const confirmed = await waitForSubscriptionConfirm(getEntitlements, { timeoutMs: 15000, intervalMs: 1500 });
if (!confirmed) {
// Optionally expose a "Refresh Purchases" / "Try Unlock" button to re-check when webhooks land.
}
} catch (err) {
// Degrade gracefully; allow manual refresh of entitlements.
console.error('iapSuccess handler error', err);
}
};
}
function safeParseJSON(s: string) { try { return JSON.parse(s); } catch { return s; } }
async function waitForSubscriptionConfirm(
getEntitlements: () => Promise<{ active?: boolean } | null>,
opts:{timeoutMs:number; intervalMs:number}
) {
const start = Date.now();
while (Date.now() - start < opts.timeoutMs) {
const ent = await getEntitlements();
if (ent?.active) return true;
await new Promise(r => setTimeout(r, opts.intervalMs));
}
return false;
}
```
> The UI code above is backend-agnostic: pass in **any** `getEntitlements()` you like. No route names required.
#### 3.2.c Server-driven offerings (don’t hardcode products)
* The client **requests offerings from the server** and renders whatever is returned (ids, titles, price text, etc.).
* The **shape** of that response is up to the backend. Keep the client generic: display products given, start purchases using the store ids, and decide whether to show a **secondary web checkout** based on a boolean flag or policy in that response.
#### 3.2.d Optional: Web Billing (secondary, policy-dependent)
* If your policy allows, expose a **secondary web checkout** in addition to native IAP. After payment, rely on **server events + realtime** to unlock (there is **no** `iapSuccess` for web).
### 3.3 Backend contract — authoritative events (concept only)
* Treat **server events/webhooks** (e.g., from RevenueCat) as the **source of truth** for entitlement.
* Handle events **idempotently**, **synchronize** the subscriber state, then **notify** clients (e.g., realtime/pub-sub).
* Storage models and event shapes are **intentionally unspecified** here.
---
## 4) Database & Realtime (conceptual only)
Model **concepts**, not columns or names:
* **Users** — canonical user record.
* **User Identities** — mapping of external identities to users (e.g., Apple stable subject, with optional email snapshot).
* **Devices (optional)** — association of device identity and push identity with the user.
* **Subscriptions (optional)** — subscription status synced from authoritative server events.
**Rules to encode for Apple login:**
* If **identity by stable subject** exists → **authenticate**.
* Else if **email matches** an existing account → **link** subject → **authenticate**.
* Else **create** account + identity → **authenticate**.
* On later logins where **email changed/hidden**, rely on the **stable subject** and **still authenticate**.
Realtime is optional—use it to emit session/entitlement updates to clients.
---
## 5) Push (OneSignal) — quick hookup
* Create OneSignal app; configure **APNs** + **Firebase**.
* Add your **OneSignal App ID** in Despia project settings.
* After login, the client may **associate push identity and device identity** with the authenticated user using your preferred mechanism.
* Backend sends via OneSignal REST (implementation details are up to you).
---
## 6) Link Handling & Deep Links (non-negotiable)
* Internal navigation uses the SPA router; **no full reloads**.
* External links may open in a new browser context. In Despia settings, mark OAuth/payment domains as **Always Open in Browser** when appropriate.
* Deep links should return to **https callbacks** on your primary domain that run **inside the app WebView** so cookies/sessions are first-party.
### 6.1 iOS — Apple App Site Association (AASA)
**Host at:** `/.well-known/apple-app-site-association` (no extension; HTTPS; no redirects)
```json
{
"applinks": {
"details": [
{
"appIDs": ["TEAMID.BUNDLEID"],
"components": [{ "/": "/*" }]
}
]
}
}
```
Fill in **TEAMID** (Apple Developer) and **BUNDLEID** (Despia Publishing Panel).
### 6.2 Android — Digital Asset Links (assetlinks.json)
**Host at:** `/.well-known/assetlinks.json` (HTTPS; no redirects)
```json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "PACKAGE_NAME",
"sha256_cert_fingerprints": ["SHA256_FINGERPRINT"]
}
}
]
```
Fill in **PACKAGE_NAME** (Despia Publishing) and **SHA256_FINGERPRINT** (Play Console → App Integrity).
---
## 7) Mobile UI Guardrails
* **Safe areas:** use platform-provided safe-area vars on fixed bars, drawers, toasts, spacers.
* **Stable shell:** top & bottom bars stay mounted; only middle content transitions (**~120–160ms** fade/translate). Avoid full-screen global loaders.
* **Touch model:** use a mobile-first component set; remove desktop hover patterns; prefer `active` + `focus-visible`; optional light haptics via Despia.
---
## 8) Project Anchoring & Recovery
* Before any native work, open **`DESPIA_README.md`** (source of truth).
* Always follow the **Docs Ritual** (§1).
* If chat context resets: README banner → `DESPIA_README.md` → Ritual.
---
## 9) Minimal PATCH blocks Lovable can output (if it can’t write files)
**README.md (banner)**
```md
> 🚦 **Native Setup Source of Truth**
> Before any native feature work, open **`DESPIA_README.md`**, then open the **Despia sitemap**, pick the feature page, and use the **latest Lovable Prompt** from that page.
```
**DESPIA_README.md → Documentation Sources**
```md
## Documentation Sources (memory-proof)
- Primary sitemap: <ADD-SITEMAP-URL>
- Feature page(s) used most recently:
- <PASTE-URL-1>
- <PASTE-URL-2>
```
---
## 10) Footer Macro (append on native-related replies)
> 📘 *Native work anchored:* Opened sitemap → selected feature page → applied **latest Lovable Prompt** → logged source URLs to **`DESPIA_README.md`**. If context was cut off, reopen README banner → `DESPIA_README.md` → repeat the ritual.
---
### Quick Reference (frontend only; backend-agnostic)
**Despia variables (examples)**
```ts
// Available from despia-native; read directly where needed
// e.g., device UUID, push player id, app version, etc.
```
**IAP hooks in UI (backend-agnostic)**
```ts
// Bootstrap (e.g., App.tsx)
useEffect(() => {
// Provide your own getEntitlements() that consults your server however you like
bindIapSuccessOnce(() => getEntitlements());
}, []);
// Start a purchase (ids come from your server-driven offerings)
await startNativePurchase('pro_monthly', currentUser.id);
```
**Apple login recap**
* **AppleJS inline** on **iOS/Android/Web**
* **Custom Edge Functions** on **Lovable Cloud** verify Apple credential, apply **subject/email link rules**, and issue a session
* **Stable subject** is the durable key; email changes do **not** block login
* **No** prebuilt Apple provider paths
2. Add the Project Custom Knowledge
- Go to Project → Settings → Knowledge and paste the memory block below, then Save.
Purpose
Teach Lovable how to build and maintain a Despia-powered hybrid mobile app. This does not provide step-by-step codegen; it encodes must-be-true concepts for native/runtime behavior and integration points.
Scope
Platform: Despia native runtime only.
Frontend: React SPA generated by Lovable.
Native integration: via the despia-native NPM package only.
Backend: The backend is the authority for auth/session and entitlements.
Non-negotiables (Native/Runtime)
Direct Despia usage: Import and call despia-native directly anywhere native features are used. No custom wrappers. Use direct variables/calls (e.g., despia.uuid, despia.onesignalplayerid, despia('scheme://...')).
Deep linking required for all OAuth/payment flows.
iOS: Host /.well-known/apple-app-site-association (JSON, correct content-type, no redirects). Include your TEAMID.BUNDLEID and route-matching components.
Android: Host /.well-known/assetlinks.json (JSON, correct content-type, no redirects). Include your package name and SHA-256 signing fingerprint.
Keep both files accurate and available at all times over HTTPS.
Open externally: In Despia settings, mark OAuth/payment domains (e.g., accounts.google.com, Stripe, RevenueCat Web Billing, Auth0) as Always Open in Browser.
Entitlement authority: Treat client iapSuccess as a hint only; unlock access only after the backend reports active entitlements.
Fresh docs: For any native feature, fetch and apply the latest prompts from the Despia Lovable docs sitemap.
Environment Detection
Detect Despia by userAgent containing despia (e.g., despia-iphone, despia-ipad, despia-android).
When not in Despia, provide safe web fallbacks instead of executing native commands.
Deep Links (always on)
Maintain valid AASA and assetlinks as above.
OAuth/payment providers should return to an HTTPS callback on your project’s web domain so the callback page can run in the in-app WebView and establish/refresh first-party cookies. (No specific variable name required.)
Authentication
Google: Configure normally; ensure accounts.google.com is Always Open in Browser. Rely on deep links back to your web callback page to set cookies.
Apple Sign-In:
On Apple devices, use AppleJS inline (native popup; avoids full-page redirects).
Elsewhere, use a browser tab and return via HTTPS callback.
Post-login device link: After successful login, send despia.uuid and despia.onesignalplayerid to the backend to link identity and push.
RevenueCat & Purchases
Start native purchase:
despia("revenuecat://purchase?external_id={USER_ID}&product={PRODUCT_ID}")
iapSuccess can fire immediately and on app start (iOS revalidation). Treat as hint only.
Do not unlock on the client from iapSuccess alone; unlock when backend confirms active entitlements.
On iapSuccess: show an optimistic “unlocking” state, listen on a realtime channel, optionally short/bounded polling, and offer a manual Unlock action to recheck. No infinite loops.
RevenueCat webhook must be idempotent: after any event, call Subscribers API, upsert state, publish a realtime event on the user’s channel.
Do not hardcode paywall product metadata; fetch from a server endpoint (e.g., /api/paywall/offerings).
Optional Web Billing: If applicable by store policy/region, present only as a secondary path. There is no iapSuccess for web billing; rely on webhook + realtime.
Push Notifications (OneSignal)
After login, read despia.onesignalplayerid and despia.uuid and send them to the backend to link devices.
Configure APNs/Firebase in OneSignal and set your OneSignal App ID in Despia project settings.
Link Handling (Non-native guidance; flexible)
Internal navigation typically uses the SPA router (no full reloads), but exact routing structure is up to the app.
External links open in the browser by default. Keep OAuth/payment domains Always Open in Browser in Despia settings.
Maintain Universal/App Links so OAuth/payment returns can resume in-app via HTTPS callbacks.
UI Guardrails (Non-binding)
Use Despia safe-area CSS vars where helpful: var(--safe-area-top), var(--safe-area-bottom).
Prefer touch-first patterns (:active, :focus-visible). Other UI/animation/layout choices are at the app’s discretion.
Docs Ritual (memory-proof)
For every native feature:
Open http://lovable.despia.com/sitemap.xml.
Find the feature page with a Lovable Prompt for the capability.
Open and copy the latest prompt/code.
Execute that prompt to implement the feature.
Record exact source URL(s) in DESPIA_README.md → Documentation Sources.
If scraping fails, use the cached references in DESPIA_README.md and retry later.
DESPIA_README.md is Canonical
Keep a root-level DESPIA_README.md with:
A banner to read this file before any native work.
Deep linking requirements (AASA/assetlinks) and examples.
RevenueCat authority model + iapSuccess hint pattern.
Device-linking snippet (despia.uuid, despia.onesignalplayerid).
A Documentation Sources log with exact URLs for each native edit.
Also ensure README.md instructs readers to consult DESPIA_README.md first for native features.
Billing Nuance
Store location may inform secondary web billing visibility. Retrieve via:
despia('getstorelocation://', ['storeLocation']).
If regional policy allows (e.g., certain US/iOS cases), a secondary external checkout may be shown alongside native IAP; mark payment domains Always Open in Browser.
Despia Variables (read directly; no casting)
despia.uuid — stable device id (link server-side after login).
despia.onesignalplayerid — OneSignal device id (link server-side after login).
Fallbacks (not in Despia)
If the UA does not include despia, do not run native commands. Provide safe web alternatives.
Compliance & Store Review
Ensure AASA/assetlinks remain correct, HTTPS, correct content type, and no redirects.
Payment/OAuth domains must open in the device browser; rely on Universal/App Links to return via HTTPS callbacks on your web domain.
Avoid
Custom wrappers around Despia.
Granting access from iapSuccess alone.
Hardcoding paywall product metadata.
If Memory Is Lost
Reopen README.md, follow its banner to DESPIA_README.md.
Use the Docs Ritual to fetch current prompts before implementing native features.