|
import os |
|
import stripe |
|
import requests |
|
import logging |
|
from fastapi import APIRouter, HTTPException, Header |
|
from fastapi import Query |
|
from pydantic import BaseModel |
|
|
|
router = APIRouter() |
|
|
|
|
|
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" |
|
} |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
class AccountRequest(BaseModel): |
|
email: str |
|
name: str |
|
|
|
class CreateCustomerRequest(BaseModel): |
|
email: str |
|
phone: str |
|
name: str |
|
|
|
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") |
|
|
|
import os |
|
import stripe |
|
import requests |
|
import logging |
|
from fastapi import APIRouter, HTTPException, Header |
|
from pydantic import BaseModel |
|
|
|
router = APIRouter() |
|
|
|
|
|
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" |
|
} |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
class CreateCustomerRequest(BaseModel): |
|
email: str |
|
phone: str |
|
name: str |
|
|
|
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.post("/create_customer") |
|
def create_customer( |
|
data: CreateCustomerRequest, |
|
user_token: str = Header(None, alias="User-key") |
|
): |
|
try: |
|
if not user_token: |
|
raise HTTPException(status_code=401, detail="Missing User-key header") |
|
|
|
|
|
user_id = verify_token(user_token) |
|
logger.info(f"🔹 User verified. user_id: {user_id}") |
|
|
|
|
|
user_data_url = f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}" |
|
response = requests.get(user_data_url, headers={ |
|
"Authorization": f"Bearer {user_token}", |
|
"apikey": SUPABASE_KEY, |
|
"Content-Type": "application/json" |
|
}) |
|
|
|
if response.status_code == 200 and response.json(): |
|
user_data = response.json()[0] |
|
stripe_id = user_data.get("stripe_id") |
|
if stripe_id: |
|
logger.info(f"✅ User already has a Stripe customer: {stripe_id}") |
|
return {"customer_id": stripe_id} |
|
|
|
|
|
logger.info(f"🔹 Checking if email {data.email} already exists in Stripe...") |
|
existing_customers = stripe.Customer.list(email=data.email, limit=1) |
|
if existing_customers.data: |
|
error_message = f"Customer with email {data.email} already exists." |
|
logger.warning(f"⚠️ {error_message}") |
|
raise HTTPException(status_code=400, detail=error_message) |
|
|
|
|
|
customer = stripe.Customer.create( |
|
email=data.email, |
|
phone=data.phone, |
|
name=data.name |
|
) |
|
stripe_id = customer.id |
|
logger.info(f"✅ New Stripe customer created: {stripe_id}") |
|
|
|
|
|
update_data = {"stripe_id": stripe_id} |
|
supabase_url = f"{SUPABASE_URL}/rest/v1/User" |
|
|
|
update_headers = { |
|
"Authorization": f"Bearer {user_token}", |
|
"apikey": SUPABASE_KEY, |
|
"Content-Type": "application/json" |
|
} |
|
|
|
|
|
response = requests.patch( |
|
f"{supabase_url}?id=eq.{user_id}", |
|
headers=update_headers, |
|
json=update_data |
|
) |
|
|
|
logger.info(f"🔹 Supabase PATCH response: {response.status_code} - {response.text}") |
|
|
|
if response.status_code not in [200, 204]: |
|
logger.warning(f"⚠️ Rolling back: Deleting Stripe customer {stripe_id} due to Supabase failure.") |
|
stripe.Customer.delete(stripe_id) |
|
error_message = f"Error updating Supabase: {response.text}" |
|
raise HTTPException(status_code=500, detail=error_message) |
|
|
|
logger.info(f"✅ Successfully updated user {user_id} with stripe_id {stripe_id}") |
|
return {"customer_id": stripe_id} |
|
|
|
except HTTPException as http_err: |
|
raise http_err |
|
|
|
except Exception as e: |
|
error_message = str(e) if str(e) else "An unknown error occurred. Please try again." |
|
logger.error(f"❌ Error creating customer: {error_message}") |
|
raise HTTPException(status_code=500, detail=error_message) |
|
|
|
@router.post("/create_account") |
|
def create_account(account_data: AccountRequest): |
|
try: |
|
account = stripe.Account.create( |
|
type="express", |
|
email=account_data.email, |
|
country="BR", |
|
business_type="individual", |
|
business_profile={ |
|
"mcc": "7298", |
|
"name": account_data.name, |
|
"product_description": "We connect stylists with clients through subscription services.", |
|
"support_email": "[email protected]" |
|
}, |
|
default_currency="brl", |
|
capabilities={ |
|
"transfers": {"requested": True}, |
|
"card_payments": {"requested": True} |
|
} |
|
|
|
|
|
) |
|
return {"account_id": account.id} |
|
except Exception as e: |
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
@router.get("/check_onboarding/{account_id}") |
|
def check_onboarding(account_id: str): |
|
try: |
|
account = stripe.Account.retrieve(account_id) |
|
|
|
if account.requirements.currently_due: |
|
return {"status": "incomplete", "pending_requirements": account.requirements.currently_due} |
|
|
|
return {"status": "complete"} |
|
|
|
except Exception as e: |
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
class AccountLinkRequest(BaseModel): |
|
account_id: str |
|
|
|
@router.get("/terms") |
|
def get_terms(type: str = Query(..., regex="^(use|privacy)$")): |
|
try: |
|
term_type = "Terms of Use" if type == "use" else "Privacy Policy" |
|
|
|
url = f"{SUPABASE_URL}/rest/v1/Configuration?type=eq.{term_type}&select=text,date" |
|
response = requests.get(url, headers=SUPABASE_HEADERS) |
|
|
|
if response.status_code != 200: |
|
raise HTTPException(status_code=500, detail="Erro ao acessar a tabela Configuration") |
|
|
|
data = response.json() |
|
if not data: |
|
raise HTTPException(status_code=404, detail="Termo não encontrado") |
|
|
|
return { |
|
"type": term_type, |
|
"text": data[0]["text"], |
|
"last_updated": data[0]["date"] |
|
} |
|
|
|
except Exception as e: |
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
@router.post("/account_link") |
|
def create_account_link(link_data: AccountLinkRequest): |
|
try: |
|
account_link = stripe.AccountLink.create( |
|
account=link_data.account_id, |
|
return_url="https://ameddes.com/onboarding?success=true", |
|
refresh_url="https://ameddes.com/onboarding?success=false", |
|
type="account_onboarding", |
|
) |
|
return {"url": account_link.url} |
|
except Exception as e: |
|
raise HTTPException(status_code=500, detail=str(e)) |