|
import stripe |
|
import logging |
|
import json |
|
import os |
|
import requests |
|
from fastapi import APIRouter, HTTPException, Request |
|
from pydantic import BaseModel |
|
|
|
router = APIRouter() |
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
stripe.api_key = os.getenv("STRIPE_KEY") |
|
stripe.api_version = "2023-10-16" |
|
|
|
|
|
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co" |
|
SUPABASE_KEY = os.getenv("SUPA_KEY") |
|
|
|
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): |
|
user_id: str |
|
stylist_id: str |
|
|
|
|
|
class SubscriptionRequest(BaseModel): |
|
id: str |
|
user_id: str |
|
|
|
@router.post("/create_price") |
|
def create_price(request: Request): |
|
try: |
|
data = await request.json() |
|
amount = data.get("amount") |
|
user_id = data.get("user_id") |
|
|
|
if not amount or not user_id: |
|
raise HTTPException(status_code=400, detail="Amount and user_id are required") |
|
|
|
|
|
price = stripe.Price.create( |
|
unit_amount=int(amount * 100), |
|
currency="brl", |
|
recurring={"interval": "month"}, |
|
product_data={ |
|
"name": "Custom Subscription Price", |
|
"description": "Custom price for user subscription" |
|
} |
|
) |
|
|
|
|
|
response = requests.patch( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}", |
|
headers=SUPABASE_HEADERS, |
|
json={"price_id": price.id} |
|
) |
|
|
|
if response.status_code not in [200, 204]: |
|
raise HTTPException(status_code=500, detail="Failed to update Supabase with price_id") |
|
|
|
return {"message": "Price created successfully!", "price_id": price.id} |
|
|
|
except Exception as e: |
|
logger.error(f"Error creating price: {e}") |
|
raise HTTPException(status_code=500, detail="Error creating price.") |
|
|
|
@router.post("/create_checkout_session") |
|
def create_checkout_session(data: SubscriptionRequest): |
|
try: |
|
|
|
response = requests.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{data.id}", |
|
headers=SUPABASE_HEADERS |
|
) |
|
|
|
stylist_data = response.json() |
|
if not stylist_data: |
|
raise HTTPException(status_code=404, detail="Stylist not found") |
|
|
|
stylist = stylist_data[0] |
|
stylist_name = stylist["name"] |
|
stylist_avatar = stylist["avatar"] |
|
consultations = stylist["consultations"] |
|
stylist_stripe_id = stylist["stripe_id"] |
|
|
|
if not consultations or not stylist_stripe_id: |
|
raise HTTPException(status_code=400, detail="Stylist profile is incomplete") |
|
|
|
|
|
response_user = requests.get( |
|
f"{SUPABASE_URL}/rest/v1/User?id=eq.{data.user_id}", |
|
headers=SUPABASE_HEADERS |
|
) |
|
|
|
user_data = response_user.json() |
|
if not user_data: |
|
raise HTTPException(status_code=404, detail="User not found") |
|
|
|
user = user_data[0] |
|
user_stripe_id = user.get("stripe_id") |
|
price_id = user.get("price_id") |
|
|
|
if not user_stripe_id: |
|
raise HTTPException(status_code=400, detail="User does not have a Stripe ID") |
|
if not price_id: |
|
raise HTTPException(status_code=400, detail="User does not have a valid price ID") |
|
|
|
|
|
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 |
|
} |
|
], |
|
metadata={ |
|
"stylist_id": stylist_stripe_id, |
|
"user_id": data.user_id, |
|
"consultations_per_month": consultations |
|
} |
|
) |
|
|
|
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.") |
|
|
|
|
|
@router.post("/webhook") |
|
async def stripe_webhook(request: Request): |
|
payload = await request.body() |
|
headers = dict(request.headers) |
|
event = None |
|
|
|
try: |
|
|
|
event = stripe.Event.construct_from(json.loads(payload), stripe.api_key) |
|
except Exception as e: |
|
logger.error(f"⚠️ Error processing webhook: {e}") |
|
raise HTTPException(status_code=400, detail="Webhook error") |
|
|
|
logger.info(f"🔹 Event received: {event['type']}") |
|
|
|
|
|
if event["type"] == "checkout.session.completed": |
|
session = event["data"]["object"] |
|
payment_intent_id = session.get("payment_intent") |
|
|
|
try: |
|
|
|
payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id) |
|
except Exception as e: |
|
logger.error(f"⚠️ Error retrieving PaymentIntent: {e}") |
|
raise HTTPException(status_code=400, detail="Error retrieving PaymentIntent") |
|
|
|
|
|
user_id = session.metadata.get("user_id") |
|
stylist_id = session.metadata.get("stylist_id") |
|
consultations_per_month = session.metadata.get("consultations_per_month") |
|
|
|
|
|
amount = payment_intent["amount_received"] |
|
|
|
|
|
stylist_share = int(amount * 0.8) |
|
platform_share = amount - stylist_share |
|
|
|
|
|
logger.info(f"💸 Payment of R${amount / 100:.2f} BRL received for stylist {stylist_id}.") |
|
logger.info(f"💸 Stylist share: R${stylist_share / 100:.2f} BRL, Platform share: R${platform_share / 100:.2f} BRL.") |
|
|
|
|
|
|
|
|
|
|
|
return { |
|
"status": "Payment processed successfully!", |
|
"user_id": user_id, |
|
"stylist_id": stylist_id, |
|
"consultations_per_month": consultations_per_month, |
|
"total_paid": amount / 100, |
|
"stylist_share": stylist_share / 100, |
|
"platform_share": platform_share / 100 |
|
} |
|
|
|
return {"status": "Event received, no action needed."} |
|
|
|
|
|
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") |
|
def check_subscription(data: CheckSubscriptionRequest): |
|
try: |
|
|
|
subscriptions = stripe.Subscription.list( |
|
customer=data.user_id, |
|
status="active", |
|
expand=["data.items"] |
|
) |
|
|
|
|
|
for subscription in subscriptions["data"]: |
|
|
|
if subscription.metadata.get("stylist_id") == data.stylist_id: |
|
return { |
|
"status": "active", |
|
"subscription_id": subscription.id, |
|
"consultations_per_month": subscription.metadata.get("consultations_per_month") |
|
} |
|
|
|
|
|
return { |
|
"status": "inactive", |
|
"message": "No active subscription found for this stylist." |
|
} |
|
|
|
except stripe.error.StripeError as e: |
|
|
|
raise HTTPException(status_code=500, detail=f"Stripe error: {str(e)}") |
|
except Exception as e: |
|
|
|
logger.error(f"Error checking subscription: {e}") |
|
raise HTTPException(status_code=500, detail="Error checking subscription.") |