Update routes/subscription.py
Browse files- routes/subscription.py +40 -32
routes/subscription.py
CHANGED
@@ -58,26 +58,29 @@ def create_checkout_session(data: SubscriptionRequest):
|
|
58 |
success_url="https://yourdomain.com/success",
|
59 |
cancel_url="https://yourdomain.com/cancel",
|
60 |
payment_method_types=["card"],
|
61 |
-
mode="subscription"
|
62 |
line_items=[
|
63 |
{
|
64 |
"price_data": {
|
65 |
"currency": "brl",
|
66 |
"product_data": {
|
67 |
-
"name": f"{stylist_name}'s
|
68 |
-
"description": f"✔ {consultations}
|
69 |
"images": [stylist_avatar],
|
70 |
},
|
71 |
"unit_amount": stylist_price,
|
72 |
-
"recurring": {"interval": "month"}
|
73 |
},
|
74 |
"quantity": 1
|
75 |
}
|
76 |
],
|
77 |
-
|
|
|
|
|
|
|
|
|
78 |
"metadata": {
|
79 |
-
"stylist_id": stylist_stripe_id,
|
80 |
-
"user_id": data.user_id,
|
81 |
"consultations_per_month": consultations
|
82 |
}
|
83 |
}
|
@@ -100,6 +103,7 @@ async def stripe_webhook(request: Request):
|
|
100 |
event = None
|
101 |
|
102 |
try:
|
|
|
103 |
event = stripe.Event.construct_from(json.loads(payload), stripe.api_key)
|
104 |
except Exception as e:
|
105 |
logger.error(f"⚠️ Error processing webhook: {e}")
|
@@ -107,42 +111,46 @@ async def stripe_webhook(request: Request):
|
|
107 |
|
108 |
logger.info(f"🔹 Event received: {event['type']}")
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
-
|
120 |
stylist_share = int(amount * 0.8) # 80% para o estilista
|
121 |
platform_share = amount - stylist_share # 20% para a plataforma
|
122 |
|
123 |
-
#
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
)
|
131 |
-
logger.info(f"💸 Transfer successful: R${stylist_share / 100:.2f} BRL to {stylist_id}")
|
132 |
-
transfer_status = "Completed"
|
133 |
-
except Exception as e:
|
134 |
-
logger.error(f"🚨 Transfer error for stylist {stylist_id}: {e}")
|
135 |
-
transfer_status = "Failed"
|
136 |
|
137 |
return {
|
138 |
"status": "Payment processed successfully!",
|
139 |
-
"user_id": user_id, #
|
140 |
-
"stylist_id": stylist_id,
|
141 |
"consultations_per_month": consultations_per_month,
|
142 |
"total_paid": amount / 100,
|
143 |
"stylist_share": stylist_share / 100,
|
144 |
-
"platform_share": platform_share / 100
|
145 |
-
"transfer_status": transfer_status
|
146 |
}
|
147 |
|
148 |
return {"status": "Event received, no action needed."}
|
|
|
58 |
success_url="https://yourdomain.com/success",
|
59 |
cancel_url="https://yourdomain.com/cancel",
|
60 |
payment_method_types=["card"],
|
61 |
+
mode="payment", # 🔥 Alterado de "subscription" para "payment"
|
62 |
line_items=[
|
63 |
{
|
64 |
"price_data": {
|
65 |
"currency": "brl",
|
66 |
"product_data": {
|
67 |
+
"name": f"{stylist_name}'s Service",
|
68 |
+
"description": f"✔ {consultations} consultations",
|
69 |
"images": [stylist_avatar],
|
70 |
},
|
71 |
"unit_amount": stylist_price,
|
|
|
72 |
},
|
73 |
"quantity": 1
|
74 |
}
|
75 |
],
|
76 |
+
payment_intent_data={ # 🔥 Adicionando transferência automática para o estilista
|
77 |
+
"application_fee_amount": int(stylist_price * 0.2), # 🔹 20% fica para a plataforma
|
78 |
+
"transfer_data": {
|
79 |
+
"destination": stylist_stripe_id # 🔥 Enviando direto para o estilista
|
80 |
+
},
|
81 |
"metadata": {
|
82 |
+
"stylist_id": stylist_stripe_id,
|
83 |
+
"user_id": data.user_id,
|
84 |
"consultations_per_month": consultations
|
85 |
}
|
86 |
}
|
|
|
103 |
event = None
|
104 |
|
105 |
try:
|
106 |
+
# Tenta construir o evento Stripe a partir do payload recebido
|
107 |
event = stripe.Event.construct_from(json.loads(payload), stripe.api_key)
|
108 |
except Exception as e:
|
109 |
logger.error(f"⚠️ Error processing webhook: {e}")
|
|
|
111 |
|
112 |
logger.info(f"🔹 Event received: {event['type']}")
|
113 |
|
114 |
+
# Verifica se o evento é de uma sessão de checkout completada
|
115 |
+
if event["type"] == "checkout.session.completed":
|
116 |
+
session = event["data"]["object"]
|
117 |
+
payment_intent_id = session.get("payment_intent")
|
118 |
|
119 |
+
try:
|
120 |
+
# Recupera o PaymentIntent associado à sessão
|
121 |
+
payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id)
|
122 |
+
except Exception as e:
|
123 |
+
logger.error(f"⚠️ Error retrieving PaymentIntent: {e}")
|
124 |
+
raise HTTPException(status_code=400, detail="Error retrieving PaymentIntent")
|
125 |
+
|
126 |
+
# Extrai os dados do metadata
|
127 |
+
user_id = session.metadata.get("user_id") # ID do cliente
|
128 |
+
stylist_id = session.metadata.get("stylist_id") # ID do estilista no Stripe
|
129 |
+
consultations_per_month = session.metadata.get("consultations_per_month") # Consultas por mês
|
130 |
+
|
131 |
+
# Verifica o valor pago
|
132 |
+
amount = payment_intent["amount_received"]
|
133 |
|
134 |
+
# Define a divisão do pagamento entre a plataforma e o estilista
|
135 |
stylist_share = int(amount * 0.8) # 80% para o estilista
|
136 |
platform_share = amount - stylist_share # 20% para a plataforma
|
137 |
|
138 |
+
# Aqui, o pagamento já foi transferido para o estilista, então não há mais necessidade de fazer uma transferência manual
|
139 |
+
logger.info(f"💸 Payment of R${amount / 100:.2f} BRL received for stylist {stylist_id}.")
|
140 |
+
logger.info(f"💸 Stylist share: R${stylist_share / 100:.2f} BRL, Platform share: R${platform_share / 100:.2f} BRL.")
|
141 |
+
|
142 |
+
# Aqui você pode atualizar seu banco de dados com as informações do pagamento
|
143 |
+
# Exemplo de atualização do banco com o valor e o status do pagamento
|
144 |
+
# update_payment_status(user_id, stylist_id, amount, stylist_share, platform_share)
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
|
146 |
return {
|
147 |
"status": "Payment processed successfully!",
|
148 |
+
"user_id": user_id, # Retorna o ID do cliente
|
149 |
+
"stylist_id": stylist_id, # Retorna o ID do estilista
|
150 |
"consultations_per_month": consultations_per_month,
|
151 |
"total_paid": amount / 100,
|
152 |
"stylist_share": stylist_share / 100,
|
153 |
+
"platform_share": platform_share / 100
|
|
|
154 |
}
|
155 |
|
156 |
return {"status": "Event received, no action needed."}
|