Pular para conteudo
Integracao com IA

Integre seu SaaS em minutos

Copie o prompt abaixo, cole na sua IA favorita (Claude, Cursor, Lovable) junto com o prompt do seu projeto, e seu app nasce integrado.

1

Copie o prompt

Clique no botao abaixo pra copiar o prompt completo de integracao

2

Cole na sua IA

Junto com o prompt do seu projeto. A IA gera o app ja integrado.

3

Configure as envs

Copie sua API Key e Webhook Secret do painel e coloque no .env

Prompt de integracao

Este prompt contem tudo que a IA precisa pra implementar: SSO, webhooks, controle de acesso, tracking, shortcuts e mais.

Funciona com Claude Code, Cursor, Lovable, v0, Bolt e qualquer IA que aceita prompts longos

O que o prompt inclui

Variaveis de ambiente com validacao
Migration pro banco de dados
Modulo API client (lib/afinny.ts)
Endpoint SSO (/afinny/auth)
Webhook receiver com HMAC
Middleware de controle de acesso
Tracking de uso (analytics)
Script de home screen shortcut
Feature gating por plano
Health check endpoint
Checklist final de verificacao

Suas credenciais

Acesse Configuracoes ou a pagina de settings do seu produto pra copiar sua API Key e Webhook Secret. Cole no .env do seu projeto:

AFINNY_API_KEY=sua_api_key_aqui
AFINNY_WEBHOOK_SECRET=seu_webhook_secret_aqui
AFINNY_APP_ID=slug-do-seu-produto
AFINNY_API_URL=https://api.afinny.com/v1

Ou copie por partes

Se preferir, copie cada secao separadamente:

1. Variaveis de ambiente
# Adicione ao .env do seu projeto
AFINNY_API_KEY=           # API key do painel Afinny → Settings → Integracao
AFINNY_WEBHOOK_SECRET=    # Webhook secret do painel Afinny → Settings → Integracao
AFINNY_APP_ID=            # Slug do seu produto cadastrado na Afinny
AFINNY_API_URL=https://api.afinny.com/v1

# Validacao no startup do app:
const REQUIRED = ['AFINNY_API_KEY', 'AFINNY_WEBHOOK_SECRET', 'AFINNY_APP_ID']
REQUIRED.forEach(k => { if (!process.env[k]) throw new Error(`[Afinny] Missing: ${k}`) })
2. Database migration
ALTER TABLE users
  ADD COLUMN IF NOT EXISTS afinny_id           VARCHAR(255) UNIQUE,
  ADD COLUMN IF NOT EXISTS afinny_plan         VARCHAR(100),
  ADD COLUMN IF NOT EXISTS afinny_status       VARCHAR(50)  DEFAULT 'inactive',
  ADD COLUMN IF NOT EXISTS afinny_free         BOOLEAN      DEFAULT FALSE,
  ADD COLUMN IF NOT EXISTS afinny_sub_id       VARCHAR(255),
  ADD COLUMN IF NOT EXISTS afinny_period_end   TIMESTAMP,
  ADD COLUMN IF NOT EXISTS afinny_trial_end    TIMESTAMP,
  ADD COLUMN IF NOT EXISTS afinny_inf_id       VARCHAR(255),
  ADD COLUMN IF NOT EXISTS afinny_inf_username VARCHAR(255),
  ADD COLUMN IF NOT EXISTS afinny_synced_at    TIMESTAMP;

CREATE INDEX IF NOT EXISTS idx_users_afinny_id ON users(afinny_id);
CREATE INDEX IF NOT EXISTS idx_users_afinny_status ON users(afinny_status);
3. API Client (lib/afinny.ts)
const BASE = process.env.AFINNY_API_URL!
const API_KEY = process.env.AFINNY_API_KEY!

export async function validateSSOToken(token: string) {
  const res = await fetch(`${BASE}/sso/validate`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
    body: JSON.stringify({ token }),
  })
  if (!res.ok) return null
  const data = await res.json()
  return data.valid ? data : null
}

export function verifyWebhookSignature(payload: string, signature: string | null): boolean {
  if (!signature) return false
  const crypto = require('crypto')
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.AFINNY_WEBHOOK_SECRET!)
    .update(payload).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
}

export function hasActiveAccess(status: string | null): boolean {
  return ['active', 'trialing', 'influencer_free'].includes(status ?? '')
}

export async function trackEvent(userId: string, event: string, metadata?: Record<string, unknown>) {
  fetch(`${BASE}/track`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
    body: JSON.stringify({ user_id: userId, app_id: process.env.AFINNY_APP_ID, event, metadata }),
  }).catch(() => {})
}
4. SSO Endpoint (/afinny/auth)
// app/afinny/auth/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server'
import { validateSSOToken, trackEvent } from '@/lib/afinny'

export async function GET(req: NextRequest) {
  const token = req.nextUrl.searchParams.get('token')
  if (!token) return NextResponse.redirect(new URL('/login?error=missing_token', req.url))

  const payload = await validateSSOToken(token)
  if (!payload) return NextResponse.redirect(new URL('/login?error=invalid_token', req.url))

  const { user, subscription } = payload

  // Upsert user no seu banco
  const localUser = await db.user.upsert({
    where: { afinny_id: user.id },
    update: {
      email: user.email, name: user.name,
      afinny_plan: subscription.plan,
      afinny_status: subscription.status,
      afinny_free: subscription.influencer_free,
      afinny_sub_id: subscription.id,
      afinny_synced_at: new Date(),
    },
    create: {
      afinny_id: user.id, email: user.email, name: user.name,
      afinny_plan: subscription.plan,
      afinny_status: subscription.status,
      afinny_free: subscription.influencer_free,
      afinny_sub_id: subscription.id,
      afinny_synced_at: new Date(),
    },
  })

  await trackEvent(user.id, 'session_started')

  // Crie sessao local com seu sistema de auth
  const response = NextResponse.redirect(new URL('/dashboard', req.url))
  // response.cookies.set('session', ...) — adapte ao seu auth
  return response
}
5. Webhook Receiver
// app/api/webhooks/afinny/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { verifyWebhookSignature } from '@/lib/afinny'

export async function POST(req: NextRequest) {
  const body = await req.text()
  const signature = req.headers.get('x-afinny-signature')

  if (!verifyWebhookSignature(body, signature)) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
  }

  const { event, data } = JSON.parse(body)

  switch (event) {
    case 'subscription.created':
    case 'trial.started':
      await db.user.upsert({
        where: { afinny_id: data.subscription.user_id },
        update: { afinny_status: 'active', afinny_plan: data.subscription.plan },
        create: { afinny_id: data.subscription.user_id, email: data.subscription.user_email,
                  afinny_status: 'active', afinny_plan: data.subscription.plan },
      })
      break
    case 'subscription.canceled':
      await db.user.update({
        where: { afinny_id: data.subscription.user_id },
        data: { afinny_status: 'canceled' },
      })
      break
    case 'affiliate.approved':
      await db.user.upsert({
        where: { afinny_id: data.affiliate.influencer_id },
        update: { afinny_status: 'active', afinny_free: true },
        create: { afinny_id: data.affiliate.influencer_id,
                  email: data.affiliate.influencer_email,
                  afinny_status: 'active', afinny_free: true },
      })
      break
    case 'affiliate.revoked':
      await db.user.update({
        where: { afinny_id: data.affiliate.influencer_id },
        data: { afinny_status: 'canceled', afinny_free: false },
      })
      break
  }

  return NextResponse.json({ received: true })
}
6. Access Control Middleware
// middleware.ts
import { hasActiveAccess } from '@/lib/afinny'

const PUBLIC = ['/', '/login', '/afinny/auth', '/api/webhooks/afinny']

export async function middleware(req) {
  const path = req.nextUrl.pathname
  if (PUBLIC.some(p => path === p || path.startsWith(p))) return NextResponse.next()

  const session = await getSession(req) // seu sistema de auth
  if (!session) return NextResponse.redirect(new URL('/login', req.url))

  if (path.startsWith('/dashboard') && !hasActiveAccess(session.afinny_status)) {
    return NextResponse.redirect(new URL('/subscription-required', req.url))
  }
  return NextResponse.next()
}
7. Tracking de uso
// No layout do dashboard, fire-and-forget:
import { trackEvent } from '@/lib/afinny'

// Quando usuario abre o app
trackEvent(user.afinny_id, 'app_opened', { plan: user.afinny_plan })

// Quando usa feature importante
trackEvent(user.afinny_id, 'feature_used', { feature: 'create_project' })
8. Home Screen Shortcut
// components/AfinnyShortcutBanner.tsx — adicione no layout root
// Detecta quando esta dentro do Afinny WebView e oferece atalho na home

'use client'
import { useEffect } from 'react'

export function AfinnyShortcutBanner() {
  useEffect(() => {
    const isAfinny = navigator.userAgent.includes('AfinnyApp')
    if (!isAfinny) return
    if (localStorage.getItem('afinny_shortcut_shown')) return

    // Mostra banner oferecendo adicionar atalho
    // Ao clicar: window.ReactNativeWebView.postMessage(JSON.stringify({
    //   type: 'AFINNY_ADD_SHORTCUT',
    //   payload: { appName: document.title, iconUrl: '/icon.png' }
    // }))
  }, [])
  return null
}
9. Feature Gating por plano
// lib/features.ts
const HIERARCHY = { basic: 1, pro: 2, enterprise: 3, influencer_free: 2 }

export function planIncludes(userPlan: string, required: string): boolean {
  return (HIERARCHY[userPlan] ?? 0) >= (HIERARCHY[required] ?? 0)
}

// Uso:
if (!planIncludes(user.afinny_plan, 'pro')) {
  return NextResponse.json({ error: 'Upgrade necessario' }, { status: 403 })
}
10. Health Check
// app/api/afinny/health/route.ts
export async function GET() {
  return NextResponse.json({
    status: 'ok',
    app_id: process.env.AFINNY_APP_ID,
    integration: { sso: true, webhook: true, version: '1.0' },
    timestamp: new Date().toISOString(),
  })
}
11. Checklist final
[ ] AFINNY_API_KEY configurada no .env
[ ] AFINNY_WEBHOOK_SECRET configurada no .env
[ ] Migration rodada (campos afinny_* na tabela users)
[ ] GET /afinny/auth funciona (SSO endpoint)
[ ] POST /api/webhooks/afinny funciona (webhook receiver)
[ ] HMAC valida assinatura corretamente
[ ] Middleware bloqueia acesso sem subscription ativa
[ ] GET /api/afinny/health retorna status: ok
[ ] Tracking reporta app_opened
[ ] URLs registradas no painel Afinny (SSO URL + Webhook URL)

Exemplo de prompt pro dev

"Crie um app web de [seu nicho] chamado [NomeDoApp] com:
- [feature 1]
- [feature 2]
- [feature 3]

IMPORTANTE: Este app deve ser integrado com a Afinny.
Cole aqui o prompt de integracao copiado acima."

A IA le o prompt de integracao e implementa SSO, webhooks e controle de acesso automaticamente.

Duvidas? Fale com a gente em contato@afinny.com