File size: 28,182 Bytes
912124c
0a2b45f
01ccbf3
5e6bfbb
 
01ccbf3
 
c919ade
370df27
5252942
01ccbf3
912124c
 
 
0a2b45f
 
fb71f9d
9686665
 
912124c
 
9686665
703a507
9686665
 
 
 
 
01ccbf3
 
 
 
 
912124c
8c90efe
 
dbbfb58
8c90efe
01ccbf3
3ac3303
5d7e429
732810d
 
e9983ad
 
50a3d09
 
 
b598016
50a3d09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732810d
940799f
 
 
28c1611
940799f
 
28c1611
940799f
 
 
 
 
 
 
 
03be51c
940799f
03be51c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940799f
 
 
 
da5b64a
 
 
 
 
59aeb79
da5b64a
 
 
deff01c
bbd5a8a
fd41c6e
68dc6ca
bbd5a8a
 
68dc6ca
 
 
 
bbd5a8a
68dc6ca
 
a0c68f3
 
68dc6ca
bbd5a8a
 
 
 
deff01c
bbd5a8a
8bd2328
 
bbd5a8a
 
c38a888
f3c072b
8825b34
 
 
8bd2328
a0c68f3
 
8825b34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbd5a8a
 
 
 
 
 
deff01c
bbd5a8a
da5b64a
8bd2328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2375d08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
da5b64a
 
 
 
427eb72
680dbcb
 
ad67519
680dbcb
427eb72
680dbcb
 
 
b598016
50a3d09
6e544eb
90f8b9c
680dbcb
e9983ad
 
 
8093d40
 
 
 
 
427eb72
8093d40
 
 
 
 
 
 
 
94ce89a
50a3d09
6e544eb
 
 
 
 
 
 
 
 
2ccdcce
6e544eb
2ccdcce
6e544eb
2ccdcce
 
5644e04
b014290
 
94ce89a
 
b014290
94ce89a
13b2342
 
94ce89a
b014290
 
13b2342
 
 
 
 
 
 
 
 
 
 
 
 
 
261640c
 
50a3d09
13b2342
5644e04
261640c
 
 
 
 
ad67519
13b2342
e9983ad
 
 
 
 
ceb5d72
d97c844
 
 
 
 
 
 
 
 
ceb5d72
 
6e544eb
5aab8a4
ad67519
 
5aab8a4
e9983ad
 
427eb72
 
261640c
6e544eb
7c86d6c
ed0d9dd
7d06ed4
 
 
 
912124c
7d06ed4
 
 
ebb4d38
50a3d09
90f8b9c
36ac915
ebb4d38
ef840ba
01ccbf3
f70e965
ebb4d38
01ccbf3
 
 
427eb72
36ac915
 
 
59615d0
08cdb44
 
01ccbf3
36ac915
c9000d6
7d06ed4
d80190a
 
c9000d6
d80190a
ebb4d38
d80190a
c9000d6
36ac915
a6bb840
36ac915
ebb4d38
968f9d5
9aebe5f
 
 
 
b3116aa
912124c
24c2864
 
912124c
7ef17d7
9aebe5f
fd41c6e
 
 
 
 
 
b3116aa
 
fd41c6e
b3116aa
 
1735268
36ac915
 
 
a0c68f3
 
1735268
b3116aa
 
 
 
 
a0c68f3
 
ab80e0b
36ac915
b3116aa
968f9d5
b3116aa
36ac915
01ccbf3
912124c
b598016
 
d80190a
0ea8eb8
a664e20
 
 
912124c
 
 
 
 
ed0d9dd
912124c
 
 
9b46162
 
 
c919ade
6873666
3cbd47c
6873666
9b46162
6873666
 
 
3cbd47c
6873666
 
f2aed3f
c919ade
f2aed3f
c919ade
f2aed3f
 
 
 
 
 
 
 
 
602fe8a
c919ade
 
3cbd47c
 
9b46162
 
d8e0746
 
c919ade
fb19d38
 
 
 
c919ade
fb19d38
c919ade
fb19d38
 
 
c919ade
 
 
fb19d38
 
 
 
48e31fe
39e10b6
 
9bfe44f
 
39e10b6
3cbd47c
 
 
 
 
 
 
 
6873666
3cbd47c
 
 
 
 
 
 
 
6873666
602fe8a
6873666
3cbd47c
 
 
 
 
 
 
6873666
 
c919ade
3cbd47c
c919ade
 
 
6873666
 
602fe8a
9b46162
3cbd47c
 
 
 
 
 
9b46162
 
fb19d38
39e10b6
fb19d38
 
 
c919ade
fb19d38
c919ade
39e10b6
 
 
c919ade
 
 
 
 
 
 
9b46162
 
9bfe44f
9b46162
 
9bfe44f
c919ade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
import stripe
import logging
import json
from datetime import datetime
import pytz
import os
import requests
import asyncio
import jwt
from fastapi import APIRouter, HTTPException, Request, Header
from pydantic import BaseModel

router = APIRouter()

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

# 🔥 Pegando as chaves do ambiente
stripe.api_key = os.getenv("STRIPE_KEY")  # Lendo do ambiente
stripe.api_version = "2023-10-16"

# 🔥 Supabase Configuração com Secrets
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co"
SUPABASE_KEY = os.getenv("SUPA_KEY")  # Lendo do ambiente

if not stripe.api_key or not SUPABASE_KEY:
    raise ValueError("❌ STRIPE_KEY ou SUPA_KEY não foram definidos no ambiente!")

SUPABASE_HEADERS = {
    "apikey": SUPABASE_KEY,
    "Authorization": f"Bearer {SUPABASE_KEY}",
    "Content-Type": "application/json"
}

class CheckSubscriptionRequest(BaseModel):
    stylist_id: str
    user_token: str = Header(None, alias="User-key")  
    
class SubscriptionRequest(BaseModel):
    id: str  # ID do estilista

class CreatePriceRequest(BaseModel):
    amount: int  # Valor em centavos (ex: 2500 para R$25,00)
    emergency_price: int  # Valor de emergência (ex: 500 para R$5,00)
    consultations: int  # Número de consultas (ex: 3)

def verify_token(user_token: str) -> str:
    """
    Valida o token JWT no Supabase e retorna o user_id se for válido.
    """
    headers = {
        "Authorization": f"Bearer {user_token}",
        "apikey": SUPABASE_KEY,
        "Content-Type": "application/json"
    }

    response = requests.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers)

    if response.status_code == 200:
        user_data = response.json()
        user_id = user_data.get("id")
        if not user_id:
            raise HTTPException(status_code=400, detail="Invalid token: User ID not found")
        return user_id
    else:
        raise HTTPException(status_code=401, detail="Invalid or expired token")

@router.get("/subscription/status/{subscription_id}")
async def check_subscription_status(subscription_id: str):
    try:
        # 🔹 Consulta a Stripe diretamente
        stripe_url = f"https://api.stripe.com/v1/subscriptions/{subscription_id}"
        headers = {
            "Authorization": f"Bearer {stripe.api_key}"  # ✅ Usa a chave definida no ambiente
        }
        response = requests.get(stripe_url, headers=headers)

        if response.status_code != 200:
            raise HTTPException(status_code=404, detail="Subscription not found in Stripe")

        subscription = response.json()

        # 🔹 Pega os dados necessários
        status = subscription.get("status")
        cancel_at_period_end = subscription.get("cancel_at_period_end", False)
        canceled_at = subscription.get("canceled_at")
        current_period_end = subscription.get("current_period_end")

        # 🔹 Converte timestamps para data legível
        ny_tz = pytz.timezone("America/New_York")

        canceled_at_date = (
            datetime.utcfromtimestamp(canceled_at).replace(tzinfo=pytz.utc).astimezone(ny_tz).isoformat()
            if canceled_at else None
        )

        expiration_date = (
            datetime.utcfromtimestamp(current_period_end).replace(tzinfo=pytz.utc).astimezone(ny_tz).isoformat()
            if current_period_end else None
        )

        return {
            "subscription_id": subscription_id,
            "status": status,
            "cancel_at_period_end": cancel_at_period_end,
            "canceled_at": canceled_at_date,
            "expiration_date": expiration_date
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
        
@router.post("/webhook")
async def stripe_webhook(request: Request):
    try:
        payload = await request.json()
        event_type = payload.get("type")

        if event_type == "invoice.payment_succeeded":
            invoice = payload.get("data", {}).get("object", {})

            # 🔹 Capturar valores da fatura
            amount_paid = invoice.get("amount_paid", 0)  # Valor total pago (em centavos)
            currency = invoice.get("currency", "usd")  # Moeda do pagamento

            # 🔹 Buscando metadados
            metadata = invoice.get("metadata", {})
            subscription_metadata = invoice.get("subscription_details", {}).get("metadata", {})
            line_items = invoice.get("lines", {}).get("data", [])
            line_item_metadata = line_items[0].get("metadata", {}) if line_items else {}

            # 🔹 Pegando os valores corretos
            stylist_id = metadata.get("stylist_id") or subscription_metadata.get("stylist_id") or line_item_metadata.get("stylist_id")
            user_id = metadata.get("user_id") or subscription_metadata.get("user_id") or line_item_metadata.get("user_id")
            price_id = metadata.get("price_id") or subscription_metadata.get("price_id") or line_item_metadata.get("price_id")
            subscription_id = invoice.get("subscription")  # Capturando o ID da assinatura

            # 🔹 Calculando a divisão do pagamento
            stylist_amount = int(amount_paid * 0.8)  # 80% para o estilista
            platform_amount = int(amount_paid * 0.2)  # 20% para a plataforma

            # 🔹 Logando as informações detalhadas
            logger.info(f"✅ Pagamento bem-sucedido! Valor total: R$ {amount_paid / 100:.2f}")
            logger.info(f"👤 Stylist ID: {stylist_id}")
            logger.info(f"👥 User ID: {user_id}")
            logger.info(f"💰 Estilista recebe: R$ {stylist_amount / 100:.2f}")
            logger.info(f"🏛️ Plataforma fica com: R$ {platform_amount / 100:.2f}")

            # 🔹 Inserir dados na tabela Subscriptions no Supabase
            subscription_data = {
                "stylist_id": stylist_id,
                "customer_id": user_id,
                "active": True,  # Assinatura ativa após pagamento
                "sub_id": subscription_id,  # ID da assinatura
                "price_id": price_id  # ID do preço
            }

            supabase_headers = {
                "Authorization": f"Bearer {SUPABASE_KEY}",
                "apikey": SUPABASE_KEY,
                "Content-Type": "application/json"
            }

            subscription_url = f"{SUPABASE_URL}/rest/v1/Subscriptions"
            response_subscription = requests.post(
                subscription_url,
                headers=supabase_headers,
                json=subscription_data
            )

            if response_subscription.status_code == 201:
                logger.info(f"✅ Subscription added successfully for user {user_id}")
            else:
                logger.error(f"❌ Failed to add subscription: {response_subscription.status_code} - {response_subscription.text}")

            return {
                "status": "success",
                "total_paid": amount_paid / 100,
                "stylist_id": stylist_id,
                "user_id": user_id,
                "stylist_amount": stylist_amount / 100,
                "platform_amount": platform_amount / 100
            }

        elif event_type == "customer.subscription.deleted":
            subscription = payload.get("data", {}).get("object", {})
            subscription_id = subscription.get("id")

            if not subscription_id:
                logger.error("❌ Subscription ID not found in event payload.")
                return {"status": "error", "message": "Subscription ID missing."}

            logger.info(f"🔹 Subscription {subscription_id} canceled. Updating database...")

            # 🔹 Atualizar active = False no Supabase
            update_url = f"{SUPABASE_URL}/rest/v1/Subscriptions?sub_id=eq.{subscription_id}"
            update_data = {"active": False}

            supabase_headers = {
                "Authorization": f"Bearer {SUPABASE_KEY}",
                "apikey": SUPABASE_KEY,
                "Content-Type": "application/json"
            }

            response_update = requests.patch(update_url, headers=supabase_headers, json=update_data)

            if response_update.status_code in [200, 204]:
                logger.info(f"✅ Subscription {subscription_id} marked as inactive in Supabase.")
                return {"status": "success", "message": "Subscription canceled and updated."}
            else:
                logger.error(f"❌ Failed to update subscription: {response_update.status_code} - {response_update.text}")
                return {"status": "error", "message": "Failed to update subscription."}

        if event_type == "customer.subscription.updated":
            subscription = payload.get("data", {}).get("object", {})
            subscription_id = subscription.get("id")
            canceled_at = subscription.get("canceled_at")  # Timestamp do cancelamento
            cancel_status = subscription.get("cancel_at_period_end", False)

            if canceled_at:
                # Convertendo timestamp para data e horário de Nova York
                ny_tz = pytz.timezone("America/New_York")
                canceled_date = datetime.fromtimestamp(canceled_at, ny_tz).isoformat()

                logger.info(f"🔹 Subscription {subscription_id} canceled at {canceled_date} (New York Time).")

                # 🔹 Atualizando Supabase
                update_url = f"{SUPABASE_URL}/rest/v1/Subscriptions?sub_id=eq.{subscription_id}"
                update_data = {
                    "canceled": True,
                    "canceled_date": canceled_date
                }

                supabase_headers = {
                    "Authorization": f"Bearer {SUPABASE_KEY}",
                    "apikey": SUPABASE_KEY,
                    "Content-Type": "application/json"
                }

                response_update = requests.patch(update_url, headers=supabase_headers, json=update_data)

                if response_update.status_code in [200, 204]:
                    logger.info(f"✅ Subscription {subscription_id} updated with canceled date.")
                    return {"status": "success", "message": "Subscription updated with cancellation date."}
                else:
                    logger.error(f"❌ Failed to update subscription: {response_update.status_code} - {response_update.text}")
                    return {"status": "error", "message": "Failed to update subscription."}
            else:
                logger.info(f"🔹 Subscription {subscription_id} updated, but not canceled.")
                return {"status": "success", "message": "Subscription updated but not canceled."}

    except Exception as e:
        logger.error(f"❌ Erro no webhook: {str(e)}")
        return {"status": "error", "message": str(e)}
        
@router.post("/create_price")
async def create_price(
    data: CreatePriceRequest,
    user_token: str = Header(None, alias="User-key")
):
    try:
        if not user_token:
            raise HTTPException(status_code=401, detail="Missing User-key header")

        # 🔹 1. Validar o token e obter user_id
        user_id = verify_token(user_token)
        logger.info(f"🔹 User verified. user_id: {user_id}")

        amount = data.amount
        emergency_price = data.emergency_price
        consultations = data.consultations

        # 🔹 2. Verificar se os valores estão dentro dos limites permitidos
        if not (500 <= amount <= 99900):
            raise HTTPException(status_code=400, detail="Amount must be between $5 and $999")
        if not (500 <= emergency_price <= 99900):
            raise HTTPException(status_code=400, detail="Emergency price must be between $5 and $999")

        # 🔹 3. Consultations precisa ser 0 obrigatoriamente se não for definido
        if consultations is None:
            consultations = 0
        elif consultations < 0:
            raise HTTPException(status_code=400, detail="Consultations must be greater than or equal to 0")

        logger.info(f"🔹 Validated amounts: amount = {amount}, emergency_price = {emergency_price}, consultations = {consultations}")

        # 🔹 4. Buscar price_id, name, avatar e bio do usuário no Supabase
        supabase_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}"
        headers = {
            "Authorization": f"Bearer {user_token}",  # 🔹 Incluindo token correto no header
            **SUPABASE_HEADERS
        }
        response = requests.get(supabase_url, headers=headers)
        logger.info(f"🔹 Supabase GET response: {response.status_code} - {response.text}")

        if response.status_code != 200:
            raise HTTPException(status_code=500, detail=f"Failed to fetch user from Supabase: {response.text}")

        user_data = response.json()
        if not user_data:
            raise HTTPException(status_code=404, detail="User not found in Supabase")

        user = user_data[0]
        existing_price_id = user.get("price_id")
        user_name = user.get("name", "Unknown User")  # 🔹 Recuperar o nome do usuário
        user_avatar = user.get("avatar", None)  # 🔹 Recuperar a URL do avatar do usuário, se existir
        user_bio = user.get("bio", "No description provided.")  # 🔹 Recuperar a bio, com padrão se estiver vazia
        logger.info(f"🔹 Existing price_id: {existing_price_id}, user_name: {user_name}, user_avatar: {user_avatar}, user_bio: {user_bio}")

        # 🔹 5. Criar produto no Stripe com nome, imagem e descrição baseados no usuário
        product_data = {
            "name": f"{user_name} - {amount} BRL",  # Nome do produto
            "description": user_bio,  # Descrição do produto, com fallback se vazia
        }
        
        if user_avatar:  # Verificar se o avatar existe e adicionar a imagem
            product_data["images"] = [user_avatar]

        product = stripe.Product.create(**product_data)
        product_id = product.id
        logger.info(f"✅ New product created: {product_id}")

        # 🔹 6. Criar novo preço no Stripe associado ao produto
        price = stripe.Price.create(
            unit_amount=amount,
            currency="brl",
            recurring={"interval": "month"},
            product=product_id  # Associando o preço ao produto
        )
        new_price_id = price.id
        logger.info(f"✅ New price created: {new_price_id}")

        # 🔹 7. Se já houver um price_id, cancelar assinaturas ativas ao final do período
        if existing_price_id:
            subscriptions = stripe.Subscription.list(status="active")
            for sub in subscriptions.auto_paging_iter():
                if sub["items"]["data"][0]["price"]["id"] == existing_price_id:
                    stripe.Subscription.modify(sub.id, cancel_at_period_end=True)
                    logger.info(f"🔹 Subscription {sub.id} set to cancel at period end.")

        # 🔹 8. Atualizar Supabase com o novo price_id e valores adicionais
        update_data = {
            "price_id": new_price_id,  # Novo price_id
            "price": amount,  # Atualizando o valor de 'price'
            "emergency_price": emergency_price,  # Atualizando o valor de 'emergency_price'
            "consultations": consultations  # Atualizando o valor de 'consultations'
        }
        
        # Aqui estamos incluindo o cabeçalho de autenticação com o token JWT correto
        update_headers = {
            "Authorization": f"Bearer {user_token}",  # Incluindo token correto no header
            "apikey": SUPABASE_KEY,
            "Content-Type": "application/json"
        }
        
        update_response = requests.patch(supabase_url, headers=update_headers, json=update_data)
        
        # Log detalhado para verificar a resposta
        logger.info(f"🔹 Supabase PATCH response: {update_response.status_code} - {update_response.text}")
        
        if update_response.status_code not in [200, 204]:
            raise HTTPException(status_code=500, detail=f"Failed to update Supabase: {update_response.text}")
        
        logger.info(f"✅ Successfully updated user {user_id} with new price_id, price, emergency_price, and consultations")
        return {"message": "Price created and user updated successfully!", "price_id": new_price_id}

    except Exception as e:
        logger.error(f"❌ Error creating price: {e}")
        raise HTTPException(status_code=500, detail=f"Error creating price: {str(e)}")
        
@router.post("/create_checkout_session")
def create_checkout_session(
    data: SubscriptionRequest,
    user_token: str = Header(None, alias="User-key")
):
    try:
        if not user_token:
            raise HTTPException(status_code=401, detail="Missing User-key header")

        # 🔹 1. Validar o token do cliente e obter user_id (cliente)
        user_id = verify_token(user_token)

        # 🔹 2. Buscar dados do estilista no Supabase
        response_stylist = requests.get(
            f"{SUPABASE_URL}/rest/v1/User?id=eq.{data.id}",
            headers=SUPABASE_HEADERS
        )
        stylist_data = response_stylist.json()
        if not stylist_data:
            raise HTTPException(status_code=404, detail="Stylist not found")

        stylist = stylist_data[0]
        stylist_id = stylist.get("id")  # ID do estilista no Supabase
        stylist_stripe_id = stylist.get("stripe_id")  # ID do estilista no Stripe
        price_id = stylist.get("price_id")  # ID do preço no Stripe

        if not stylist_stripe_id or not price_id:
            raise HTTPException(status_code=400, detail="Stylist profile is incomplete")

        # 🔹 3. Buscar dados do cliente no Supabase
        response_user = requests.get(
            f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}",
            headers=SUPABASE_HEADERS
        )
        user_data = response_user.json()
        if not user_data:
            raise HTTPException(status_code=404, detail="Client not found")

        user = user_data[0]
        user_stripe_id = user.get("stripe_id")  # ID do cliente no Stripe

        if not user_stripe_id:
            raise HTTPException(status_code=400, detail="Client does not have a Stripe Customer ID")

        # 🔹 Buscar detalhes do preço no Stripe
        price_details = stripe.Price.retrieve(price_id)
        stylist_price = price_details.unit_amount  # Obtém o valor do preço em centavos
        
        # 🔹 Criar Checkout Session no Stripe (Sem `payment_intent_data`)
        session = stripe.checkout.Session.create(
            success_url="https://yourdomain.com/success",
            cancel_url="https://yourdomain.com/cancel",
            payment_method_types=["card"],
            mode="subscription",
            customer=user_stripe_id,
            line_items=[
                {
                    "price": price_id,
                    "quantity": 1
                }
            ],
            subscription_data={  # 🔹 Aplicando divisão de receita via `application_fee_percent`
                "application_fee_percent": 20,  # 20% para a plataforma
                "transfer_data": {
                    "destination": stylist_stripe_id  # Conta do estilista no Stripe
                },
                "metadata": {
                    "stylist_id": stylist_id,
                    "stylist_stripe_id": stylist_stripe_id,
                    "user_id": user_id,
                    "user_stripe_id": user_stripe_id,
                    "price_id": price_id
                }
            },
            metadata={  # 🔹 Adicionando metadados no checkout
                "stylist_id": stylist_id,
                "stylist_stripe_id": stylist_stripe_id,
                "user_id": user_id,
                "user_stripe_id": user_stripe_id,
                "price_id": price_id
            }
        )
        
        logger.info(f"📌 Checkout session created successfully: {session.url}")
        
        return {"message": "Checkout session created successfully!", "checkout_url": session.url}

    except Exception as e:
        logger.error(f"Error creating checkout session: {e}")
        raise HTTPException(status_code=500, detail="Error creating checkout session.")
        
### **CANCELAMENTO DE ASSINATURA**
class CancelSubscriptionRequest(BaseModel):
    subscription_id: str

@router.post("/cancel_subscription")
def cancel_subscription(data: CancelSubscriptionRequest):
    try:
        subscription = stripe.Subscription.modify(
            data.subscription_id,
            cancel_at_period_end=True,
        )
        return {"status": "Subscription will be canceled at period end"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/check_subscription")
async def check_subscription(
    data: CheckSubscriptionRequest,
    user_token: str = Header(None, alias="User-key")
):
    try:
        if not user_token:
            raise HTTPException(status_code=401, detail="Missing User-key header")
        
        # 🔹 Validar o token e obter user_id do cliente
        user_id = verify_token(user_token)

        # 🔹 Buscar o stripe_id do usuário no Supabase
        response_user = await async_request(
            f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}",
            SUPABASE_HEADERS
        )
        user_data = response_user.json()
        if not user_data:
            raise HTTPException(status_code=404, detail="User not found")
        
        user_stripe_id = user_data[0].get("stripe_id")
        if not user_stripe_id:
            raise HTTPException(status_code=404, detail="Stripe customer not found for user")

        # 🔹 Buscar todas as assinaturas do cliente (ativas, canceladas, expiradas, etc.)
        subscriptions = await async_stripe_request(
            stripe.Subscription.list,
            customer=user_stripe_id,
            expand=["data.items"]
        )

        logger.info(f"Stripe response data: {subscriptions}")

        if not subscriptions or not subscriptions["data"]:
            # Se não houver assinaturas para o cliente, desativa as assinaturas associadas ao estilista no banco de dados
            logger.info(f"No active subscription found for user {user_id}. Deactivating all subscriptions for stylist {data.stylist_id}.")

            # 🔹 Buscar todas as assinaturas do estilista no banco de dados para este cliente
            response_subscriptions = await async_request(
                f"{SUPABASE_URL}/rest/v1/Subscriptions?customer_id=eq.{user_id}&stylist_id=eq.{data.stylist_id}",
                SUPABASE_HEADERS
            )
            if response_subscriptions.status_code == 200:
                subscriptions_data = response_subscriptions.json()
                await asyncio.gather(
                    *(update_subscription_status(sub['id'], False) for sub in subscriptions_data)
                )
            else:
                logger.error(f"❌ Failed to fetch subscriptions from Supabase for stylist {data.stylist_id} and user {user_id}.")
            
            return {"status": "inactive", "message": "No active subscription found for stylist."}

        # 🔹 Verifica se existe uma assinatura ativa para o estilista
        stylist_found = False
        for subscription in subscriptions["data"]:
            if subscription["metadata"].get("stylist_id") == data.stylist_id:
                stylist_found = True
                subscription_id = subscription["id"]
                price_id = subscription["items"]["data"][0]["price"]["id"]
                status = subscription.get("status")
                cancel_at_period_end = subscription.get("cancel_at_period_end", False)
                canceled_at = subscription.get("canceled_at")
                current_period_end = subscription.get("current_period_end")

                # 🔹 Converte timestamps para data legível
                nyc_tz = pytz.timezone('America/New_York')
                canceled_at_date = (
                    datetime.utcfromtimestamp(canceled_at).replace(tzinfo=pytz.utc).astimezone(nyc_tz).isoformat()
                    if canceled_at else None
                )
                expiration_date = (
                    datetime.utcfromtimestamp(current_period_end).replace(tzinfo=pytz.utc).astimezone(nyc_tz).isoformat()
                    if current_period_end else None
                )

                # 🔹 Atualiza o banco de dados Supabase com os dados da assinatura
                subscription_data = {
                    "stylist_id": data.stylist_id,
                    "customer_id": user_id,
                    "active": status == "active",
                    "sub_id": subscription_id,
                    "price_id": price_id,
                    "canceled": cancel_at_period_end or (status == "canceled"),
                    "canceled_date": canceled_at_date
                }

                await async_request(
                    f"{SUPABASE_URL}/rest/v1/Subscriptions",
                    SUPABASE_HEADERS,
                    json=subscription_data,
                    method='POST'
                )

                # Retorna informações sobre a assinatura sincronizada
                return {
                    "status": status,
                    "subscription_id": subscription_id,
                    "consultations_per_month": subscription["metadata"].get("consultations_per_month", None),
                    "cancel_at_period_end": cancel_at_period_end,
                    "canceled_at": canceled_at_date,
                    "expiration_date": expiration_date
                }

        # 🔹 Caso não tenha encontrado assinatura ativa para o estilista
        if not stylist_found:
            logger.info(f"No active subscription found for stylist {data.stylist_id}. Deactivating all subscriptions for this stylist.")

            # 🔹 Buscar todas as assinaturas do estilista no banco de dados
            response_subscriptions = await async_request(
                f"{SUPABASE_URL}/rest/v1/Subscriptions?stylist_id=eq.{data.stylist_id}",
                SUPABASE_HEADERS
            )
            if response_subscriptions.status_code == 200:
                subscriptions_data = response_subscriptions.json()
                await asyncio.gather(
                    *(update_subscription_status(sub['id'], False) for sub in subscriptions_data)
                )
            else:
                logger.error(f"❌ Failed to fetch subscriptions from Supabase for stylist {data.stylist_id}.")
            
            return {"status": "inactive", "message": "No active subscription found for stylist."}

    except stripe.error.StripeError as e:
        logger.error(f"Stripe error: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Stripe error: {str(e)}")
    except Exception as e:
        logger.error(f"Error checking subscription: {str(e)}")
        raise HTTPException(status_code=500, detail="Error checking subscription.")


# Função assíncrona para fazer requisições de rede
async def async_request(url, headers, json=None, method='GET'):
    method_func = requests.post if method == 'POST' else requests.get
    response = method_func(url, headers=headers, json=json)
    return response

# Função assíncrona para chamar a Stripe API
async def async_stripe_request(func, **kwargs):
    loop = asyncio.get_event_loop()
    response = await loop.run_in_executor(None, lambda: func(**kwargs))
    return response

# Função assíncrona para atualizar o status de uma assinatura
async def update_subscription_status(subscription_id, status):
    update_data = {
        "active": status
    }
    subscription_url = f"{SUPABASE_URL}/rest/v1/Subscriptions?id=eq.{subscription_id}"
    update_response = await async_request(
        subscription_url,
        SUPABASE_HEADERS,
        json=update_data,
        method='PATCH'
    )
    if update_response.status_code == 200:
        logger.info(f"✅ Subscription {subscription_id} deactivated successfully.")
    else:
        logger.error(f"❌ Failed to deactivate subscription {subscription_id}.")