@nextsparkjs/core 0.1.0-beta.107 → 0.1.0-beta.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +45 -39
- package/dist/components/ui/password-input.d.ts +7 -0
- package/dist/components/ui/password-input.d.ts.map +1 -1
- package/dist/components/ui/password-input.js +12 -5
- package/dist/messages/de/auth.json +50 -3
- package/dist/messages/de/index.d.ts +47 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/en/auth.json +101 -9
- package/dist/messages/en/index.d.ts +92 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/es/auth.json +106 -14
- package/dist/messages/es/index.d.ts +92 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/fr/auth.json +50 -3
- package/dist/messages/fr/index.d.ts +47 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/it/auth.json +50 -3
- package/dist/messages/it/index.d.ts +47 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/pt/auth.json +50 -3
- package/dist/messages/pt/index.d.ts +47 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/styles/classes.json +1 -1
- package/package.json +2 -2
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +0 -410
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +0 -410
|
@@ -280,18 +280,65 @@ declare const _default: {
|
|
|
280
280
|
title: string;
|
|
281
281
|
description: string;
|
|
282
282
|
inviteBanner: string;
|
|
283
|
+
form: {
|
|
284
|
+
firstName: string;
|
|
285
|
+
firstNamePlaceholder: string;
|
|
286
|
+
lastName: string;
|
|
287
|
+
lastNamePlaceholder: string;
|
|
288
|
+
email: string;
|
|
289
|
+
emailPlaceholder: string;
|
|
290
|
+
password: string;
|
|
291
|
+
confirmPassword: string;
|
|
292
|
+
agreeToThe: string;
|
|
293
|
+
termsAndConditions: string;
|
|
294
|
+
creatingAccount: string;
|
|
295
|
+
createAccount: string;
|
|
296
|
+
orContinueWith: string;
|
|
297
|
+
continueWithGoogle: string;
|
|
298
|
+
passwordRequirements: {
|
|
299
|
+
minChars: string;
|
|
300
|
+
uppercase: string;
|
|
301
|
+
lowercase: string;
|
|
302
|
+
number: string;
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
footer: {
|
|
306
|
+
alreadyHaveAccount: string;
|
|
307
|
+
signIn: string;
|
|
308
|
+
signInAria: string;
|
|
309
|
+
};
|
|
283
310
|
emailVerification: {
|
|
284
311
|
title: string;
|
|
285
312
|
description: string;
|
|
313
|
+
checkInbox: string;
|
|
314
|
+
emailSentAria: string;
|
|
315
|
+
step1: string;
|
|
316
|
+
step2: string;
|
|
317
|
+
step3: string;
|
|
318
|
+
didntReceive: string;
|
|
319
|
+
back: string;
|
|
320
|
+
sending: string;
|
|
321
|
+
resendEmail: string;
|
|
322
|
+
alreadyVerified: string;
|
|
323
|
+
signIn: string;
|
|
286
324
|
};
|
|
287
325
|
messages: {
|
|
288
326
|
termsError: string;
|
|
289
327
|
creatingAccount: string;
|
|
290
328
|
accountCreated: string;
|
|
291
329
|
createError: string;
|
|
330
|
+
inviteSuccess: string;
|
|
331
|
+
inviteJoinedTeam: string;
|
|
292
332
|
};
|
|
293
333
|
errors: {
|
|
294
334
|
mustAgreeToTerms: string;
|
|
335
|
+
userAlreadyExists: string;
|
|
336
|
+
emailMismatch: string;
|
|
337
|
+
invitationExpired: string;
|
|
338
|
+
invitationNotFound: string;
|
|
339
|
+
failedToCreate: string;
|
|
340
|
+
googleFailed: string;
|
|
341
|
+
failedToResend: string;
|
|
295
342
|
};
|
|
296
343
|
};
|
|
297
344
|
forgotPassword: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/it/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/it/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,wBAoBU"}
|
|
@@ -60,18 +60,65 @@
|
|
|
60
60
|
"title": "Criar Conta",
|
|
61
61
|
"description": "Insira suas informacoes para comecar",
|
|
62
62
|
"inviteBanner": "Crie uma conta para aceitar seu convite para a equipe",
|
|
63
|
+
"form": {
|
|
64
|
+
"firstName": "Nome",
|
|
65
|
+
"firstNamePlaceholder": "Joao",
|
|
66
|
+
"lastName": "Sobrenome",
|
|
67
|
+
"lastNamePlaceholder": "Silva",
|
|
68
|
+
"email": "Email",
|
|
69
|
+
"emailPlaceholder": "email@exemplo.com",
|
|
70
|
+
"password": "Senha",
|
|
71
|
+
"confirmPassword": "Confirmar senha",
|
|
72
|
+
"agreeToThe": "Concordo com os",
|
|
73
|
+
"termsAndConditions": "termos e condicoes",
|
|
74
|
+
"creatingAccount": "Criando conta...",
|
|
75
|
+
"createAccount": "Criar conta",
|
|
76
|
+
"orContinueWith": "Ou continue com",
|
|
77
|
+
"continueWithGoogle": "Continuar com Google",
|
|
78
|
+
"passwordRequirements": {
|
|
79
|
+
"minChars": "8+ caracteres",
|
|
80
|
+
"uppercase": "Uma maiuscula",
|
|
81
|
+
"lowercase": "Uma minuscula",
|
|
82
|
+
"number": "Um numero"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"footer": {
|
|
86
|
+
"alreadyHaveAccount": "Ja tem uma conta?",
|
|
87
|
+
"signIn": "Entrar",
|
|
88
|
+
"signInAria": "Ir para a pagina de login"
|
|
89
|
+
},
|
|
63
90
|
"emailVerification": {
|
|
64
91
|
"title": "Verifique seu email!",
|
|
65
|
-
"description": "Enviamos um link de verificacao para"
|
|
92
|
+
"description": "Enviamos um link de verificacao para",
|
|
93
|
+
"checkInbox": "Verifique sua caixa de entrada e clique no link de verificacao para ativar sua conta. O link expira em 24 horas.",
|
|
94
|
+
"emailSentAria": "Email enviado com sucesso",
|
|
95
|
+
"step1": "Abra sua caixa de entrada",
|
|
96
|
+
"step2": "Encontre o email que enviamos (verifique o spam se necessario)",
|
|
97
|
+
"step3": "Clique no botao de verificacao",
|
|
98
|
+
"didntReceive": "Nao recebeu o email?",
|
|
99
|
+
"back": "Voltar",
|
|
100
|
+
"sending": "Enviando...",
|
|
101
|
+
"resendEmail": "Reenviar Email",
|
|
102
|
+
"alreadyVerified": "Ja verificou?",
|
|
103
|
+
"signIn": "Entrar"
|
|
66
104
|
},
|
|
67
105
|
"messages": {
|
|
68
106
|
"termsError": "Erro: Voce deve aceitar os termos e condicoes",
|
|
69
107
|
"creatingAccount": "Criando conta...",
|
|
70
108
|
"accountCreated": "Conta criada com sucesso. Verifique seu email.",
|
|
71
|
-
"createError": "Erro ao criar conta: {error}"
|
|
109
|
+
"createError": "Erro ao criar conta: {error}",
|
|
110
|
+
"inviteSuccess": "Conta criada com sucesso!",
|
|
111
|
+
"inviteJoinedTeam": "Voce entrou para a equipe."
|
|
72
112
|
},
|
|
73
113
|
"errors": {
|
|
74
|
-
"mustAgreeToTerms": "Voce deve aceitar os termos e condicoes"
|
|
114
|
+
"mustAgreeToTerms": "Voce deve aceitar os termos e condicoes",
|
|
115
|
+
"userAlreadyExists": "Ja existe uma conta com este email. Por favor faca login.",
|
|
116
|
+
"emailMismatch": "Este convite foi enviado para outro endereco de email.",
|
|
117
|
+
"invitationExpired": "Este convite expirou. Solicite um novo.",
|
|
118
|
+
"invitationNotFound": "Convite invalido. Solicite um novo.",
|
|
119
|
+
"failedToCreate": "Erro ao criar conta",
|
|
120
|
+
"googleFailed": "Erro ao cadastrar com Google",
|
|
121
|
+
"failedToResend": "Erro ao reenviar o email de verificacao"
|
|
75
122
|
}
|
|
76
123
|
},
|
|
77
124
|
"forgotPassword": {
|
|
@@ -280,18 +280,65 @@ declare const _default: {
|
|
|
280
280
|
title: string;
|
|
281
281
|
description: string;
|
|
282
282
|
inviteBanner: string;
|
|
283
|
+
form: {
|
|
284
|
+
firstName: string;
|
|
285
|
+
firstNamePlaceholder: string;
|
|
286
|
+
lastName: string;
|
|
287
|
+
lastNamePlaceholder: string;
|
|
288
|
+
email: string;
|
|
289
|
+
emailPlaceholder: string;
|
|
290
|
+
password: string;
|
|
291
|
+
confirmPassword: string;
|
|
292
|
+
agreeToThe: string;
|
|
293
|
+
termsAndConditions: string;
|
|
294
|
+
creatingAccount: string;
|
|
295
|
+
createAccount: string;
|
|
296
|
+
orContinueWith: string;
|
|
297
|
+
continueWithGoogle: string;
|
|
298
|
+
passwordRequirements: {
|
|
299
|
+
minChars: string;
|
|
300
|
+
uppercase: string;
|
|
301
|
+
lowercase: string;
|
|
302
|
+
number: string;
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
footer: {
|
|
306
|
+
alreadyHaveAccount: string;
|
|
307
|
+
signIn: string;
|
|
308
|
+
signInAria: string;
|
|
309
|
+
};
|
|
283
310
|
emailVerification: {
|
|
284
311
|
title: string;
|
|
285
312
|
description: string;
|
|
313
|
+
checkInbox: string;
|
|
314
|
+
emailSentAria: string;
|
|
315
|
+
step1: string;
|
|
316
|
+
step2: string;
|
|
317
|
+
step3: string;
|
|
318
|
+
didntReceive: string;
|
|
319
|
+
back: string;
|
|
320
|
+
sending: string;
|
|
321
|
+
resendEmail: string;
|
|
322
|
+
alreadyVerified: string;
|
|
323
|
+
signIn: string;
|
|
286
324
|
};
|
|
287
325
|
messages: {
|
|
288
326
|
termsError: string;
|
|
289
327
|
creatingAccount: string;
|
|
290
328
|
accountCreated: string;
|
|
291
329
|
createError: string;
|
|
330
|
+
inviteSuccess: string;
|
|
331
|
+
inviteJoinedTeam: string;
|
|
292
332
|
};
|
|
293
333
|
errors: {
|
|
294
334
|
mustAgreeToTerms: string;
|
|
335
|
+
userAlreadyExists: string;
|
|
336
|
+
emailMismatch: string;
|
|
337
|
+
invitationExpired: string;
|
|
338
|
+
invitationNotFound: string;
|
|
339
|
+
failedToCreate: string;
|
|
340
|
+
googleFailed: string;
|
|
341
|
+
failedToResend: string;
|
|
295
342
|
};
|
|
296
343
|
};
|
|
297
344
|
forgotPassword: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/pt/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/pt/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,wBAoBU"}
|
package/dist/styles/classes.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.108",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -453,7 +453,7 @@
|
|
|
453
453
|
"tailwind-merge": "^3.3.1",
|
|
454
454
|
"uuid": "^13.0.0",
|
|
455
455
|
"zod": "^4.1.5",
|
|
456
|
-
"@nextsparkjs/testing": "0.1.0-beta.
|
|
456
|
+
"@nextsparkjs/testing": "0.1.0-beta.108"
|
|
457
457
|
},
|
|
458
458
|
"scripts": {
|
|
459
459
|
"postinstall": "node scripts/postinstall.mjs || true",
|
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Polar.sh Webhook Handler
|
|
3
|
-
*
|
|
4
|
-
* Processes Polar webhook events for subscription lifecycle management.
|
|
5
|
-
* CRITICAL: Verifies webhook signatures using ALL request headers.
|
|
6
|
-
*
|
|
7
|
-
* Polar event types:
|
|
8
|
-
* - checkout.created / checkout.updated
|
|
9
|
-
* - subscription.created / subscription.updated / subscription.canceled
|
|
10
|
-
* - order.created / order.paid
|
|
11
|
-
*
|
|
12
|
-
* NOTE: This handler uses direct query() calls (bypassing RLS) because:
|
|
13
|
-
* 1. Webhooks have no user context (no session, no auth)
|
|
14
|
-
* 2. RLS policies require user membership which webhooks can't satisfy
|
|
15
|
-
* 3. Webhook signature verification provides security at the API level
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { NextRequest } from 'next/server'
|
|
19
|
-
import { query, queryOne } from '@nextsparkjs/core/lib/db'
|
|
20
|
-
|
|
21
|
-
// Polar webhook verification - import from gateway
|
|
22
|
-
import { getBillingGateway } from '@nextsparkjs/core/lib/billing/gateways/factory'
|
|
23
|
-
|
|
24
|
-
export async function POST(request: NextRequest) {
|
|
25
|
-
// 1. Get raw body and ALL headers (Polar needs full headers for verification)
|
|
26
|
-
const payload = await request.text()
|
|
27
|
-
const headers: Record<string, string> = {}
|
|
28
|
-
|
|
29
|
-
request.headers.forEach((value, key) => {
|
|
30
|
-
headers[key] = value
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// Verify required Polar webhook headers
|
|
34
|
-
if (!headers['webhook-id'] || !headers['webhook-signature'] || !headers['webhook-timestamp']) {
|
|
35
|
-
return Response.json(
|
|
36
|
-
{ error: 'Missing required webhook headers (webhook-id, webhook-signature, webhook-timestamp)' },
|
|
37
|
-
{ status: 400 }
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 2. Verify webhook signature (MANDATORY for security)
|
|
42
|
-
let event: { id: string; type: string; data: Record<string, unknown> }
|
|
43
|
-
try {
|
|
44
|
-
const gateway = getBillingGateway()
|
|
45
|
-
event = gateway.verifyWebhookSignature(payload, headers)
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error('[polar-webhook] Signature verification failed:', error)
|
|
48
|
-
return Response.json({ error: 'Invalid signature' }, { status: 400 })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 3. Check for duplicate events (idempotency)
|
|
52
|
-
const eventId = event.id || headers['webhook-id']
|
|
53
|
-
|
|
54
|
-
const existing = await queryOne(
|
|
55
|
-
`SELECT id FROM "billing_events" WHERE metadata->>'polarEventId' = $1`,
|
|
56
|
-
[eventId]
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
if (existing) {
|
|
60
|
-
console.log(`[polar-webhook] Event ${eventId} already processed, skipping`)
|
|
61
|
-
return Response.json({ received: true, status: 'duplicate' })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 4. Handle events
|
|
65
|
-
try {
|
|
66
|
-
console.log(`[polar-webhook] Processing event type: ${event.type}`)
|
|
67
|
-
|
|
68
|
-
switch (event.type) {
|
|
69
|
-
case 'checkout.updated':
|
|
70
|
-
await handleCheckoutUpdated(event.data, eventId)
|
|
71
|
-
break
|
|
72
|
-
|
|
73
|
-
case 'subscription.created':
|
|
74
|
-
await handleSubscriptionCreated(event.data, eventId)
|
|
75
|
-
break
|
|
76
|
-
|
|
77
|
-
case 'subscription.updated':
|
|
78
|
-
await handleSubscriptionUpdated(event.data, eventId)
|
|
79
|
-
break
|
|
80
|
-
|
|
81
|
-
case 'subscription.canceled':
|
|
82
|
-
await handleSubscriptionCanceled(event.data, eventId)
|
|
83
|
-
break
|
|
84
|
-
|
|
85
|
-
case 'order.paid':
|
|
86
|
-
await handleOrderPaid(event.data, eventId)
|
|
87
|
-
break
|
|
88
|
-
|
|
89
|
-
default:
|
|
90
|
-
console.log(`[polar-webhook] Unhandled event type: ${event.type}`)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return Response.json({ received: true })
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error('[polar-webhook] Handler error:', error)
|
|
96
|
-
return Response.json({ error: 'Handler failed' }, { status: 500 })
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ===========================================
|
|
101
|
-
// POLAR EVENT HANDLERS
|
|
102
|
-
// ===========================================
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Handle checkout.updated
|
|
106
|
-
* Polar fires this when a checkout session is completed (status changes to 'succeeded')
|
|
107
|
-
*/
|
|
108
|
-
async function handleCheckoutUpdated(data: Record<string, unknown>, eventId: string) {
|
|
109
|
-
const status = data.status as string
|
|
110
|
-
if (status !== 'succeeded') {
|
|
111
|
-
console.log(`[polar-webhook] Checkout status: ${status}, ignoring (only process succeeded)`)
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const metadata = data.metadata as Record<string, string> | undefined
|
|
116
|
-
const teamId = metadata?.teamId
|
|
117
|
-
if (!teamId) {
|
|
118
|
-
console.warn('[polar-webhook] No teamId in checkout metadata')
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const subscriptionId = data.subscriptionId as string | undefined
|
|
123
|
-
const customerId = data.customerId as string | undefined
|
|
124
|
-
const planSlug = metadata?.planSlug
|
|
125
|
-
const billingPeriod = metadata?.billingPeriod || 'monthly'
|
|
126
|
-
|
|
127
|
-
console.log(`[polar-webhook] Checkout completed for team ${teamId}, plan: ${planSlug}`)
|
|
128
|
-
|
|
129
|
-
// Get plan ID from slug
|
|
130
|
-
let planId: string | null = null
|
|
131
|
-
if (planSlug) {
|
|
132
|
-
const planResult = await queryOne<{ id: string }>(
|
|
133
|
-
`SELECT id FROM plans WHERE slug = $1 LIMIT 1`,
|
|
134
|
-
[planSlug]
|
|
135
|
-
)
|
|
136
|
-
planId = planResult?.id || null
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (!planId) {
|
|
140
|
-
console.warn(`[polar-webhook] Plan ${planSlug} not found in database, keeping current plan`)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Update subscription with Polar IDs
|
|
144
|
-
if (planId) {
|
|
145
|
-
await query(
|
|
146
|
-
`UPDATE subscriptions
|
|
147
|
-
SET "externalSubscriptionId" = $1,
|
|
148
|
-
"externalCustomerId" = $2,
|
|
149
|
-
"paymentProvider" = 'polar',
|
|
150
|
-
"planId" = $3,
|
|
151
|
-
"billingInterval" = $4,
|
|
152
|
-
status = 'active',
|
|
153
|
-
"updatedAt" = NOW()
|
|
154
|
-
WHERE "teamId" = $5
|
|
155
|
-
AND status IN ('active', 'trialing', 'past_due')`,
|
|
156
|
-
[subscriptionId || null, customerId || null, planId, billingPeriod, teamId]
|
|
157
|
-
)
|
|
158
|
-
} else {
|
|
159
|
-
await query(
|
|
160
|
-
`UPDATE subscriptions
|
|
161
|
-
SET "externalSubscriptionId" = $1,
|
|
162
|
-
"externalCustomerId" = $2,
|
|
163
|
-
"paymentProvider" = 'polar',
|
|
164
|
-
status = 'active',
|
|
165
|
-
"updatedAt" = NOW()
|
|
166
|
-
WHERE "teamId" = $3
|
|
167
|
-
AND status IN ('active', 'trialing', 'past_due')`,
|
|
168
|
-
[subscriptionId || null, customerId || null, teamId]
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Log billing event
|
|
173
|
-
const amount = data.amount as number | undefined
|
|
174
|
-
const currency = data.currency as string | undefined
|
|
175
|
-
await logBillingEvent({
|
|
176
|
-
teamId,
|
|
177
|
-
type: 'payment',
|
|
178
|
-
status: 'succeeded',
|
|
179
|
-
amount: amount || 0,
|
|
180
|
-
currency: currency || 'usd',
|
|
181
|
-
polarEventId: eventId,
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Handle subscription.created
|
|
187
|
-
* New subscription was created in Polar
|
|
188
|
-
*/
|
|
189
|
-
async function handleSubscriptionCreated(data: Record<string, unknown>, eventId: string) {
|
|
190
|
-
const polarSubId = data.id as string
|
|
191
|
-
const polarCustomerId = data.customerId as string | undefined
|
|
192
|
-
const status = mapPolarStatus(data.status as string)
|
|
193
|
-
|
|
194
|
-
console.log(`[polar-webhook] Subscription created: ${polarSubId}, status: ${status}`)
|
|
195
|
-
|
|
196
|
-
// Try to find existing subscription by customer ID
|
|
197
|
-
if (polarCustomerId) {
|
|
198
|
-
await query(
|
|
199
|
-
`UPDATE subscriptions
|
|
200
|
-
SET "externalSubscriptionId" = $1,
|
|
201
|
-
status = $2,
|
|
202
|
-
"paymentProvider" = 'polar',
|
|
203
|
-
"updatedAt" = NOW()
|
|
204
|
-
WHERE "externalCustomerId" = $3`,
|
|
205
|
-
[polarSubId, status, polarCustomerId]
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
// Log webhook event for idempotency tracking
|
|
209
|
-
await logWebhookEvent(polarCustomerId, 'subscription.created', eventId)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Handle subscription.updated
|
|
215
|
-
* Subscription status or plan changed
|
|
216
|
-
*/
|
|
217
|
-
async function handleSubscriptionUpdated(data: Record<string, unknown>, eventId: string) {
|
|
218
|
-
const polarSubId = data.id as string
|
|
219
|
-
const status = mapPolarStatus(data.status as string)
|
|
220
|
-
const cancelAtPeriodEnd = (data.cancelAtPeriodEnd as boolean) ?? false
|
|
221
|
-
|
|
222
|
-
console.log(`[polar-webhook] Subscription updated ${polarSubId}, status: ${status}`)
|
|
223
|
-
|
|
224
|
-
// Update subscription status
|
|
225
|
-
await query(
|
|
226
|
-
`UPDATE subscriptions
|
|
227
|
-
SET status = $1,
|
|
228
|
-
"cancelAtPeriodEnd" = $2,
|
|
229
|
-
"updatedAt" = NOW()
|
|
230
|
-
WHERE "externalSubscriptionId" = $3`,
|
|
231
|
-
[status, cancelAtPeriodEnd, polarSubId]
|
|
232
|
-
)
|
|
233
|
-
|
|
234
|
-
// Log webhook event for idempotency tracking
|
|
235
|
-
await logWebhookEventBySubId(polarSubId, 'subscription.updated', eventId)
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Handle subscription.canceled
|
|
240
|
-
* Subscription was canceled (revoked) in Polar
|
|
241
|
-
*/
|
|
242
|
-
async function handleSubscriptionCanceled(data: Record<string, unknown>, eventId: string) {
|
|
243
|
-
const polarSubId = data.id as string
|
|
244
|
-
|
|
245
|
-
console.log(`[polar-webhook] Subscription canceled ${polarSubId}`)
|
|
246
|
-
|
|
247
|
-
await query(
|
|
248
|
-
`UPDATE subscriptions
|
|
249
|
-
SET status = 'canceled',
|
|
250
|
-
"canceledAt" = NOW(),
|
|
251
|
-
"updatedAt" = NOW()
|
|
252
|
-
WHERE "externalSubscriptionId" = $1`,
|
|
253
|
-
[polarSubId]
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
// Log webhook event for idempotency tracking
|
|
257
|
-
await logWebhookEventBySubId(polarSubId, 'subscription.canceled', eventId)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Handle order.paid
|
|
262
|
-
* Payment was completed for an order (Polar's equivalent of invoice.paid)
|
|
263
|
-
*/
|
|
264
|
-
async function handleOrderPaid(data: Record<string, unknown>, eventId: string) {
|
|
265
|
-
const subscriptionId = data.subscriptionId as string | undefined
|
|
266
|
-
const amount = data.amount as number | undefined
|
|
267
|
-
const currency = data.currency as string | undefined
|
|
268
|
-
|
|
269
|
-
console.log(`[polar-webhook] Order paid: ${eventId}`)
|
|
270
|
-
|
|
271
|
-
if (subscriptionId) {
|
|
272
|
-
// Mark subscription as active
|
|
273
|
-
await query(
|
|
274
|
-
`UPDATE subscriptions
|
|
275
|
-
SET status = 'active',
|
|
276
|
-
"updatedAt" = NOW()
|
|
277
|
-
WHERE "externalSubscriptionId" = $1`,
|
|
278
|
-
[subscriptionId]
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
// Log billing event for audit trail (recurring payments)
|
|
282
|
-
const sub = await queryOne<{ id: string }>(
|
|
283
|
-
`SELECT id FROM subscriptions WHERE "externalSubscriptionId" = $1 LIMIT 1`,
|
|
284
|
-
[subscriptionId]
|
|
285
|
-
)
|
|
286
|
-
if (sub) {
|
|
287
|
-
await query(
|
|
288
|
-
`INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
|
|
289
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
290
|
-
[
|
|
291
|
-
sub.id,
|
|
292
|
-
'payment',
|
|
293
|
-
'succeeded',
|
|
294
|
-
amount || 0,
|
|
295
|
-
currency || 'usd',
|
|
296
|
-
JSON.stringify({ polarEventId: eventId })
|
|
297
|
-
]
|
|
298
|
-
)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// ===========================================
|
|
304
|
-
// HELPERS
|
|
305
|
-
// ===========================================
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Map Polar subscription status to our internal status
|
|
309
|
-
*/
|
|
310
|
-
function mapPolarStatus(polarStatus: string): string {
|
|
311
|
-
const statusMap: Record<string, string> = {
|
|
312
|
-
active: 'active',
|
|
313
|
-
trialing: 'trialing',
|
|
314
|
-
past_due: 'past_due',
|
|
315
|
-
canceled: 'canceled',
|
|
316
|
-
incomplete: 'past_due',
|
|
317
|
-
incomplete_expired: 'expired',
|
|
318
|
-
unpaid: 'past_due',
|
|
319
|
-
revoked: 'canceled',
|
|
320
|
-
}
|
|
321
|
-
return statusMap[polarStatus] || polarStatus
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Log billing event for audit trail.
|
|
326
|
-
* Also serves as idempotency record (polarEventId in metadata).
|
|
327
|
-
*/
|
|
328
|
-
async function logBillingEvent(params: {
|
|
329
|
-
teamId: string
|
|
330
|
-
type: string
|
|
331
|
-
status: string
|
|
332
|
-
amount: number
|
|
333
|
-
currency: string
|
|
334
|
-
polarEventId: string
|
|
335
|
-
}) {
|
|
336
|
-
const sub = await queryOne<{ id: string }>(
|
|
337
|
-
`SELECT id FROM subscriptions WHERE "teamId" = $1 LIMIT 1`,
|
|
338
|
-
[params.teamId]
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
if (!sub) {
|
|
342
|
-
console.warn(`[polar-webhook] No subscription found for team ${params.teamId}, cannot log billing event`)
|
|
343
|
-
return
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
await query(
|
|
347
|
-
`INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
|
|
348
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
349
|
-
[
|
|
350
|
-
sub.id,
|
|
351
|
-
params.type,
|
|
352
|
-
params.status,
|
|
353
|
-
params.amount,
|
|
354
|
-
params.currency,
|
|
355
|
-
JSON.stringify({ polarEventId: params.polarEventId })
|
|
356
|
-
]
|
|
357
|
-
)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Log a webhook event by customer ID for idempotency tracking.
|
|
362
|
-
* Used by subscription handlers that don't involve payments.
|
|
363
|
-
*/
|
|
364
|
-
async function logWebhookEvent(
|
|
365
|
-
polarCustomerId: string,
|
|
366
|
-
eventType: string,
|
|
367
|
-
polarEventId: string
|
|
368
|
-
) {
|
|
369
|
-
const sub = await queryOne<{ id: string }>(
|
|
370
|
-
`SELECT id FROM subscriptions WHERE "externalCustomerId" = $1 LIMIT 1`,
|
|
371
|
-
[polarCustomerId]
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
if (!sub) {
|
|
375
|
-
console.warn(`[polar-webhook] No subscription found for customer ${polarCustomerId}, cannot log webhook event`)
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
await query(
|
|
380
|
-
`INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
|
|
381
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
382
|
-
[sub.id, 'webhook', eventType, 0, 'usd', JSON.stringify({ polarEventId })]
|
|
383
|
-
)
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Log a webhook event by external subscription ID for idempotency tracking.
|
|
388
|
-
* Used by subscription handlers that identify by subscription ID.
|
|
389
|
-
*/
|
|
390
|
-
async function logWebhookEventBySubId(
|
|
391
|
-
polarSubId: string,
|
|
392
|
-
eventType: string,
|
|
393
|
-
polarEventId: string
|
|
394
|
-
) {
|
|
395
|
-
const sub = await queryOne<{ id: string }>(
|
|
396
|
-
`SELECT id FROM subscriptions WHERE "externalSubscriptionId" = $1 LIMIT 1`,
|
|
397
|
-
[polarSubId]
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
if (!sub) {
|
|
401
|
-
console.warn(`[polar-webhook] No subscription found for polar sub ${polarSubId}, cannot log webhook event`)
|
|
402
|
-
return
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
await query(
|
|
406
|
-
`INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
|
|
407
|
-
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
408
|
-
[sub.id, 'webhook', eventType, 0, 'usd', JSON.stringify({ polarEventId })]
|
|
409
|
-
)
|
|
410
|
-
}
|