MVP sem integrações = produto pela metade.
Realidade: 80% dos MVPs precisam de pelo menos 3 integrações:
- Pagamento (Stripe, Mercado Pago)
- E-mail transacional (SendGrid, Resend)
- Storage de arquivos (Cloudinary, AWS S3)
Mais 40% precisam de: 4. Mapas/geolocalização (Google Maps) 5. SMS (Twilio) 6. Videoconferência (Daily.co, Zoom)
Este artigo mostra implementação completa das 3 integrações essenciais — do zero ao funcionando em menor que 8h cada, com código pronto e custos reais.
1. Pagamentos: Stripe (implementação completa)
Por que Stripe: Melhor API, aceita cartão internacional, compliance automático (PCI-DSS).
Alternativa BR: Mercado Pago (aceita apenas LATAM, API pior).
Setup inicial (15min)
# 1. Criar conta em stripe.com/br
# 2. Pegar chaves API (Dashboard → Developers → API keys)
# 3. Instalar SDK
npm install stripe @stripe/stripe-js
# 4. Configurar env
# .env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
Checkout básico (one-time payment)
Frontend (Next.js):
// app/checkout/page.tsx
'use client';
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export default function CheckoutPage() {
const handleCheckout = async () => {
const res = await fetch('/api/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: 19900, // R$ 199,00 em centavos
description: 'Plano Premium - Mensal',
}),
});
const { sessionId } = await res.json();
const stripe = await stripePromise;
await stripe?.redirectToCheckout({ sessionId });
};
return (
<button onClick={handleCheckout} className="btn-primary">
Assinar por R$ 199/mês
</button>
);
}
Backend (API Route):
// app/api/create-checkout-session/route.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-11-20.acacia',
});
export async function POST(req: Request) {
const { amount, description } = await req.json();
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'brl',
product_data: { name: description },
unit_amount: amount,
},
quantity: 1,
},
],
mode: 'payment',
success_url: `${process.env.NEXT_PUBLIC_URL}/success`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/cancel`,
});
return Response.json({ sessionId: session.id });
}
Cobrança recorrente (assinatura)
// Criar produto e preço (apenas 1x, via Dashboard ou API)
const price = await stripe.prices.create({
currency: 'brl',
unit_amount: 19900, // R$ 199
recurring: { interval: 'month' },
product_data: { name: 'Plano Premium' },
});
// Criar assinatura para cliente
const subscription = await stripe.subscriptions.create({
customer: customerId, // ID do cliente no Stripe
items: [{ price: price.id }],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent'],
});
return Response.json({
subscriptionId: subscription.id,
clientSecret: subscription.latest_invoice.payment_intent.client_secret,
});
Webhook (confirmar pagamento)
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get('stripe-signature')!;
let event;
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return Response.json({ error: 'Webhook signature verification failed' }, { status: 400 });
}
// Pagamento confirmado
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Ativar conta do usuário
await activateUserAccount(session.customer);
}
// Assinatura criada
if (event.type === 'customer.subscription.created') {
const subscription = event.data.object;
// Lógica de ativação
}
// Pagamento recorrente falhou
if (event.type === 'invoice.payment_failed') {
const invoice = event.data.object;
// Enviar e-mail de lembrete
await sendPaymentFailedEmail(invoice.customer_email);
}
return Response.json({ received: true });
}
Custo Stripe:
- Taxa: 3,4% + R$ 0,80 por transação
- Exemplo: R$ 199 → Taxa de R$ 7,57 (3,8%)
2. E-mail transacional: Resend (mais simples que SendGrid)
Por que Resend: API moderna, templates em React (TSX), deliverability excelente.
Setup (10min)
npm install resend react-email @react-email/components
# .env.local
RESEND_API_KEY=re_...
Template de e-mail em React
// emails/welcome.tsx
import {
Body,
Button,
Container,
Head,
Html,
Preview,
Section,
Text,
} from '@react-email/components';
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
}
export default function WelcomeEmail({ userName, loginUrl }: WelcomeEmailProps) {
return (
<Html>
<Head />
<Preview>Bem-vindo ao OrientMe, {userName}!</Preview>
<Body style={{ backgroundColor: '#f6f9fc', fontFamily: 'Arial' }}>
<Container style={{ margin: '40px auto', padding: '20px' }}>
<Text style={{ fontSize: '24px', fontWeight: 'bold' }}>
Olá {userName}! 👋
</Text>
<Text>
Sua conta foi criada com sucesso. Estamos animados para ter você conosco!
</Text>
<Section style={{ textAlign: 'center', margin: '32px 0' }}>
<Button
href={loginUrl}
style={{
backgroundColor: '#23a4ff',
color: '#fff',
padding: '12px 24px',
borderRadius: '6px',
textDecoration: 'none',
}}
>
Acessar minha conta
</Button>
</Section>
<Text style={{ fontSize: '14px', color: '#666' }}>
Dúvidas? Responda este e-mail ou acesse nossa central de ajuda.
</Text>
</Container>
</Body>
</Html>
);
}
Enviar e-mail
// lib/email.ts
import { Resend } from 'resend';
import WelcomeEmail from '@/emails/welcome';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendWelcomeEmail(to: string, userName: string) {
await resend.emails.send({
from: 'OrientMe <noreply@orientme.com.br>',
to,
subject: `Bem-vindo, ${userName}!`,
react: WelcomeEmail({ userName, loginUrl: 'https://app.orientme.com.br/login' }),
});
}
// Uso
await sendWelcomeEmail('joao@exemplo.com', 'João Silva');
Custo Resend:
- Grátis: 3.000 e-mails/mês
- R$ 100/mês: 50.000 e-mails
3. Storage de arquivos: Cloudinary
Por que Cloudinary: Upload direto do browser, transformações automáticas (resize, crop), CDN global.
Setup (15min)
npm install cloudinary next-cloudinary
# .env.local
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=...
CLOUDINARY_API_KEY=...
CLOUDINARY_API_SECRET=...
Upload de imagem (frontend)
// components/image-upload.tsx
'use client';
import { CldUploadWidget } from 'next-cloudinary';
import { useState } from 'react';
export default function ImageUpload() {
const [imageUrl, setImageUrl] = useState('');
return (
<div>
<CldUploadWidget
uploadPreset="ml_default" // Criar no Cloudinary Dashboard
onSuccess={(result) => {
setImageUrl(result.info.secure_url);
}}
>
{({ open }) => (
<button onClick={() => open()}>
Upload de foto
</button>
)}
</CldUploadWidget>
{imageUrl && (
<img src={imageUrl} alt="Upload" width={200} />
)}
</div>
);
}
Transformações automáticas
import { CldImage } from 'next-cloudinary';
// Imagem otimizada (webp, lazy load, responsive)
<CldImage
src={publicId} // ID da imagem no Cloudinary
width={400}
height={300}
crop="fill"
gravity="face" // Foca no rosto se for foto de pessoa
alt="Foto de perfil"
/>
// Thumbnail circular (avatar)
<CldImage
src={publicId}
width={80}
height={80}
crop="thumb"
gravity="face"
radius="max" // Torna circular
alt="Avatar"
/>
Custo Cloudinary:
- Grátis: 25GB storage + 25GB bandwidth/mês
- R$ 400/mês: 100GB storage + 100GB bandwidth
4. Mapas e geolocalização: Google Maps API
Setup (20min):
npm install @googlemaps/js-api-loader
# 1. Criar projeto em console.cloud.google.com
# 2. Ativar APIs: Maps JavaScript API, Geocoding API, Places API
# 3. Criar chave API
# .env.local
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=AIza...
Autocomplete de endereço
'use client';
import { useLoadScript, StandaloneSearchBox } from '@react-google-maps/api';
import { useRef, useState } from 'react';
const libraries: ('places')[] = ['places'];
export default function AddressAutocomplete() {
const { isLoaded } = useLoadScript({
googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
libraries,
});
const searchBoxRef = useRef<google.maps.places.SearchBox | null>(null);
const [address, setAddress] = useState('');
const handlePlacesChanged = () => {
const places = searchBoxRef.current?.getPlaces();
if (places && places.length > 0) {
const place = places[0];
setAddress(place.formatted_address || '');
// Latitude e longitude
const lat = place.geometry?.location?.lat();
const lng = place.geometry?.location?.lng();
console.log({ lat, lng, address: place.formatted_address });
}
};
if (!isLoaded) return <div>Carregando...</div>;
return (
<StandaloneSearchBox
onLoad={(ref) => (searchBoxRef.current = ref)}
onPlacesChanged={handlePlacesChanged}
>
<input
type="text"
placeholder="Digite seu endereço"
className="w-full p-2 border rounded"
/>
</StandaloneSearchBox>
);
}
Calcular distância entre 2 pontos
// lib/maps.ts
export async function calculateDistance(
origin: { lat: number; lng: number },
destination: { lat: number; lng: number }
) {
const response = await fetch(
`https://maps.googleapis.com/maps/api/distancematrix/json?origins=${origin.lat},${origin.lng}&destinations=${destination.lat},${destination.lng}&key=${process.env.GOOGLE_MAPS_API_KEY}`
);
const data = await response.json();
return {
distance: data.rows[0].elements[0].distance.value, // metros
duration: data.rows[0].elements[0].duration.value, // segundos
};
}
// Uso
const { distance, duration } = await calculateDistance(
{ lat: -23.550520, lng: -46.633308 }, // São Paulo
{ lat: -22.906847, lng: -43.172896 } // Rio de Janeiro
);
console.log(`Distância: ${distance / 1000}km`); // 429km
console.log(`Duração: ${duration / 60}min`); // 382min
Custo Google Maps:
- R$ 750 de crédito grátis/mês
- Autocomplete: $2,83 por 1.000 requests
- Geocoding: $5,00 por 1.000 requests
- Realidade: R$ 0 até ~5K usuários/mês
5. Bônus: SMS (Twilio)
Setup rápido:
npm install twilio
# .env.local
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=...
TWILIO_PHONE_NUMBER=+55...
Enviar SMS:
import twilio from 'twilio';
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
export async function sendSMS(to: string, message: string) {
await client.messages.create({
body: message,
from: process.env.TWILIO_PHONE_NUMBER,
to,
});
}
// Uso
await sendSMS(
'+5521987654321',
'Seu código de confirmação é: 739281'
);
Custo Twilio:
- R$ 0,30 por SMS enviado no Brasil
Resumo de custos (mensal)
| Integração | 0-1K usuários | 1K-10K | 10K-100K |
|---|---|---|---|
| Stripe | R$ 0 (apenas taxa 3,4%) | R$ 0 | R$ 0 |
| Resend | R$ 0 (3K e-mails) | R$ 100 | R$ 300 |
| Cloudinary | R$ 0 (25GB) | R$ 80 | R$ 400 |
| Google Maps | R$ 0 (crédito) | R$ 0 | R$ 200 |
| Twilio | R$ 0-150 | R$ 300-1K | R$ 2K-5K |
| TOTAL | R$ 0-150 | R$ 480-1.180 | R$ 2.900-5.900 |
Conclusão: Integrações custam quase R$ 0 até validar produto com 1K usuários. Depois escalam gradualmente.
Não desenvolva sistema de pagamento, e-mail ou storage próprio. Use APIs prontas e foque no core do seu produto.