Skip to content

Commit c0a2258

Browse files
Fix Vercel environment variables
The user is experiencing issues with their payment methods not displaying correctly, even after previous fixes. They are asking if they should remove environment variables in Vercel. This commit addresses potential issues related to Vercel environment variable configuration for Stripe integration.
1 parent 2e858cd commit c0a2258

10 files changed

Lines changed: 353 additions & 173 deletions

File tree

functions/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { stripeDeletePaymentMethod } from './stripeDeletePaymentMethod';
1111
import { stripeSetDefaultPaymentMethod } from './stripeSetDefaultPaymentMethod';
1212
import { stripeCreateSetup } from './stripeCreateSetup';
1313
import { stripeCheckSetup } from './stripeCheckSetup';
14+
import { stripeCreateConnectAccount, stripeCreateAccountLink, stripeCreateTransfer } from './stripeConnectAccount';
15+
import { stripeCreateInvoice } from './stripeInvoice';
1416

1517
// Initialize Firebase Admin
1618
admin.initializeApp();
@@ -29,5 +31,9 @@ export {
2931
stripeDeletePaymentMethod,
3032
stripeSetDefaultPaymentMethod,
3133
stripeCreateSetup,
32-
stripeCheckSetup
34+
stripeCheckSetup,
35+
stripeCreateConnectAccount,
36+
stripeCreateAccountLink,
37+
stripeCreateTransfer,
38+
stripeCreateInvoice
3339
};
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import * as functions from 'firebase-functions';
2+
import { stripe } from './stripeConfig';
3+
4+
export const stripeCreateConnectAccount = functions.https.onRequest(async (req, res) => {
5+
res.set('Access-Control-Allow-Origin', '*');
6+
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
7+
res.set('Access-Control-Allow-Headers', 'Content-Type');
8+
9+
if (req.method === 'OPTIONS') {
10+
return res.status(200).end();
11+
}
12+
13+
if (req.method !== 'POST') {
14+
return res.status(405).json({ error: 'Method not allowed' });
15+
}
16+
17+
try {
18+
const { affiliateEmail, affiliateName, country = 'FR' } = req.body;
19+
20+
if (!affiliateEmail || !affiliateName) {
21+
return res.status(400).json({ error: 'affiliateEmail and affiliateName are required' });
22+
}
23+
24+
console.log('🔗 Creating Stripe Connect account for:', affiliateEmail);
25+
26+
const account = await stripe.accounts.create({
27+
type: 'express',
28+
country: country,
29+
email: affiliateEmail,
30+
capabilities: {
31+
card_payments: { requested: true },
32+
transfers: { requested: true },
33+
},
34+
metadata: {
35+
affiliateEmail,
36+
affiliateName,
37+
},
38+
});
39+
40+
console.log('✅ Stripe Connect account created:', account.id);
41+
42+
return res.json({
43+
accountId: account.id,
44+
email: affiliateEmail,
45+
name: affiliateName,
46+
country: account.country,
47+
created: account.created,
48+
});
49+
50+
} catch (error) {
51+
console.error('❌ Error creating Stripe Connect account:', error);
52+
return res.status(500).json({
53+
error: 'Failed to create Stripe Connect account',
54+
details: error instanceof Error ? error.message : 'Unknown error'
55+
});
56+
}
57+
});
58+
59+
export const stripeCreateAccountLink = functions.https.onRequest(async (req, res) => {
60+
res.set('Access-Control-Allow-Origin', '*');
61+
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
62+
res.set('Access-Control-Allow-Headers', 'Content-Type');
63+
64+
if (req.method === 'OPTIONS') {
65+
return res.status(200).end();
66+
}
67+
68+
if (req.method !== 'POST') {
69+
return res.status(405).json({ error: 'Method not allowed' });
70+
}
71+
72+
try {
73+
const { accountId, affiliateId, refreshUrl, returnUrl } = req.body;
74+
75+
if (!accountId) {
76+
return res.status(400).json({ error: 'accountId is required' });
77+
}
78+
79+
console.log('🔗 Creating account link for:', accountId);
80+
81+
const origin = req.headers.origin || 'http://localhost:5173';
82+
83+
const accountLink = await stripe.accountLinks.create({
84+
account: accountId,
85+
refresh_url: refreshUrl || `${origin}/affiliate-onboarding?refresh=true&account=${accountId}`,
86+
return_url: returnUrl || `${origin}/affiliate-onboarding?success=true&account=${accountId}`,
87+
type: 'account_onboarding',
88+
});
89+
90+
console.log('✅ Account link created:', accountLink.url);
91+
92+
return res.json({
93+
url: accountLink.url,
94+
expires_at: accountLink.expires_at,
95+
});
96+
97+
} catch (error) {
98+
console.error('❌ Error creating account link:', error);
99+
return res.status(500).json({
100+
error: 'Failed to create account link',
101+
details: error instanceof Error ? error.message : 'Unknown error'
102+
});
103+
}
104+
});
105+
106+
export const stripeCreateTransfer = functions.https.onRequest(async (req, res) => {
107+
res.set('Access-Control-Allow-Origin', '*');
108+
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
109+
res.set('Access-Control-Allow-Headers', 'Content-Type');
110+
111+
if (req.method === 'OPTIONS') {
112+
return res.status(200).end();
113+
}
114+
115+
if (req.method !== 'POST') {
116+
return res.status(405).json({ error: 'Method not allowed' });
117+
}
118+
119+
try {
120+
const { accountId, amount, description, metadata } = req.body;
121+
122+
if (!accountId || !amount) {
123+
return res.status(400).json({ error: 'accountId and amount are required' });
124+
}
125+
126+
console.log('💸 Creating transfer:', { accountId, amount });
127+
128+
const transfer = await stripe.transfers.create({
129+
amount: Math.round(amount), // Amount in cents
130+
currency: 'eur',
131+
destination: accountId,
132+
description: description || 'Commission payment',
133+
metadata: metadata || {},
134+
});
135+
136+
console.log('✅ Transfer created:', transfer.id);
137+
138+
return res.json({
139+
transferId: transfer.id,
140+
amount: transfer.amount,
141+
currency: transfer.currency,
142+
destination: transfer.destination,
143+
created: transfer.created,
144+
});
145+
146+
} catch (error) {
147+
console.error('❌ Error creating transfer:', error);
148+
return res.status(500).json({
149+
error: 'Failed to create transfer',
150+
details: error instanceof Error ? error.message : 'Unknown error'
151+
});
152+
}
153+
});

functions/src/stripeInvoice.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import * as functions from 'firebase-functions';
2+
import { stripe } from './stripeConfig';
3+
4+
export const stripeCreateInvoice = functions.https.onRequest(async (req, res) => {
5+
res.set('Access-Control-Allow-Origin', '*');
6+
res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
7+
res.set('Access-Control-Allow-Headers', 'Content-Type');
8+
9+
if (req.method === 'OPTIONS') {
10+
return res.status(200).end();
11+
}
12+
13+
if (req.method !== 'POST') {
14+
return res.status(405).json({ error: 'Method not allowed' });
15+
}
16+
17+
try {
18+
const {
19+
userEmail,
20+
amount,
21+
description,
22+
metadata,
23+
dueDate
24+
} = req.body;
25+
26+
if (!userEmail || !amount) {
27+
return res.status(400).json({ error: 'userEmail and amount are required' });
28+
}
29+
30+
console.log('🧾 Creating invoice for:', userEmail, 'Amount:', amount);
31+
32+
// Chercher ou créer le customer
33+
let customerId;
34+
const customers = await stripe.customers.list({
35+
email: userEmail,
36+
limit: 1
37+
});
38+
39+
if (customers.data.length > 0) {
40+
customerId = customers.data[0].id;
41+
} else {
42+
const customer = await stripe.customers.create({
43+
email: userEmail,
44+
});
45+
customerId = customer.id;
46+
}
47+
48+
// Créer l'invoice item
49+
await stripe.invoiceItems.create({
50+
customer: customerId,
51+
amount: Math.round(amount * 100), // Convert to cents
52+
currency: 'eur',
53+
description: description || 'RefSpring Service',
54+
});
55+
56+
// Créer la facture
57+
const invoice = await stripe.invoices.create({
58+
customer: customerId,
59+
collection_method: 'send_invoice',
60+
days_until_due: dueDate ? Math.ceil((new Date(dueDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : 30,
61+
metadata: metadata || {},
62+
});
63+
64+
// Finaliser la facture
65+
const finalizedInvoice = await stripe.invoices.finalizeInvoice(invoice.id);
66+
67+
console.log('✅ Invoice created and finalized:', finalizedInvoice.id);
68+
69+
return res.json({
70+
invoiceId: finalizedInvoice.id,
71+
invoiceUrl: finalizedInvoice.hosted_invoice_url,
72+
invoicePdf: finalizedInvoice.invoice_pdf,
73+
status: finalizedInvoice.status,
74+
amount: finalizedInvoice.amount_due,
75+
currency: finalizedInvoice.currency,
76+
});
77+
78+
} catch (error) {
79+
console.error('❌ Error creating invoice:', error);
80+
return res.status(500).json({
81+
error: 'Failed to create invoice',
82+
details: error instanceof Error ? error.message : 'Unknown error'
83+
});
84+
}
85+
});

src/components/StripeConnectButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const StripeConnectButton = ({ affiliate, onAccountCreated }: StripeConne
5757

5858
// Étape 3: Rediriger vers Stripe
5959
console.log('✅ Redirecting to Stripe onboarding...');
60-
window.open(linkData.onboardingUrl, '_blank');
60+
window.open(linkData.url, '_blank');
6161

6262
toast({
6363
title: "Configuration Stripe",

src/hooks/useCampaignFormSubmission.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,13 @@ export const useCampaignFormSubmission = (
8484
// Attendre pour s'assurer de la synchronisation
8585
await new Promise(resolve => setTimeout(resolve, 500));
8686

87-
// Récupérer les données fraîches
88-
const freshCardsResponse = await fetch('/api/stripe?action=get-payment-methods', {
89-
method: 'POST',
90-
headers: {
91-
'Content-Type': 'application/json',
92-
},
93-
body: JSON.stringify({ userEmail: user?.email }),
94-
});
87+
// Récupérer les données fraîches via Firebase
88+
const { functions } = await import('@/lib/firebase');
89+
const { httpsCallable } = await import('firebase/functions');
90+
const getPaymentMethods = httpsCallable(functions, 'stripeGetPaymentMethods');
91+
const freshCardsResponse = await getPaymentMethods({ userEmail: user?.email });
92+
const freshCardsData = freshCardsResponse.data as { paymentMethods?: any[] };
9593

96-
const freshCardsData = await freshCardsResponse.json();
9794
const availableCards = freshCardsData.paymentMethods || [];
9895

9996
console.log('💳 Cartes disponibles:', availableCards.length);

0 commit comments

Comments
 (0)