feat: swap Stripe for Mollie payment integration
Replace Stripe checkout with Mollie payments API. Mollie handles payment method selection on their hosted checkout (card, SEPA, iDEAL, PayPal, etc). Simpler auth model — single API key, no webhook secrets. - Rewrite /api/create-checkout-session for Mollie payment creation - Rewrite /api/webhook for Mollie status verification flow - Update google-sheets.ts: stripeSessionId → paymentSessionId - Remove payment method radio buttons (Mollie shows all methods) - Update docker-compose env vars - Swap stripe npm package for @mollie/api-client Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3b63d62b95
commit
8f59233918
|
|
@ -1,9 +1,7 @@
|
||||||
# Stripe Payment Integration
|
# Mollie Payment Integration
|
||||||
STRIPE_SECRET_KEY=sk_live_xxx
|
MOLLIE_API_KEY=live_xxx
|
||||||
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
|
|
||||||
|
|
||||||
# Public URL (used for Stripe redirect URLs)
|
# Public URL (used for payment redirect URLs)
|
||||||
NEXT_PUBLIC_BASE_URL=https://cryptocommonsgather.ing
|
NEXT_PUBLIC_BASE_URL=https://cryptocommonsgather.ing
|
||||||
|
|
||||||
# Google Sheets Integration
|
# Google Sheets Integration
|
||||||
|
|
|
||||||
|
|
@ -1,138 +1,86 @@
|
||||||
import { type NextRequest, NextResponse } from "next/server"
|
import { type NextRequest, NextResponse } from "next/server"
|
||||||
import Stripe from "stripe"
|
import createMollieClient from "@mollie/api-client"
|
||||||
|
|
||||||
// Lazy initialization to avoid build-time errors
|
// Lazy initialization to avoid build-time errors
|
||||||
let stripe: Stripe | null = null
|
let mollieClient: ReturnType<typeof createMollieClient> | null = null
|
||||||
|
|
||||||
function getStripe() {
|
function getMollie() {
|
||||||
if (!stripe) {
|
if (!mollieClient) {
|
||||||
stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
mollieClient = createMollieClient({ apiKey: process.env.MOLLIE_API_KEY! })
|
||||||
apiVersion: "2024-12-18.acacia",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return stripe
|
return mollieClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic pricing configuration (in EUR cents)
|
// Dynamic pricing configuration (in EUR)
|
||||||
const TICKET_PRICE_CENTS = 8000 // €80 early bird
|
const TICKET_PRICE = 80 // €80 early bird
|
||||||
|
const DORM_PRICE = 235.2 // €235.20 (€39.20/night x 6)
|
||||||
|
const DOUBLE_PRICE = 301.2 // €301.20 (€50.20/night x 6)
|
||||||
|
const FOOD_PRICE = 135 // €135 (6 days)
|
||||||
|
|
||||||
// Public base URL (needed because request.nextUrl.origin returns internal Docker address)
|
// Public base URL
|
||||||
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || "https://cryptocommonsgather.ing"
|
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || "https://cryptocommonsgather.ing"
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const formData = await request.formData()
|
const formData = await request.formData()
|
||||||
const paymentMethod = formData.get("paymentMethod") as string
|
|
||||||
const registrationDataStr = formData.get("registrationData") as string
|
const registrationDataStr = formData.get("registrationData") as string
|
||||||
const includeAccommodation = formData.get("includeAccommodation") === "true"
|
const includeAccommodation = formData.get("includeAccommodation") === "true"
|
||||||
const accommodationType = formData.get("accommodationType") as string || "dorm"
|
const accommodationType = (formData.get("accommodationType") as string) || "dorm"
|
||||||
const includeFood = formData.get("includeFood") === "true"
|
const includeFood = formData.get("includeFood") === "true"
|
||||||
|
|
||||||
const registrationData = registrationDataStr ? JSON.parse(registrationDataStr) : null
|
const registrationData = registrationDataStr ? JSON.parse(registrationDataStr) : null
|
||||||
|
|
||||||
// Accommodation pricing (in EUR cents)
|
// Calculate total
|
||||||
const DORM_PRICE_CENTS = 23520 // €235.20 (€39.20/night x 6)
|
let total = TICKET_PRICE
|
||||||
const DOUBLE_PRICE_CENTS = 30120 // €301.20 (€50.20/night x 6)
|
const descriptionParts = ["CCG 2026 Ticket (€80)"]
|
||||||
const FOOD_PRICE_CENTS = 13500 // €135 (6 days)
|
|
||||||
|
|
||||||
// Build line items dynamically
|
|
||||||
const lineItems: Stripe.Checkout.SessionCreateParams.LineItem[] = [
|
|
||||||
{
|
|
||||||
price_data: {
|
|
||||||
currency: "eur",
|
|
||||||
product_data: {
|
|
||||||
name: "CCG 2026 Ticket",
|
|
||||||
description: "Crypto Commons Gathering 2026 - August 16-22, Austria",
|
|
||||||
},
|
|
||||||
unit_amount: TICKET_PRICE_CENTS,
|
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
if (includeAccommodation) {
|
if (includeAccommodation) {
|
||||||
const isDorm = accommodationType === "dorm"
|
const isDorm = accommodationType === "dorm"
|
||||||
lineItems.push({
|
const accomPrice = isDorm ? DORM_PRICE : DOUBLE_PRICE
|
||||||
price_data: {
|
total += accomPrice
|
||||||
currency: "eur",
|
descriptionParts.push(`${isDorm ? "Dorm" : "Double Room"} (€${accomPrice.toFixed(2)})`)
|
||||||
product_data: {
|
|
||||||
name: isDorm ? "Accommodation - Dorm (6 nights)" : "Accommodation - Double Room (6 nights)",
|
|
||||||
description: `Commons Hub, ${isDorm ? "dorm bed" : "double room"} — ${isDorm ? "€39.20" : "€50.20"}/night`,
|
|
||||||
},
|
|
||||||
unit_amount: isDorm ? DORM_PRICE_CENTS : DOUBLE_PRICE_CENTS,
|
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeFood) {
|
if (includeFood) {
|
||||||
lineItems.push({
|
total += FOOD_PRICE
|
||||||
price_data: {
|
descriptionParts.push(`Food Package (€${FOOD_PRICE})`)
|
||||||
currency: "eur",
|
|
||||||
product_data: {
|
|
||||||
name: "Food Package (6 days)",
|
|
||||||
description: "Breakfast, catered lunches & dinners, coffee & tea",
|
|
||||||
},
|
|
||||||
unit_amount: FOOD_PRICE_CENTS,
|
|
||||||
},
|
|
||||||
quantity: 1,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let paymentMethodTypes: Stripe.Checkout.SessionCreateParams.PaymentMethodType[] = ["card"]
|
// Build metadata for webhook
|
||||||
|
const metadata: Record<string, string> = {}
|
||||||
if (paymentMethod === "sepa_debit") {
|
if (registrationData) {
|
||||||
paymentMethodTypes = ["sepa_debit"]
|
metadata.name = registrationData.name || ""
|
||||||
} else if (paymentMethod === "crypto") {
|
metadata.contact = registrationData.contact || ""
|
||||||
paymentMethodTypes = ["customer_balance"]
|
metadata.contributions = (registrationData.contributions || "").substring(0, 500)
|
||||||
|
metadata.expectations = (registrationData.expectations || "").substring(0, 500)
|
||||||
|
metadata.howHeard = registrationData.howHeard || ""
|
||||||
|
metadata.dietary =
|
||||||
|
(registrationData.dietary || []).join(", ") +
|
||||||
|
(registrationData.dietaryOther ? `, ${registrationData.dietaryOther}` : "")
|
||||||
|
metadata.crewConsent = registrationData.crewConsent || ""
|
||||||
|
metadata.accommodation = includeAccommodation ? accommodationType : "none"
|
||||||
|
metadata.food = includeFood ? "yes" : "no"
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await getStripe().checkout.sessions.create({
|
const payment = await getMollie().payments.create({
|
||||||
payment_method_types: paymentMethodTypes,
|
amount: {
|
||||||
line_items: lineItems,
|
value: total.toFixed(2),
|
||||||
mode: "payment",
|
currency: "EUR",
|
||||||
success_url: `${BASE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
||||||
cancel_url: `${BASE_URL}/register`,
|
|
||||||
metadata: registrationData
|
|
||||||
? {
|
|
||||||
name: registrationData.name,
|
|
||||||
contact: registrationData.contact,
|
|
||||||
contributions: registrationData.contributions.substring(0, 500),
|
|
||||||
expectations: registrationData.expectations.substring(0, 500),
|
|
||||||
howHeard: registrationData.howHeard || "",
|
|
||||||
dietary:
|
|
||||||
registrationData.dietary.join(", ") +
|
|
||||||
(registrationData.dietaryOther ? `, ${registrationData.dietaryOther}` : ""),
|
|
||||||
crewConsent: registrationData.crewConsent,
|
|
||||||
accommodation: includeAccommodation ? accommodationType : "none",
|
|
||||||
food: includeFood ? "yes" : "no",
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
...(paymentMethod === "crypto" && {
|
|
||||||
payment_method_options: {
|
|
||||||
customer_balance: {
|
|
||||||
funding_type: "bank_transfer",
|
|
||||||
bank_transfer: {
|
|
||||||
type: "us_bank_account",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
allow_promotion_codes: true,
|
|
||||||
billing_address_collection: "required",
|
|
||||||
phone_number_collection: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
},
|
||||||
|
description: `CCG 2026 Registration — ${descriptionParts.join(" + ")}`,
|
||||||
|
redirectUrl: `${BASE_URL}/success`,
|
||||||
|
webhookUrl: `${BASE_URL}/api/webhook`,
|
||||||
|
metadata,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use 303 redirect for POST requests (tells browser to follow with GET)
|
// Redirect to Mollie checkout
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 303,
|
status: 303,
|
||||||
headers: { Location: session.url! },
|
headers: { Location: payment.getCheckoutUrl()! },
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error creating checkout session:", err)
|
console.error("Error creating Mollie payment:", err)
|
||||||
return NextResponse.json({ error: "Error creating checkout session" }, { status: 500 })
|
return NextResponse.json({ error: "Error creating payment" }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,121 +1,93 @@
|
||||||
import { type NextRequest, NextResponse } from "next/server"
|
import { type NextRequest, NextResponse } from "next/server"
|
||||||
import Stripe from "stripe"
|
import createMollieClient from "@mollie/api-client"
|
||||||
import { updatePaymentStatus } from "@/lib/google-sheets"
|
import { updatePaymentStatus } from "@/lib/google-sheets"
|
||||||
import { sendPaymentConfirmation } from "@/lib/email"
|
import { sendPaymentConfirmation } from "@/lib/email"
|
||||||
import { addToListmonk } from "@/lib/listmonk"
|
import { addToListmonk } from "@/lib/listmonk"
|
||||||
|
|
||||||
// Lazy initialization to avoid build-time errors
|
// Lazy initialization
|
||||||
let stripe: Stripe | null = null
|
let mollieClient: ReturnType<typeof createMollieClient> | null = null
|
||||||
|
|
||||||
function getStripe() {
|
function getMollie() {
|
||||||
if (!stripe) {
|
if (!mollieClient) {
|
||||||
stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
|
mollieClient = createMollieClient({ apiKey: process.env.MOLLIE_API_KEY! })
|
||||||
apiVersion: "2024-12-18.acacia",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return stripe
|
return mollieClient
|
||||||
}
|
|
||||||
|
|
||||||
function getWebhookSecret() {
|
|
||||||
return process.env.STRIPE_WEBHOOK_SECRET!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = await request.text()
|
// Mollie sends payment ID in the body as form data
|
||||||
const signature = request.headers.get("stripe-signature")!
|
const formData = await request.formData()
|
||||||
|
const paymentId = formData.get("id") as string
|
||||||
|
|
||||||
let event: Stripe.Event
|
if (!paymentId) {
|
||||||
|
console.error("[Webhook] No payment ID received")
|
||||||
try {
|
return NextResponse.json({ error: "Missing payment ID" }, { status: 400 })
|
||||||
event = getStripe().webhooks.constructEvent(body, signature, getWebhookSecret())
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[Webhook] Signature verification failed:", err)
|
|
||||||
return NextResponse.json({ error: "Invalid signature" }, { status: 400 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the event
|
// Fetch the full payment from Mollie API (this is how you verify — no signature needed)
|
||||||
switch (event.type) {
|
const payment = await getMollie().payments.get(paymentId)
|
||||||
case "checkout.session.completed": {
|
const metadata = (payment.metadata || {}) as Record<string, string>
|
||||||
const session = event.data.object as Stripe.Checkout.Session
|
|
||||||
console.log("[Webhook] Payment successful:", session.id)
|
|
||||||
|
|
||||||
// Extract registration data from metadata
|
console.log(`[Webhook] Payment ${paymentId} status: ${payment.status}`)
|
||||||
const metadata = session.metadata || {}
|
|
||||||
const customerEmail = session.customer_details?.email || ""
|
|
||||||
|
|
||||||
// Update Google Sheet with payment confirmation
|
if (payment.status === "paid") {
|
||||||
const updated = await updatePaymentStatus({
|
const customerEmail = payment.billingAddress?.email || ""
|
||||||
|
const amountPaid = `€${payment.amount.value}`
|
||||||
|
|
||||||
|
// Update Google Sheet
|
||||||
|
const updated = await updatePaymentStatus({
|
||||||
|
name: metadata.name || "",
|
||||||
|
email: customerEmail,
|
||||||
|
paymentSessionId: paymentId,
|
||||||
|
paymentStatus: "Paid",
|
||||||
|
paymentMethod: payment.method || "unknown",
|
||||||
|
amountPaid,
|
||||||
|
paymentDate: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
console.log(`[Webhook] Google Sheet updated for ${metadata.name}`)
|
||||||
|
} else {
|
||||||
|
console.error(`[Webhook] Failed to update Google Sheet for ${metadata.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send confirmation email
|
||||||
|
if (customerEmail) {
|
||||||
|
await sendPaymentConfirmation({
|
||||||
name: metadata.name || "",
|
name: metadata.name || "",
|
||||||
email: customerEmail,
|
email: customerEmail,
|
||||||
stripeSessionId: session.id,
|
amountPaid,
|
||||||
paymentStatus: "Paid",
|
paymentMethod: payment.method || "card",
|
||||||
paymentMethod: session.payment_method_types?.[0] || "unknown",
|
contributions: metadata.contributions || "",
|
||||||
amountPaid: session.amount_total
|
dietary: metadata.dietary || "",
|
||||||
? `€${(session.amount_total / 100).toFixed(2)}`
|
|
||||||
: "",
|
|
||||||
paymentDate: new Date().toISOString(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (updated) {
|
// Add to Listmonk newsletter
|
||||||
console.log(`[Webhook] Google Sheet updated for ${metadata.name}`)
|
addToListmonk({
|
||||||
} else {
|
email: customerEmail,
|
||||||
console.error(`[Webhook] Failed to update Google Sheet for ${metadata.name}`)
|
name: metadata.name || "",
|
||||||
}
|
attribs: {
|
||||||
|
contact: metadata.contact,
|
||||||
// Send payment confirmation email
|
contributions: metadata.contributions,
|
||||||
if (customerEmail) {
|
expectations: metadata.expectations,
|
||||||
await sendPaymentConfirmation({
|
},
|
||||||
name: metadata.name || "",
|
}).catch((err) => console.error("[Webhook] Listmonk sync failed:", err))
|
||||||
email: customerEmail,
|
|
||||||
amountPaid: session.amount_total
|
|
||||||
? `€${(session.amount_total / 100).toFixed(2)}`
|
|
||||||
: "",
|
|
||||||
paymentMethod: session.payment_method_types?.[0] || "card",
|
|
||||||
contributions: metadata.contributions || "",
|
|
||||||
dietary: metadata.dietary || "",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add to Listmonk newsletter
|
|
||||||
addToListmonk({
|
|
||||||
email: customerEmail,
|
|
||||||
name: metadata.name || "",
|
|
||||||
attribs: {
|
|
||||||
contact: metadata.contact,
|
|
||||||
contributions: metadata.contributions,
|
|
||||||
expectations: metadata.expectations,
|
|
||||||
},
|
|
||||||
}).catch((err) => console.error("[Webhook] Listmonk sync failed:", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
case "payment_intent.succeeded": {
|
} else if (payment.status === "failed" || payment.status === "canceled" || payment.status === "expired") {
|
||||||
const paymentIntent = event.data.object as Stripe.PaymentIntent
|
console.log(`[Webhook] Payment ${payment.status}: ${paymentId}`)
|
||||||
console.log("[Webhook] PaymentIntent successful:", paymentIntent.id)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "payment_intent.payment_failed": {
|
|
||||||
const paymentIntent = event.data.object as Stripe.PaymentIntent
|
|
||||||
console.error("[Webhook] Payment failed:", paymentIntent.id)
|
|
||||||
|
|
||||||
// Optionally update sheet to mark as failed
|
if (metadata.name) {
|
||||||
const failedMetadata = paymentIntent.metadata || {}
|
await updatePaymentStatus({
|
||||||
if (failedMetadata.name) {
|
name: metadata.name,
|
||||||
await updatePaymentStatus({
|
paymentSessionId: paymentId,
|
||||||
name: failedMetadata.name,
|
paymentStatus: "Failed",
|
||||||
stripeSessionId: paymentIntent.id,
|
paymentDate: new Date().toISOString(),
|
||||||
paymentStatus: "Failed",
|
})
|
||||||
paymentDate: new Date().toISOString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
console.log("[Webhook] Unhandled event type:", event.type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mollie expects 200 OK
|
||||||
return NextResponse.json({ received: true })
|
return NextResponse.json({ received: true })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("[Webhook] Error:", err)
|
console.error("[Webhook] Error:", err)
|
||||||
|
|
|
||||||
|
|
@ -258,34 +258,10 @@ export default function RegisterPage() {
|
||||||
<input type="hidden" name="includeFood" value={includeFood ? "true" : "false"} />
|
<input type="hidden" name="includeFood" value={includeFood ? "true" : "false"} />
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<p className="text-sm text-muted-foreground">
|
||||||
<Label className="text-base font-semibold mb-3 block">Select Payment Method</Label>
|
You'll be redirected to Mollie's secure checkout where you can pay by credit card,
|
||||||
<RadioGroup name="paymentMethod" defaultValue="card" className="space-y-3">
|
SEPA bank transfer, iDEAL, PayPal, or other methods.
|
||||||
<div className="flex items-center space-x-2 border rounded-lg p-4 hover:border-primary transition-colors">
|
</p>
|
||||||
<RadioGroupItem value="card" id="card" />
|
|
||||||
<Label htmlFor="card" className="flex-1 cursor-pointer">
|
|
||||||
<div className="font-medium">Credit Card</div>
|
|
||||||
<div className="text-sm text-muted-foreground">Visa, Mastercard, Amex</div>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 border rounded-lg p-4 hover:border-primary transition-colors">
|
|
||||||
<RadioGroupItem value="sepa_debit" id="sepa" />
|
|
||||||
<Label htmlFor="sepa" className="flex-1 cursor-pointer">
|
|
||||||
<div className="font-medium">SEPA Bank Transfer</div>
|
|
||||||
<div className="text-sm text-muted-foreground">Direct debit from European banks</div>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 border rounded-lg p-4 hover:border-primary transition-colors border-primary/30">
|
|
||||||
<RadioGroupItem value="crypto" id="crypto" />
|
|
||||||
<Label htmlFor="crypto" className="flex-1 cursor-pointer">
|
|
||||||
<div className="font-medium">Cryptocurrency</div>
|
|
||||||
<div className="text-sm text-muted-foreground">USDC Stablecoin (settles as EUR)</div>
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
</RadioGroup>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex gap-3 pt-4">
|
<div className="flex gap-3 pt-4">
|
||||||
<Button type="button" variant="outline" onClick={() => setStep("form")} className="flex-1">
|
<Button type="button" variant="outline" onClick={() => setStep("form")} className="flex-1">
|
||||||
|
|
@ -302,7 +278,7 @@ export default function RegisterPage() {
|
||||||
|
|
||||||
<div className="text-center text-sm text-muted-foreground">
|
<div className="text-center text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
All payments are processed securely through Stripe. You'll receive a confirmation email after successful
|
All payments are processed securely through Mollie. You'll receive a confirmation email after successful
|
||||||
payment.
|
payment.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
- MOLLIE_API_KEY=${MOLLIE_API_KEY}
|
||||||
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
|
||||||
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
|
|
||||||
- NEXT_PUBLIC_BASE_URL=https://staging-ccg.jeffemmett.com
|
- NEXT_PUBLIC_BASE_URL=https://staging-ccg.jeffemmett.com
|
||||||
- GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY}
|
- GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY}
|
||||||
- GOOGLE_SHEET_ID=${GOOGLE_SHEET_ID}
|
- GOOGLE_SHEET_ID=${GOOGLE_SHEET_ID}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
|
- MOLLIE_API_KEY=${MOLLIE_API_KEY}
|
||||||
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
|
||||||
- NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
|
|
||||||
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-https://cryptocommonsgather.ing}
|
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-https://cryptocommonsgather.ing}
|
||||||
- GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY}
|
- GOOGLE_SERVICE_ACCOUNT_KEY=${GOOGLE_SERVICE_ACCOUNT_KEY}
|
||||||
- GOOGLE_SHEET_ID=${GOOGLE_SHEET_ID}
|
- GOOGLE_SHEET_ID=${GOOGLE_SHEET_ID}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export interface RegistrationData {
|
||||||
export interface PaymentUpdateData {
|
export interface PaymentUpdateData {
|
||||||
email?: string
|
email?: string
|
||||||
name?: string
|
name?: string
|
||||||
stripeSessionId: string
|
paymentSessionId: string
|
||||||
paymentStatus: "Paid" | "Failed"
|
paymentStatus: "Paid" | "Failed"
|
||||||
paymentMethod?: string
|
paymentMethod?: string
|
||||||
amountPaid?: string
|
amountPaid?: string
|
||||||
|
|
@ -131,7 +131,7 @@ export async function updatePaymentStatus(data: PaymentUpdateData): Promise<bool
|
||||||
rows[targetRowIndex - 1][8], // I: Crew Consent (preserve)
|
rows[targetRowIndex - 1][8], // I: Crew Consent (preserve)
|
||||||
data.paymentStatus, // J: Payment Status
|
data.paymentStatus, // J: Payment Status
|
||||||
data.paymentMethod || "", // K: Payment Method
|
data.paymentMethod || "", // K: Payment Method
|
||||||
data.stripeSessionId, // L: Stripe Session ID
|
data.paymentSessionId, // L: Stripe Session ID
|
||||||
data.amountPaid || "", // M: Amount Paid
|
data.amountPaid || "", // M: Amount Paid
|
||||||
data.paymentDate || new Date().toISOString(), // N: Payment Date
|
data.paymentDate || new Date().toISOString(), // N: Payment Date
|
||||||
],
|
],
|
||||||
|
|
@ -180,7 +180,7 @@ export async function initializeSheetHeaders(): Promise<void> {
|
||||||
"Crew Consent",
|
"Crew Consent",
|
||||||
"Payment Status",
|
"Payment Status",
|
||||||
"Payment Method",
|
"Payment Method",
|
||||||
"Stripe Session ID",
|
"Payment Session ID",
|
||||||
"Amount Paid",
|
"Amount Paid",
|
||||||
"Payment Date",
|
"Payment Date",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
|
"@mollie/api-client": "^4.4.0",
|
||||||
"@radix-ui/react-accordion": "1.2.2",
|
"@radix-ui/react-accordion": "1.2.2",
|
||||||
"@radix-ui/react-alert-dialog": "1.1.4",
|
"@radix-ui/react-alert-dialog": "1.1.4",
|
||||||
"@radix-ui/react-aspect-ratio": "1.1.1",
|
"@radix-ui/react-aspect-ratio": "1.1.1",
|
||||||
|
|
@ -58,7 +59,6 @@
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
"recharts": "2.15.4",
|
"recharts": "2.15.4",
|
||||||
"sonner": "^1.7.4",
|
"sonner": "^1.7.4",
|
||||||
"stripe": "latest",
|
|
||||||
"tailwind-merge": "^2.5.5",
|
"tailwind-merge": "^2.5.5",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
|
|
|
||||||
141
pnpm-lock.yaml
141
pnpm-lock.yaml
|
|
@ -11,6 +11,9 @@ importers:
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
specifier: ^3.10.0
|
specifier: ^3.10.0
|
||||||
version: 3.10.0(react-hook-form@7.69.0(react@19.2.0))
|
version: 3.10.0(react-hook-form@7.69.0(react@19.2.0))
|
||||||
|
'@mollie/api-client':
|
||||||
|
specifier: ^4.4.0
|
||||||
|
version: 4.4.0
|
||||||
'@radix-ui/react-accordion':
|
'@radix-ui/react-accordion':
|
||||||
specifier: 1.2.2
|
specifier: 1.2.2
|
||||||
version: 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
version: 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
|
|
@ -155,9 +158,6 @@ importers:
|
||||||
sonner:
|
sonner:
|
||||||
specifier: ^1.7.4
|
specifier: ^1.7.4
|
||||||
version: 1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
version: 1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||||
stripe:
|
|
||||||
specifier: latest
|
|
||||||
version: 20.1.0(@types/node@22.19.3)
|
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^2.5.5
|
specifier: ^2.5.5
|
||||||
version: 2.6.0
|
version: 2.6.0
|
||||||
|
|
@ -411,6 +411,10 @@ packages:
|
||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@mollie/api-client@4.4.0':
|
||||||
|
resolution: {integrity: sha512-V2OKBGS4TUKpbARV4JSjjtXqfRqfTQ5KCKGudEFEOEh/yQxrM2zXhkoq2gIbA6nGIVPh5nVaAzqLhp4C+e4aMQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
'@next/env@16.0.10':
|
'@next/env@16.0.10':
|
||||||
resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
|
resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
|
||||||
|
|
||||||
|
|
@ -1375,6 +1379,9 @@ packages:
|
||||||
'@types/d3-timer@3.0.2':
|
'@types/d3-timer@3.0.2':
|
||||||
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
|
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
|
||||||
|
|
||||||
|
'@types/node-fetch@2.6.13':
|
||||||
|
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
|
||||||
|
|
||||||
'@types/node@22.19.3':
|
'@types/node@22.19.3':
|
||||||
resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==}
|
resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==}
|
||||||
|
|
||||||
|
|
@ -1416,6 +1423,9 @@ packages:
|
||||||
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
asynckit@0.4.0:
|
||||||
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
|
|
||||||
autoprefixer@10.4.23:
|
autoprefixer@10.4.23:
|
||||||
resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
|
resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
@ -1481,6 +1491,10 @@ packages:
|
||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
@ -1554,6 +1568,10 @@ packages:
|
||||||
decimal.js-light@2.5.1:
|
decimal.js-light@2.5.1:
|
||||||
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
|
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
|
||||||
|
|
||||||
|
delayed-stream@1.0.0:
|
||||||
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2:
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -1612,6 +1630,10 @@ packages:
|
||||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
escalade@3.2.0:
|
escalade@3.2.0:
|
||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -1634,6 +1656,10 @@ packages:
|
||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
form-data@4.0.5:
|
||||||
|
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
formdata-polyfill@4.0.10:
|
formdata-polyfill@4.0.10:
|
||||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||||
engines: {node: '>=12.20.0'}
|
engines: {node: '>=12.20.0'}
|
||||||
|
|
@ -1699,6 +1725,10 @@ packages:
|
||||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
has-tostringtag@1.0.2:
|
||||||
|
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
@ -1839,6 +1869,14 @@ packages:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
minimatch@9.0.5:
|
minimatch@9.0.5:
|
||||||
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
||||||
engines: {node: '>=16 || 14 >=14.17'}
|
engines: {node: '>=16 || 14 >=14.17'}
|
||||||
|
|
@ -1887,6 +1925,15 @@ packages:
|
||||||
engines: {node: '>=10.5.0'}
|
engines: {node: '>=10.5.0'}
|
||||||
deprecated: Use your platform's native DOMException instead
|
deprecated: Use your platform's native DOMException instead
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
|
||||||
node-fetch@3.3.2:
|
node-fetch@3.3.2:
|
||||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
|
@ -2083,6 +2130,9 @@ packages:
|
||||||
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
|
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
ruply@1.0.1:
|
||||||
|
resolution: {integrity: sha512-p39LnaaJyuucPGlgaB0KiyifpcuOkn24+Hq5y0ejAD/LlH+mRAbkHn2tckCLgHir+S+nis1WYG+TYEC4zHX0WQ==}
|
||||||
|
|
||||||
safe-buffer@5.2.1:
|
safe-buffer@5.2.1:
|
||||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||||
|
|
||||||
|
|
@ -2156,15 +2206,6 @@ packages:
|
||||||
resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
|
resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
stripe@20.1.0:
|
|
||||||
resolution: {integrity: sha512-o1VNRuMkY76ZCq92U3EH3/XHm/WHp7AerpzDs4Zyo8uE5mFL4QUcv/2SudWsSnhBSp4moO2+ZoGCZ7mT8crPmQ==}
|
|
||||||
engines: {node: '>=16'}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/node': '>=16'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/node':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
styled-jsx@5.1.6:
|
styled-jsx@5.1.6:
|
||||||
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
@ -2196,6 +2237,9 @@ packages:
|
||||||
tiny-invariant@1.3.3:
|
tiny-invariant@1.3.3:
|
||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
|
|
||||||
|
tr46@0.0.3:
|
||||||
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
|
@ -2257,6 +2301,12 @@ packages:
|
||||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1:
|
||||||
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
@ -2436,6 +2486,14 @@ snapshots:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@mollie/api-client@4.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@types/node-fetch': 2.6.13
|
||||||
|
node-fetch: 2.7.0
|
||||||
|
ruply: 1.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
'@next/env@16.0.10': {}
|
'@next/env@16.0.10': {}
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@16.0.10':
|
'@next/swc-darwin-arm64@16.0.10':
|
||||||
|
|
@ -3357,6 +3415,11 @@ snapshots:
|
||||||
|
|
||||||
'@types/d3-timer@3.0.2': {}
|
'@types/d3-timer@3.0.2': {}
|
||||||
|
|
||||||
|
'@types/node-fetch@2.6.13':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.19.3
|
||||||
|
form-data: 4.0.5
|
||||||
|
|
||||||
'@types/node@22.19.3':
|
'@types/node@22.19.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.21.0
|
undici-types: 6.21.0
|
||||||
|
|
@ -3395,6 +3458,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
asynckit@0.4.0: {}
|
||||||
|
|
||||||
autoprefixer@10.4.23(postcss@8.5.6):
|
autoprefixer@10.4.23(postcss@8.5.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.28.1
|
browserslist: 4.28.1
|
||||||
|
|
@ -3464,6 +3529,10 @@ snapshots:
|
||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
dependencies:
|
||||||
|
delayed-stream: 1.0.0
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
|
|
@ -3522,6 +3591,8 @@ snapshots:
|
||||||
|
|
||||||
decimal.js-light@2.5.1: {}
|
decimal.js-light@2.5.1: {}
|
||||||
|
|
||||||
|
delayed-stream@1.0.0: {}
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
detect-node-es@1.1.0: {}
|
detect-node-es@1.1.0: {}
|
||||||
|
|
@ -3574,6 +3645,13 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
|
|
||||||
|
es-set-tostringtag@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
es-errors: 1.3.0
|
||||||
|
get-intrinsic: 1.3.0
|
||||||
|
has-tostringtag: 1.0.2
|
||||||
|
hasown: 2.0.2
|
||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
eventemitter3@4.0.7: {}
|
eventemitter3@4.0.7: {}
|
||||||
|
|
@ -3592,6 +3670,14 @@ snapshots:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
|
form-data@4.0.5:
|
||||||
|
dependencies:
|
||||||
|
asynckit: 0.4.0
|
||||||
|
combined-stream: 1.0.8
|
||||||
|
es-set-tostringtag: 2.1.0
|
||||||
|
hasown: 2.0.2
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
formdata-polyfill@4.0.10:
|
formdata-polyfill@4.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
fetch-blob: 3.2.0
|
fetch-blob: 3.2.0
|
||||||
|
|
@ -3690,6 +3776,10 @@ snapshots:
|
||||||
|
|
||||||
has-symbols@1.1.0: {}
|
has-symbols@1.1.0: {}
|
||||||
|
|
||||||
|
has-tostringtag@1.0.2:
|
||||||
|
dependencies:
|
||||||
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
@ -3804,6 +3894,12 @@ snapshots:
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.52.0
|
||||||
|
|
||||||
minimatch@9.0.5:
|
minimatch@9.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.0.2
|
brace-expansion: 2.0.2
|
||||||
|
|
@ -3844,6 +3940,10 @@ snapshots:
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
node-domexception@1.0.0: {}
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
|
||||||
node-fetch@3.3.2:
|
node-fetch@3.3.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
data-uri-to-buffer: 4.0.1
|
data-uri-to-buffer: 4.0.1
|
||||||
|
|
@ -4036,6 +4136,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
glob: 10.5.0
|
glob: 10.5.0
|
||||||
|
|
||||||
|
ruply@1.0.1: {}
|
||||||
|
|
||||||
safe-buffer@5.2.1: {}
|
safe-buffer@5.2.1: {}
|
||||||
|
|
||||||
scheduler@0.27.0: {}
|
scheduler@0.27.0: {}
|
||||||
|
|
@ -4140,12 +4242,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex: 6.2.2
|
ansi-regex: 6.2.2
|
||||||
|
|
||||||
stripe@20.1.0(@types/node@22.19.3):
|
|
||||||
dependencies:
|
|
||||||
qs: 6.14.0
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/node': 22.19.3
|
|
||||||
|
|
||||||
styled-jsx@5.1.6(react@19.2.0):
|
styled-jsx@5.1.6(react@19.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
client-only: 0.0.1
|
client-only: 0.0.1
|
||||||
|
|
@ -4163,6 +4259,8 @@ snapshots:
|
||||||
|
|
||||||
tiny-invariant@1.3.3: {}
|
tiny-invariant@1.3.3: {}
|
||||||
|
|
||||||
|
tr46@0.0.3: {}
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
tw-animate-css@1.3.3: {}
|
tw-animate-css@1.3.3: {}
|
||||||
|
|
@ -4226,6 +4324,13 @@ snapshots:
|
||||||
|
|
||||||
web-streams-polyfill@3.3.3: {}
|
web-streams-polyfill@3.3.3: {}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue