Roochip Lite — Integration Guide
Complete reference for the Rooverse development team: how ROO credits work, how to integrate them, and what to build.
What is Roochip Lite?
Roochip Lite is a standalone B2B credit issuing platform. Rooverse does not own any payment processing, credit storage, or ledger logic. Instead, Rooverse calls Roochip’s REST API and receives credit events via signed webhooks.
Think of Roochip Lite as the credit bank. Rooverse is the app that uses it. Users never interact with Roochip directly — they see only the Rooverse UI.
The ROO Currency
One unified wallet
Purchased ROO and earned ROO are the same currency in one wallet. There is no distinction between “bought credits” and “reward credits” in the balance — only the ledger history records how credits were obtained.
How to display ROO in the UI
| Value | Display | Notes |
|---|---|---|
5.00 | 5 ROO | Hide trailing decimal for whole numbers |
5.50 | 5.5 ROO | Show 1 decimal if meaningful |
120.01 | 120.01 ROO | Full precision in transaction history |
0.01 | +0.01 ROO | Prefix with + for reward toasts |
System Architecture
How all the components connect:
Key principle
When a user pays, money goes to Roochip Lite’s payment accounts. Roochip is the merchant of record. Rooverse has zero payment compliance obligations for ROO purchases.
Rollout Phases
- Buy button returns “coming soon”
- Users request a free 5 ROO promo code
- Admin generates & shares the code
- User enters code in app to redeem
- One request + one redemption per user, ever
- Stripe checkout via WebView (web & tablet)
- Apple IAP (iOS)
- Google IAP (Android)
- Package selection UI in app
- Checkout returns real checkout_url
POST /v1/checkout switches from returning 503 coming_soon to a real checkout_url. No other changes to the Rooverse integration are required.
User Wallet & Balance
Every Rooverse user has exactly one ROO wallet, managed entirely by Roochip Lite. The wallet is created automatically on the first API call for that user — Rooverse does not need to explicitly register users.
Where the balance lives
| Location | What it is | When to use |
|---|---|---|
| Roochip Lite database | Authoritative balance | Always fetch live before any deduction |
users.roochip_balance (Rooverse DB) | Cached balance — updated by webhook | Display in UI, leaderboards, low-latency reads |
When to refresh the displayed balance
- On wallet screen open — fetch live from API
- After receiving a
credits.issuedwebhook — update cache withnew_balance - After a successful deduct call — use
new_balancefrom response - After a successful reward call — use
new_balancefrom response - After promo code redemption — use
new_balancefrom response
Wallet screen — what to show
| UI element | Data source | Notes |
|---|---|---|
| Current balance | GET /v1/users/:id/balance → balance | Fetch live on screen open |
| Transaction history | GET /v1/users/:id/transactions | Paginated, 20 per page |
| “Buy More” button | — | Phase 1: shows coming soon + code request. Phase 2: opens checkout. |
| Transaction type label | entries[].type | purchase / reward / deduction / adjustment |
| Amount sign | entries[].credits_delta | Positive = added, negative = deducted |
Earning ROO — Reward System
Users earn ROO by performing actions inside Rooverse. Rooverse owns all reward rules — Roochip has no knowledge of triggers, caps, or amounts. Rooverse applies its own logic before calling the reward API.
Suggested reward values
| Trigger | ROO | reason string | Cap recommendation |
|---|---|---|---|
| Like a post | 0.01 | like | Max 50 rewarded likes/day per user |
| Follow a user | 0.05 | follow | Max 10 rewarded follows/day per user |
| Daily login streak | 0.10 | daily_login | Once per calendar day per user |
| Create a post | 0.02 | post_created | Max 5/day |
| Referral signup | 5.00 | referral_bonus | Once per referred user |
| 7-day streak milestone | 1.00 | streak_7day | Once per streak completion |
These are suggestions only. Rooverse controls the amounts, triggers, and caps entirely.
POST /v1/users/:id/reward, Roochip issues the credits unconditionally. Build daily caps, cooldowns, and deduplication on the Rooverse side first.
Anti-abuse implementation pattern
// Rooverse backend pseudocode
async function handleLikeEvent(userId, postId) {
// 1. Rooverse rules check
const todayLikes = await db.countRewardsToday(userId, 'like');
if (todayLikes >= 50) return;
const alreadyRewarded = await db.rewardExists(userId, postId, 'like');
if (alreadyRewarded) return;
// 2. Only now call Roochip
await roochip.post(`/v1/users/${userId}/reward`, {
credits: 0.01,
reason: 'like',
reference_id: postId
});
}
Spending ROO — Paid Features
When a user activates a paid feature, the Rooverse backend calls the deduct API. Credits are deducted atomically — PostgreSQL row-level locking prevents concurrent overdrafts.
Example feature costs
| Feature | Suggested cost | reason string |
|---|---|---|
| AI-generated content | 10.00 ROO | ai_generation |
| Premium filter / effect | 5.00 ROO | premium_filter |
| Boost post visibility | 20.00 ROO | post_boost |
| Profile badge / cosmetic | 50.00 ROO | profile_badge |
| Unlock exclusive content | 15.00 ROO | content_unlock |
POST /v1/users/:id/deduct first and verify a 200 response before executing the feature. If you run the feature first and deduction fails, the user receives it for free. On 402 Insufficient credits — block the feature and show the upsell prompt.
Deduct-then-run pattern
async function runAIGeneration(userId, jobId) {
const COST = 10.00;
// 1. Deduct first (atomic, row-locked)
const result = await roochip.post(`/v1/users/${userId}/deduct`, {
credits: COST,
reason: 'ai_generation',
reference_id: jobId
});
if (result.status === 402) {
return { error: 'insufficient_credits', required: COST };
}
// 2. Credits confirmed deducted - now run the feature
await runAIJob(jobId);
// 3. Update Rooverse UI cache
await db.users.update({ roochip_balance: result.data.new_balance }, userId);
}
Buying ROO — Purchase Flow
Users top up their ROO wallet by purchasing credit packages. Packages are defined and priced by Roochip Lite. Rooverse fetches and displays them — it never sets prices or creates packages.
Available packages (current seed data)
| Name | ROO Credits | Price | Value per ROO |
|---|---|---|---|
| Starter Pack | 10 ROO | $10.00 | $1.00 per ROO |
| Popular Pack | 25 ROO | $25.00 | $1.00 per ROO |
| Premium Pack | 50 ROO | $50.00 | $1.00 per ROO |
Phase 1 — “Coming soon” response
Until Stripe is configured, POST /v1/checkout returns:
HTTP 503
{
"status": "coming_soon",
"message": "Payment system coming soon! In the meantime, request a free 5-credit code.",
"request_code_endpoint": "POST /v1/codes/request"
}
Display this message and show a “Request a free code” button immediately below it.
Phase 2 — Real checkout (future)
POST /v1/checkout will return a checkout_url. Web: open in in-app WebView. Mobile: use native IAP (see Payment Methods).
Payment Methods
| Method | Platform | Who processes | Typical fee | Status |
|---|---|---|---|---|
| Stripe | Web / WebView | Roochip Lite | ~1.5–2.9% + 30p | Pending keys |
| Apple IAP | iOS native | Apple → Roochip Lite | 15–30% | Pending keys |
| Google IAP | Android native | Google → Roochip Lite | 15–30% | Pending service account |
| Promo Code | Any | Admin-issued, free | None | Live now |
credits.issued webhook.
Why mobile must use native IAP
Apple and Google require that in-app purchases of digital goods go through their own payment systems — not Stripe. The Rooverse mobile app sends the receipt / purchase token to the Rooverse backend, which forwards it to Roochip for server-side verification and credit issuance.
Mobile IAP call (from Rooverse backend)
// iOS: after successful StoreKit purchase
const response = await fetch('https://nevis-credit-system-test.vercel.app/iap/apple', {
method: 'POST',
headers: { 'X-API-Key': process.env.ROOCHIP_API_KEY },
body: JSON.stringify({
external_user_id: userId,
receipt_data: appleReceiptBase64, // from StoreKit
package_id: selectedPackage.id,
tenant_id: TENANT_ID
})
});
// Roochip verifies with Apple, issues credits, fires webhook
Ownership Boundaries
| Concern | Roochip Lite | Rooverse |
|---|---|---|
| Stripe account & keys | Roochip owns | None |
| Apple IAP verification | Roochip owns | None |
| Google IAP verification | Roochip owns | None |
| Credit wallet & ledger | Roochip owns | None |
| Credit packages & pricing | Roochip owns | None |
| User identity & auth | None | Rooverse owns, sends user IDs |
| Balance display in UI | None | Rooverse reads via API |
| Reward rules & caps | None | Rooverse decides, calls reward API |
| Deducting for features | None | Rooverse calls deduct API |
| Promo code generation | Admin generates via Roochip API | None |
| Promo code redemption UI | None | Rooverse shows input field |
Authentication
All Rooverse → Roochip calls are server-to-server only. Never call the Roochip API from frontend JavaScript, React Native bundles, or a WebView.
X-API-Key: rc_test_rooverse_key_123 <!-- test key; replace with production key for live -->
Environment Variables (Rooverse backend)
# Provided by Roochip Lite during onboarding
ROOCHIP_API_KEY=rc_test_rooverse_key_123 # replace with production key for live
ROOCHIP_BASE_URL=https://nevis-credit-system-test.vercel.app
ROOCHIP_WEBHOOK_SECRET=roochip_provided_secret
Phase 1: Promo Code Flow
POST /v1/checkout. Returns 503 status: coming_soon.POST /v1/codes/request with the user’s ID. On success, show confirmation. On 409, show “You have already requested a code.”POST /v1/codes/request
{ "external_user_id": "rooverse-user-123" }
GET /v1/admin/codes/requests?status=pending, then POST /v1/admin/codes/generate. System returns a code like ROO-A3F29C1B. Admin shares with user via email, DM, etc.POST /v1/codes/redeem. On success, 5 ROO added instantly.POST /v1/codes/redeem
{ "external_user_id": "rooverse-user-123", "code": "ROO-A3F29C1B" }
new_balance from the redeem response to update balance display immediately. Roochip also fires a credits.issued webhook confirming the issuance.Promo Code UI — What to Build
Wallet / Buy screen
| UI Element | Behaviour |
|---|---|
| Current balance | Fetch GET /balance on screen open. Display as “120 ROO” |
| “Buy More ROO” button | Calls POST /v1/checkout. On 503 shows Phase 1 message |
| “Payment coming soon” banner | Shown when checkout returns 503 |
| “Request a free code” button | Calls POST /v1/codes/request. Hide after first request. |
| “Enter promo code” input | Always visible or toggled. Submits to POST /v1/codes/redeem |
| Transaction history | Calls GET /transactions. Load more on scroll. |
After requesting a code
On success → “Your request has been received. We’ll send you a code soon.”
On 409 → “You’ve already submitted a request. Check your messages for your code.”
roochip_code_requested BOOLEAN DEFAULT FALSE to the users table. Set to TRUE when the user taps “Request Code”. Use this to show “Code requested — check your messages” instead of the button on subsequent visits.
Promo Code Endpoints
// Request
{ "external_user_id": "rooverse-user-123" }
// 201 Success
{ "message": "Request received. An admin will generate your code shortly.", "status": "pending" }
// 409 Already requested
{ "error": "You have already requested a promo code. Each account can only request once." }
// Request
{ "external_user_id": "rooverse-user-123", "code": "ROO-A3F29C1B" }
// 200 Success
{ "message": "5 ROO credits added to your account.", "credits_issued": 5, "new_balance": 5 }
| Status | Meaning | Show in UI |
|---|---|---|
404 | Code not found | “This code is invalid.” |
403 | Code for a different user | “This code was not issued to this account.” |
409 | Already used | “This code has already been redeemed.” |
{
"requests": [
{ "id": "uuid", "external_user_id": "rooverse-user-123", "status": "pending", "created_at": "..." }
],
"count": 1
}
Filter by status: pending | fulfilled | rejected
// Request
{ "external_user_id": "rooverse-user-123" }
// 201 Success
{
"code": "ROO-A3F29C1B",
"external_user_id": "rooverse-user-123",
"credits": 5,
"message": "Share this code with rooverse-user-123. It is single-use."
}
Get Available Packages
{
"packages": [
{ "id": "uuid", "name": "Starter Pack", "credits_to_issue": 10, "price_amount": 1000, "currency": "usd" },
{ "id": "uuid", "name": "Popular Pack", "credits_to_issue": 25, "price_amount": 2500, "currency": "usd" },
{ "id": "uuid", "name": "Premium Pack", "credits_to_issue": 50, "price_amount": 5000, "currency": "usd" }
]
}
price_amount is in cents (USD). Divide by 100: 1000 → $10.00. Pricing model: 1 Roochip Lite = $1 USD (stable 1:1 peg).Get User Balance
{ "external_user_id": "rooverse-user-123", "balance": 115.50, "updated_at": "2026-03-30T10:00:00Z" }
Get Transaction History
{
"external_user_id": "rooverse-user-123",
"total": 3, "page": 1, "limit": 20,
"entries": [
{ "type": "purchase", "credits_delta": 120.00, "balance_before": 0, "balance_after": 120.00, "created_at": "..." },
{ "type": "reward", "credits_delta": 0.01, "balance_before": 120, "balance_after": 120.01, "created_at": "..." },
{ "type": "deduction", "credits_delta": -10.00, "balance_before": 120.01, "balance_after": 110.01, "created_at": "..." }
]
}
Deduct Credits
// Request
{ "credits": 10.00, "reason": "ai_generation", "reference_id": "rooverse-job-abc123" }
// 200 Success
{ "external_user_id": "rooverse-user-123", "credits_deducted": 10.00, "new_balance": 110.00 }
// 402 Insufficient credits
{ "error": "Insufficient credits" }
Issue a Reward
// Request
{ "credits": 0.01, "reason": "like", "reference_id": "post-uuid-abc" }
// 200 Success
{ "external_user_id": "rooverse-user-123", "credits_rewarded": 0.01, "new_balance": 120.01 }
Inbound Webhooks — Roochip → Rooverse
After any credit issuance (purchase, IAP, promo, reward), Roochip POSTs to your webhook URL:
POST https://rooverse.app/webhooks/roochip
Webhook payload
{
"event": "credits.issued",
"external_user_id": "rooverse-user-123",
"credits_issued": 120.00,
"new_balance": 320.00,
"payment_source": "stripe",
"timestamp": "2026-03-30T10:00:00Z"
}
payment_source: stripe | apple_iap | google_iap | promo_code
What Rooverse must do on receipt
401 if invalid. See verification code below.roochip_events with a unique key.users.roochip_balance = new_balance for external_user_id.Webhook Signature Verification
const crypto = require('crypto');
function verifyRoochipWebhook(req, secret) {
const signature = req.headers['x-roochip-signature'];
if (!signature) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(req.body))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
app.post('/webhooks/roochip', express.json(), async (req, res) => {
if (!verifyRoochipWebhook(req, process.env.ROOCHIP_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event, external_user_id, credits_issued, new_balance, payment_source } = req.body;
if (event === 'credits.issued') {
await db.users.update({ roochip_balance: new_balance }, { where: { id: external_user_id } });
await db.roochipEvents.create({ event, external_user_id, credits_issued, new_balance, payment_source });
}
res.sendStatus(200);
});
Rooverse Database Changes Required
These are the only database changes Rooverse needs. All wallet logic lives in Roochip’s database.
1. Cached balance column (required)
ALTER TABLE users
ADD COLUMN IF NOT EXISTS roochip_balance NUMERIC(12,2) NOT NULL DEFAULT 0;
CREATE INDEX IF NOT EXISTS idx_users_roochip_balance ON users(roochip_balance);
GET /balance before any deduction to prevent races.
2. Event log table (required)
CREATE TABLE IF NOT EXISTS roochip_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_user_id TEXT NOT NULL,
event TEXT NOT NULL, -- 'credits.issued'
credits_issued NUMERIC(12,2),
new_balance NUMERIC(12,2),
payment_source TEXT, -- stripe|apple_iap|google_iap|promo_code
raw_payload JSONB, -- full webhook body
received_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_roochip_events_user ON roochip_events(external_user_id);
CREATE INDEX IF NOT EXISTS idx_roochip_events_time ON roochip_events(received_at DESC);
3. Code request flag (recommended)
ALTER TABLE users
ADD COLUMN IF NOT EXISTS roochip_code_requested BOOLEAN NOT NULL DEFAULT FALSE;
Set to TRUE when the user taps “Request Code”. Hides the button and shows “Code requested — check your messages” on subsequent visits.
Summary
| Object | Type | Purpose | Required? |
|---|---|---|---|
users.roochip_balance | Column | Cached balance for fast UI display | Required |
roochip_events | Table | Webhook log and audit trail | Required |
users.roochip_code_requested | Column | Hide “Request Code” button after first request | Recommended |
Buy Flow — Future (Phase 2)
GET /v1/packages and displays package cards with prices.POST /v1/checkout → receives checkout_url.checkout_url in in-app WebView — user never leaves the app. iOS: StoreKit. Android: Play Billing.success_url. Show: “Payment received — adding credits…” Do NOT credit wallet here.credits.issued to Rooverse.roochip_balance cache and log event. UI refreshes with new balance.success_url redirect can be navigated to directly — it is not proof of payment. Credits must only be issued via the signed Roochip webhook.
Spend Credits Flow
users.roochip_balance < 10 in Rooverse DB → show “Not enough ROO” without an API call.402 even if cached balance looks sufficient (race condition protection).roochip_balance with new_balance from response. Execute the feature.Reward Flow
reference_id (your internal event ID) for traceability. Roochip issues credits unconditionally.new_balance. Animate a “+0.01 ROO” toast or notification.Error Codes
| HTTP | Meaning | Action |
|---|---|---|
200 | Success | — |
201 | Created | — |
400 | Bad request — missing or invalid fields | Fix the payload |
401 | Invalid or missing API key | Check X-API-Key header and env var |
402 | Insufficient credits | Show upsell UI, block the feature |
403 | Tenant inactive or code issued to different user | Contact Roochip support |
404 | Package, user, or code not found | Verify IDs are correct |
409 | Duplicate request or code already used | Do not retry — already processed |
503 coming_soon | Payment gateway not yet live | Show coming soon message, offer code request |
500 | Roochip server error | Retry with exponential backoff |
What Rooverse Does NOT Need to Build
- Stripe integration of any kind
- Apple IAP receipt verification
- Google IAP purchase token verification
- Credit wallet or ledger tables
- Double-payment prevention logic
- Credit package pricing or management
- Promo code generation or storage
- One-request-per-user enforcement for promo codes
- Webhook retry logic (Roochip retries automatically)
- Currency precision handling (Roochip stores as NUMERIC(12,2))