1+ import { serve } from "https://deno.land/std@0.190.0/http/server.ts" ;
2+ import Stripe from "https://esm.sh/stripe@14.21.0" ;
3+ import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.0" ;
4+
5+ const corsHeaders = {
6+ "Access-Control-Allow-Origin" : "*" ,
7+ "Access-Control-Allow-Headers" : "authorization, x-client-info, apikey, content-type" ,
8+ } ;
9+
10+ serve ( async ( req ) => {
11+ // Handle CORS preflight requests
12+ if ( req . method === "OPTIONS" ) {
13+ return new Response ( null , { headers : corsHeaders } ) ;
14+ }
15+
16+ try {
17+ console . log ( '💳 STRIPE PAYMENT METHODS V2 - Début traitement [FRESH-DEPLOYMENT-2025-01-11]' ) ;
18+
19+ // DIAGNOSTIC COMPLET DES VARIABLES D'ENVIRONNEMENT
20+ console . log ( '🔍 DIAGNOSTIC V2 - Variables d\'environnement disponibles:' ) ;
21+ console . log ( ' - SUPABASE_URL:' , ! ! Deno . env . get ( "SUPABASE_URL" ) ) ;
22+ console . log ( ' - SUPABASE_ANON_KEY:' , ! ! Deno . env . get ( "SUPABASE_ANON_KEY" ) ) ;
23+ console . log ( ' - SUPABASE_SERVICE_ROLE_KEY:' , ! ! Deno . env . get ( "SUPABASE_SERVICE_ROLE_KEY" ) ) ;
24+ console . log ( ' - STRIPE_SECRET_KEY:' , ! ! Deno . env . get ( "STRIPE_SECRET_KEY" ) ) ;
25+ console . log ( ' - STRIPE_WEBHOOK_SECRET:' , ! ! Deno . env . get ( "STRIPE_WEBHOOK_SECRET" ) ) ;
26+ console . log ( ' - SHOPIFY_API_KEY:' , ! ! Deno . env . get ( "SHOPIFY_API_KEY" ) ) ;
27+ console . log ( ' - SHOPIFY_API_SECRET:' , ! ! Deno . env . get ( "SHOPIFY_API_SECRET" ) ) ;
28+
29+ // Liste TOUTES les variables d'environnement disponibles (masquées)
30+ const allEnvVars = Object . keys ( Deno . env . toObject ( ) ) ;
31+ console . log ( '🔍 DIAGNOSTIC V2 - Toutes les variables disponibles:' , allEnvVars . length , 'variables' ) ;
32+ console . log ( '🔍 DIAGNOSTIC V2 - Variables contenant "STRIPE":' , allEnvVars . filter ( key => key . includes ( 'STRIPE' ) ) ) ;
33+ console . log ( '🔍 DIAGNOSTIC V2 - Variables contenant "SECRET":' , allEnvVars . filter ( key => key . includes ( 'SECRET' ) ) . map ( key => `${ key } =[MASKED]` ) ) ;
34+
35+ // Initialize Supabase client
36+ const supabaseClient = createClient (
37+ Deno . env . get ( "SUPABASE_URL" ) ?? "" ,
38+ Deno . env . get ( "SUPABASE_ANON_KEY" ) ?? ""
39+ ) ;
40+
41+ // Get authenticated user
42+ const authHeader = req . headers . get ( "Authorization" ) ! ;
43+ const token = authHeader . replace ( "Bearer " , "" ) ;
44+ const { data } = await supabaseClient . auth . getUser ( token ) ;
45+ const user = data . user ;
46+
47+ if ( ! user ?. email ) {
48+ console . error ( '❌ AUTH V2 - Utilisateur non authentifié' ) ;
49+ throw new Error ( "User not authenticated" ) ;
50+ }
51+
52+ console . log ( '👤 UTILISATEUR V2 - Email:' , user . email ) ;
53+
54+ // Check if Stripe secret key is available avec diagnostic avancé
55+ const stripeSecretKey = Deno . env . get ( "STRIPE_SECRET_KEY" ) ;
56+ console . log ( '🔑 STRIPE SECRET KEY V2 - Disponible:' , ! ! stripeSecretKey ) ;
57+ console . log ( '🔑 STRIPE SECRET KEY V2 - Longueur:' , stripeSecretKey ?. length || 0 ) ;
58+ console . log ( '🔑 STRIPE SECRET KEY V2 - Commence par sk_:' , stripeSecretKey ?. startsWith ( 'sk_' ) || false ) ;
59+
60+ if ( ! stripeSecretKey ) {
61+ console . error ( '❌ STRIPE SECRET KEY V2 - Variable manquante dans l\'environnement' ) ;
62+ console . error ( '❌ DIAGNOSTIC V2 - Vérifiez que le secret STRIPE_SECRET_KEY existe dans Supabase' ) ;
63+ console . error ( '❌ DIAGNOSTIC V2 - Vérifiez que supabase/config.toml contient [functions.stripe-payment-methods-v2]' ) ;
64+ throw new Error ( "STRIPE_SECRET_KEY not found in environment variables - Check Supabase secrets configuration" ) ;
65+ }
66+
67+ console . log ( '✅ V2 - STRIPE SECRET KEY trouvée, initialisation de Stripe...' ) ;
68+
69+ // Initialize Stripe
70+ const stripe = new Stripe ( stripeSecretKey , {
71+ apiVersion : "2023-10-16" ,
72+ } ) ;
73+
74+ console . log ( '✅ V2 - Stripe initialisé avec succès' ) ;
75+
76+ // Handle different HTTP methods
77+ if ( req . method === "GET" ) {
78+ return await handleGetPaymentMethods ( stripe , user . email ) ;
79+ } else if ( req . method === "POST" ) {
80+ const body = await req . json ( ) ;
81+
82+ // Si aucune action spécifiée, c'est un GET via POST (pour compatibilité)
83+ if ( ! body || ! body . action || body . action === undefined ) {
84+ console . log ( '📋 V2 - POST sans action - Redirection vers GET' ) ;
85+ return await handleGetPaymentMethods ( stripe , user . email ) ;
86+ }
87+
88+ return await handlePaymentMethodAction ( stripe , body , user . email ) ;
89+ }
90+
91+ return new Response ( JSON . stringify ( { error : "Method not allowed" } ) , {
92+ status : 405 ,
93+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
94+ } ) ;
95+
96+ } catch ( error ) {
97+ console . error ( '❌ STRIPE PAYMENT METHODS V2 - Erreur:' , error ) ;
98+ return new Response ( JSON . stringify ( {
99+ error : error instanceof Error ? error . message : "Internal server error" ,
100+ function_version : "v2" ,
101+ timestamp : new Date ( ) . toISOString ( )
102+ } ) , {
103+ status : 500 ,
104+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
105+ } ) ;
106+ }
107+ } ) ;
108+
109+ async function handleGetPaymentMethods ( stripe : Stripe , userEmail : string ) {
110+ console . log ( '📋 GET PAYMENT METHODS V2 - Récupération pour:' , userEmail ) ;
111+
112+ try {
113+ // Find customer by email
114+ const customers = await stripe . customers . list ( {
115+ email : userEmail ,
116+ limit : 1
117+ } ) ;
118+
119+ if ( customers . data . length === 0 ) {
120+ console . log ( '👤 GET PAYMENT METHODS V2 - Aucun client Stripe trouvé' ) ;
121+ return new Response ( JSON . stringify ( { paymentMethods : [ ] } ) , {
122+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
123+ status : 200 ,
124+ } ) ;
125+ }
126+
127+ const customer = customers . data [ 0 ] ;
128+ console . log ( '👤 GET PAYMENT METHODS V2 - Client trouvé:' , customer . id ) ;
129+
130+ // Get payment methods for customer
131+ const paymentMethods = await stripe . paymentMethods . list ( {
132+ customer : customer . id ,
133+ type : 'card' ,
134+ } ) ;
135+
136+ console . log ( '💳 GET PAYMENT METHODS V2 - Méthodes trouvées:' , paymentMethods . data . length ) ;
137+
138+ // Get default payment method
139+ const defaultPaymentMethodId = typeof customer . invoice_settings ?. default_payment_method === 'string'
140+ ? customer . invoice_settings . default_payment_method
141+ : null ;
142+
143+ // Format payment methods for frontend
144+ const formattedMethods = paymentMethods . data . map ( pm => ( {
145+ id : pm . id ,
146+ type : pm . type ,
147+ card : pm . card ? {
148+ brand : pm . card . brand ,
149+ last4 : pm . card . last4 ,
150+ exp_month : pm . card . exp_month ,
151+ exp_year : pm . card . exp_year ,
152+ } : null ,
153+ isDefault : pm . id === defaultPaymentMethodId ,
154+ created : pm . created ,
155+ } ) ) ;
156+
157+ console . log ( '✅ GET PAYMENT METHODS V2 - Méthodes formatées avec succès' ) ;
158+
159+ return new Response ( JSON . stringify ( {
160+ paymentMethods : formattedMethods ,
161+ customerId : customer . id ,
162+ function_version : "v2"
163+ } ) , {
164+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
165+ status : 200 ,
166+ } ) ;
167+
168+ } catch ( error ) {
169+ console . error ( '❌ GET PAYMENT METHODS V2 - Erreur:' , error ) ;
170+ throw error ;
171+ }
172+ }
173+
174+ async function handlePaymentMethodAction ( stripe : Stripe , body : any , userEmail : string ) {
175+ const { action, customerId, paymentMethodId } = body ;
176+
177+ console . log ( '⚙️ PAYMENT METHOD ACTION V2 - Action:' , action ) ;
178+
179+ if ( action === 'delete' ) {
180+ // Delete/detach payment method
181+ if ( ! paymentMethodId ) {
182+ return new Response ( JSON . stringify ( { error : "Payment method ID required" } ) , {
183+ status : 400 ,
184+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
185+ } ) ;
186+ }
187+
188+ await stripe . paymentMethods . detach ( paymentMethodId ) ;
189+ console . log ( '✅ PAYMENT METHOD ACTION V2 - Méthode supprimée' ) ;
190+
191+ return new Response ( JSON . stringify ( { success : true , function_version : "v2" } ) , {
192+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
193+ status : 200 ,
194+ } ) ;
195+
196+ } else if ( action === 'attach' ) {
197+ // Attach payment method to customer
198+ await stripe . paymentMethods . attach ( paymentMethodId , {
199+ customer : customerId ,
200+ } ) ;
201+
202+ console . log ( '✅ PAYMENT METHOD ACTION V2 - Méthode attachée' ) ;
203+ return new Response ( JSON . stringify ( { success : true , function_version : "v2" } ) , {
204+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
205+ status : 200 ,
206+ } ) ;
207+
208+ } else if ( action === 'set_default' ) {
209+ // Set as default payment method - need to find customer first
210+ if ( ! paymentMethodId ) {
211+ return new Response ( JSON . stringify ( { error : "Payment method ID required" } ) , {
212+ status : 400 ,
213+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
214+ } ) ;
215+ }
216+
217+ // Find customer by email
218+ const customers = await stripe . customers . list ( {
219+ email : userEmail ,
220+ limit : 1
221+ } ) ;
222+
223+ if ( customers . data . length === 0 ) {
224+ return new Response ( JSON . stringify ( { error : "Customer not found" } ) , {
225+ status : 404 ,
226+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
227+ } ) ;
228+ }
229+
230+ const customer = customers . data [ 0 ] ;
231+
232+ // Set as default payment method
233+ await stripe . customers . update ( customer . id , {
234+ invoice_settings : {
235+ default_payment_method : paymentMethodId ,
236+ } ,
237+ } ) ;
238+
239+ console . log ( '✅ PAYMENT METHOD ACTION V2 - Méthode définie par défaut' ) ;
240+ return new Response ( JSON . stringify ( { success : true , function_version : "v2" } ) , {
241+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
242+ status : 200 ,
243+ } ) ;
244+ }
245+
246+ return new Response ( JSON . stringify ( { error : "Invalid action" } ) , {
247+ status : 400 ,
248+ headers : { ...corsHeaders , "Content-Type" : "application/json" } ,
249+ } ) ;
250+ }
0 commit comments