Skip to content

Commit 1d236ce

Browse files
committed
feat: stripe skill - atualiza URL do webhook e adiciona configs de planos
1 parent 1ed55ae commit 1d236ce

File tree

5 files changed

+583
-172
lines changed

5 files changed

+583
-172
lines changed

.agent/skills/riligar-infra-stripe/SKILL.md

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ Instrua o usuário:
151151
>
152152
> 1. Acesse https://dashboard.stripe.com/webhooks
153153
> 2. Clique em "Add endpoint"
154-
> 3. URL: `https://seu-dominio.com/webhook/stripe`
154+
> 3. URL: `https://seu-dominio.com/api/webhook`
155155
> 4. Selecione os eventos:
156156
> - `checkout.session.completed`
157157
> - `customer.subscription.updated`
@@ -171,14 +171,15 @@ Gere todos os arquivos necessários usando os templates de [assets/](assets/):
171171
| `routes/billing.js` | stripe-server.js (seção 2) |
172172
| `routes/webhook.js` | stripe-server.js (seção 3) |
173173
| `services/billing.js` | stripe-server.js (seção 4) |
174-
| `config/stripe-prices.js` | Price IDs coletados |
174+
| `config/stripe-prices.js` | Price IDs coletados (Step 9) |
175+
| `config/plans.js` | PLAN_MAP + PLAN_LIMITS (Step 9) |
175176
| `pages/Pricing.jsx` | stripe-client.js (seção 3) |
176177
| `components/BillingSettings.jsx` | stripe-client.js (seção 4) |
177178
| `hooks/useSubscription.js` | stripe-client.js (seção 2) |
178179

179-
### Step 9: Criar Config de Preços
180+
### Step 9: Criar Configs de Planos e Preços
180181

181-
Gere o arquivo de configuração com os Price IDs:
182+
**A) Arquivo de preços (config/stripe-prices.js):**
182183

183184
```javascript
184185
// config/stripe-prices.js
@@ -194,13 +195,53 @@ export const STRIPE_PRICES = {
194195
name: 'Pro',
195196
price: 99,
196197
features: ['Projetos ilimitados', 'Suporte prioritário', '10GB storage']
198+
},
199+
enterprise: {
200+
priceId: 'price_COLETADO_ENTERPRISE',
201+
name: 'Enterprise',
202+
price: 299,
203+
features: ['Tudo do Pro', 'Storage ilimitado', 'SLA garantido']
197204
}
198205
}
199206

200207
export const getPrice = (plan) => STRIPE_PRICES[plan]
201208
export const getPriceId = (plan) => STRIPE_PRICES[plan]?.priceId
202209
```
203210
211+
**B) Arquivo de mapeamento e limites (config/plans.js):**
212+
213+
```javascript
214+
// config/plans.js
215+
216+
// Mapeia Price IDs do Stripe para nomes de planos internos
217+
export const PLAN_MAP = {
218+
'price_COLETADO_STARTER': 'starter',
219+
'price_COLETADO_PRO': 'pro',
220+
'price_COLETADO_ENTERPRISE': 'enterprise',
221+
}
222+
223+
// Define limites de features por plano
224+
export const PLAN_LIMITS = {
225+
free: {
226+
maxFlows: 1,
227+
maxContacts: 100,
228+
// Adicione outros limites conforme necessário
229+
},
230+
starter: {
231+
maxFlows: 3,
232+
maxContacts: 500,
233+
},
234+
pro: {
235+
maxFlows: 15,
236+
maxContacts: 5000,
237+
},
238+
enterprise: {
239+
maxFlows: 55,
240+
maxContacts: Infinity,
241+
},
242+
}
243+
```
244+
204245
---
205246
206247
## Checklist Final
@@ -211,11 +252,13 @@ Ao completar o setup, confirme:
211252
- [ ] Chaves no `.env.development` e `.env.production` (backend e frontend)
212253
- [ ] Produtos criados no Stripe
213254
- [ ] Price IDs configurados em `config/stripe-prices.js`
214-
- [ ] Schema do database atualizado
215-
- [ ] Webhook endpoint configurado no Stripe Dashboard
255+
- [ ] `PLAN_MAP` e `PLAN_LIMITS` configurados em `config/plans.js`
256+
- [ ] Schema do database atualizado (campos Stripe na tabela users)
257+
- [ ] Webhook endpoint configurado no Stripe Dashboard (`/api/webhook`)
216258
- [ ] Webhook secret nos arquivos de ambiente
217-
- [ ] Rotas de billing funcionando
218-
- [ ] Página de pricing criada
259+
- [ ] Rotas de billing funcionando (`/api/billing/*`)
260+
- [ ] Página de pricing criada (com token de auth no ky)
261+
- [ ] `useSubscription` hook com header Authorization
219262
- [ ] Testado com cartão 4242 4242 4242 4242
220263
221264
---
@@ -229,8 +272,8 @@ brew install stripe/stripe-cli/stripe
229272
# Login
230273
stripe login
231274
232-
# Forward webhooks
233-
stripe listen --forward-to localhost:3000/webhook/stripe
275+
# Forward webhooks (ajuste a porta conforme seu backend)
276+
stripe listen --forward-to localhost:3333/api/webhook
234277
235278
# Testar checkout
236279
stripe trigger checkout.session.completed

.agent/skills/riligar-infra-stripe/assets/stripe-client.js

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ export const elementsAppearance = {
3232
import { useState, useEffect } from 'react'
3333
import ky from 'ky'
3434

35+
const api = ky.create({
36+
prefixUrl: import.meta.env.VITE_API_URL || 'http://localhost:3333',
37+
hooks: {
38+
beforeRequest: [
39+
request => {
40+
const token = localStorage.getItem('auth:token')
41+
if (token) {
42+
request.headers.set('Authorization', `Bearer ${token}`)
43+
}
44+
},
45+
],
46+
},
47+
})
48+
3549
export function useSubscription() {
3650
const [subscription, setSubscription] = useState(null)
3751
const [loading, setLoading] = useState(true)
@@ -40,7 +54,7 @@ export function useSubscription() {
4054
const fetchSubscription = async () => {
4155
setLoading(true)
4256
try {
43-
const data = await ky.get('/api/billing/subscription').json()
57+
const data = await api.get('api/billing/subscription').json()
4458
setSubscription(data)
4559
setError(null)
4660
} catch (err) {
@@ -77,35 +91,52 @@ import { IconCheck } from '@tabler/icons-react'
7791
import { useState } from 'react'
7892
import ky from 'ky'
7993

94+
// Importar do arquivo de configuração centralizado
95+
// import { STRIPE_PRICES } from '../config/stripe-prices'
96+
8097
const plans = [
8198
{
8299
name: 'Starter',
83-
priceId: 'price_starter_monthly',
100+
priceId: 'price_starter_monthly', // Substituir pelo valor de STRIPE_PRICES.starter.priceId
84101
price: 29,
85102
features: ['5 projetos', 'Suporte por email', '1GB de storage']
86103
},
87104
{
88105
name: 'Pro',
89-
priceId: 'price_pro_monthly',
106+
priceId: 'price_pro_monthly', // Substituir pelo valor de STRIPE_PRICES.pro.priceId
90107
price: 99,
91108
popular: true,
92109
features: ['Projetos ilimitados', 'Suporte prioritário', '10GB de storage', 'API access']
93110
},
94111
{
95112
name: 'Enterprise',
96-
priceId: 'price_enterprise_monthly',
113+
priceId: 'price_enterprise_monthly', // Substituir pelo valor de STRIPE_PRICES.enterprise.priceId
97114
price: 299,
98115
features: ['Tudo do Pro', 'Suporte dedicado', 'Storage ilimitado', 'SLA garantido']
99116
}
100117
]
101118

119+
const api = ky.create({
120+
prefixUrl: import.meta.env.VITE_API_URL || 'http://localhost:3333',
121+
hooks: {
122+
beforeRequest: [
123+
request => {
124+
const token = localStorage.getItem('auth:token')
125+
if (token) {
126+
request.headers.set('Authorization', `Bearer ${token}`)
127+
}
128+
},
129+
],
130+
},
131+
})
132+
102133
export default function Pricing() {
103134
const [loading, setLoading] = useState(null)
104135

105136
const handleCheckout = async (priceId) => {
106137
setLoading(priceId)
107138
try {
108-
const { url } = await ky.post('/api/billing/checkout', {
139+
const { url } = await api.post('api/billing/checkout', {
109140
json: { priceId, mode: 'subscription' }
110141
}).json()
111142
window.location.href = url
@@ -178,20 +209,34 @@ import { Button, Card, Group, Stack, Text, Title, Badge, Divider } from '@mantin
178209
import { useState, useEffect } from 'react'
179210
import ky from 'ky'
180211

212+
const api = ky.create({
213+
prefixUrl: import.meta.env.VITE_API_URL || 'http://localhost:3333',
214+
hooks: {
215+
beforeRequest: [
216+
request => {
217+
const token = localStorage.getItem('auth:token')
218+
if (token) {
219+
request.headers.set('Authorization', `Bearer ${token}`)
220+
}
221+
},
222+
],
223+
},
224+
})
225+
181226
export default function BillingSettings() {
182227
const [subscription, setSubscription] = useState(null)
183228
const [loading, setLoading] = useState(false)
184229

185230
useEffect(() => {
186-
ky.get('/api/billing/subscription').json()
231+
api.get('api/billing/subscription').json()
187232
.then(setSubscription)
188233
.catch(console.error)
189234
}, [])
190235

191236
const openPortal = async () => {
192237
setLoading(true)
193238
try {
194-
const { url } = await ky.post('/api/billing/portal').json()
239+
const { url } = await api.post('api/billing/portal').json()
195240
window.location.href = url
196241
} finally {
197242
setLoading(false)
@@ -201,9 +246,9 @@ export default function BillingSettings() {
201246
const cancelSubscription = async () => {
202247
setLoading(true)
203248
try {
204-
await ky.post('/api/billing/subscription/cancel').json()
249+
await api.post('api/billing/subscription/cancel').json()
205250
// Refresh subscription data
206-
const data = await ky.get('/api/billing/subscription').json()
251+
const data = await api.get('api/billing/subscription').json()
207252
setSubscription(data)
208253
} finally {
209254
setLoading(false)
@@ -356,12 +401,26 @@ import { IconDownload } from '@tabler/icons-react'
356401
import { useState, useEffect } from 'react'
357402
import ky from 'ky'
358403

404+
const api = ky.create({
405+
prefixUrl: import.meta.env.VITE_API_URL || 'http://localhost:3333',
406+
hooks: {
407+
beforeRequest: [
408+
request => {
409+
const token = localStorage.getItem('auth:token')
410+
if (token) {
411+
request.headers.set('Authorization', `Bearer ${token}`)
412+
}
413+
},
414+
],
415+
},
416+
})
417+
359418
export default function InvoiceList() {
360419
const [invoices, setInvoices] = useState([])
361420
const [loading, setLoading] = useState(true)
362421

363422
useEffect(() => {
364-
ky.get('/api/billing/invoices').json()
423+
api.get('api/billing/invoices').json()
365424
.then(({ data }) => setInvoices(data))
366425
.catch(console.error)
367426
.finally(() => setLoading(false))

0 commit comments

Comments
 (0)