Skip to content

Commit cd790a4

Browse files
committed
- Se añadieron los ID de cliente y cuenta de Stripe al modelo UserProfile.
- Se actualizaron las plantillas de perfil y configuración para mostrar el estado de la cuenta de pago. - Se creó una nueva plantilla de perfil de usuario para visualizar los perfiles individuales. - Se implementaron vistas para configurar cuentas de pago y completar la incorporación de Stripe. - Se añadieron funciones de utilidad para crear cuentas de Stripe y enlaces de incorporación. - Se mejoró el procesamiento de pagos para permitir pagos directos entre usuarios con Stripe Connect. - Se actualizaron las vistas y los formularios de pago para gestionar el nuevo flujo de pagos. - Se añadieron pruebas para la incorporación de Stripe y las funcionalidades de procesamiento de pagos. - Se introdujo la función de informes para perfiles de usuario, viajes y pagos.
1 parent f85ebab commit cd790a4

19 files changed

Lines changed: 931 additions & 37 deletions

File tree

codigo/accounts/_utils.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
"""
55
Funciones de utilidad interna para la aplicación de cuentas (accounts).
66
"""
7+
import time
78
from django.contrib import messages
89
from django.utils import timezone
10+
from django.conf import settings
11+
import stripe
912
from rides.models import Ride
1013
from .models import UserProfile
1114
from .forms import UserProfileForm
@@ -16,6 +19,9 @@
1619
get_user_age, update_last_activity
1720
)
1821

22+
# Configurar la API de Stripe
23+
stripe.api_key = settings.STRIPE_SECRET_KEY
24+
1925
def get_user_and_profile(request, username):
2026
"""
2127
Obtiene un usuario y su perfil dado un nombre de usuario.
@@ -203,3 +209,145 @@ def get_vehicle_fields(profile):
203209
bool(profile.vehicle_year),
204210
bool(profile.vehicle_color)
205211
]
212+
213+
def create_stripe_customer(user):
214+
"""
215+
Crea un cliente de Stripe para un usuario.
216+
217+
Args:
218+
user: Objeto User de Django
219+
220+
Returns:
221+
str: ID del cliente de Stripe creado, o None si hay error
222+
"""
223+
try:
224+
customer = stripe.Customer.create(
225+
email=user.email,
226+
name=f"{user.first_name} {user.last_name}" if (user.first_name and user.last_name) else user.username,
227+
metadata={
228+
'user_id': str(user.id),
229+
'username': user.username
230+
}
231+
)
232+
return customer.id
233+
except Exception as e:
234+
print(f"Error al crear cliente de Stripe: {str(e)}")
235+
return None
236+
237+
def create_stripe_connect_account(user):
238+
"""
239+
Crea una cuenta de Stripe Connect para un usuario.
240+
241+
Args:
242+
user: Objeto User de Django
243+
244+
Returns:
245+
str: ID de la cuenta de Stripe Connect creada, o None si hay error
246+
"""
247+
try:
248+
if not user.email or user.email.strip() == '':
249+
print(f"Error: El usuario {user.username} no tiene un correo electrónico válido")
250+
return None
251+
252+
try:
253+
stripe.Account.list(limit=1)
254+
print(f"Verificación de permisos de Stripe Connect: OK")
255+
except stripe.error.PermissionError as perm_error:
256+
print(f"Error de permisos en Stripe Connect: {str(perm_error)}")
257+
print("La API key no tiene permisos para crear cuentas Connect")
258+
return None
259+
except Exception as check_error:
260+
print(f"Error al verificar permisos de Stripe Connect: {str(check_error)}")
261+
262+
account = stripe.Account.create(
263+
type="standard", # Más simple que 'express' y requiere menos configuración
264+
country="ES", # España
265+
email=user.email.strip(),
266+
business_type="individual",
267+
metadata={
268+
'user_id': str(user.id),
269+
'username': user.username
270+
}
271+
)
272+
273+
print(f"Cuenta Stripe Connect creada con éxito: {account.id}")
274+
return account.id
275+
276+
except stripe.error.InvalidRequestError as e:
277+
print(f"Error de solicitud inválida al crear cuenta de Stripe Connect: {str(e)}")
278+
return None
279+
except stripe.error.AuthenticationError as e:
280+
print(f"Error de autenticación en Stripe: {str(e)}")
281+
return None
282+
except stripe.error.APIConnectionError as e:
283+
print(f"Error de conexión con la API de Stripe: {str(e)}")
284+
return None
285+
except stripe.error.StripeError as e:
286+
print(f"Error general de Stripe al crear cuenta Connect: {str(e)}")
287+
return None
288+
except Exception as e:
289+
print(f"Error inesperado al crear cuenta de Stripe Connect: {str(e)}")
290+
return None
291+
292+
def associate_stripe_accounts_to_user(user):
293+
"""
294+
Crea y asocia cuentas de Stripe a un usuario.
295+
296+
Args:
297+
user: Objeto User de Django
298+
299+
Returns:
300+
tuple: (customer_id, account_id) o (None, None) si hay error
301+
"""
302+
try:
303+
profile = user.profile
304+
305+
if profile.stripe_customer_id and profile.stripe_account_id:
306+
return profile.stripe_customer_id, profile.stripe_account_id
307+
308+
if not profile.stripe_customer_id:
309+
customer_id = create_stripe_customer(user)
310+
if customer_id:
311+
profile.stripe_customer_id = customer_id
312+
313+
if not profile.stripe_account_id:
314+
account_id = create_stripe_connect_account(user)
315+
if account_id:
316+
profile.stripe_account_id = account_id
317+
318+
if profile.stripe_customer_id or profile.stripe_account_id:
319+
profile.save(update_fields=['stripe_customer_id', 'stripe_account_id'])
320+
321+
return profile.stripe_customer_id, profile.stripe_account_id
322+
except Exception as e:
323+
print(f"Error al asociar cuentas de Stripe al usuario: {str(e)}")
324+
return None, None
325+
326+
def create_stripe_onboarding_link(user):
327+
"""
328+
Crea un enlace de onboarding para que el usuario complete su registro en Stripe Connect.
329+
330+
Args:
331+
user: Objeto User de Django
332+
333+
Returns:
334+
str: URL del enlace de onboarding, o None si hay error
335+
"""
336+
try:
337+
profile = user.profile
338+
339+
if not profile.stripe_account_id:
340+
print(f"Error: El usuario {user.username} no tiene una cuenta de Stripe Connect")
341+
return None
342+
343+
account_link = stripe.AccountLink.create(
344+
account=profile.stripe_account_id,
345+
refresh_url=f"https://{settings.ALLOWED_HOSTS[0]}/accounts/settings/",
346+
return_url=f"https://{settings.ALLOWED_HOSTS[0]}/accounts/settings/",
347+
type="account_onboarding",
348+
)
349+
350+
return account_link.url
351+
except Exception as e:
352+
print(f"Error al crear enlace de onboarding de Stripe: {str(e)}")
353+
return None

codigo/accounts/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
LOGIN_TEMPLATE = 'accounts/login.html'
5454
REGISTER_TEMPLATE = 'accounts/register.html'
5555
PROFILE_TEMPLATE = 'accounts/profile.html'
56+
USER_PROFILE_TEMPLATE = 'accounts/user_profile.html'
5657
SETTINGS_TEMPLATE = 'accounts/settings.html'
5758
CHANGE_PASSWORD_TEMPLATE = 'accounts/change_password.html'
5859

codigo/accounts/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ class UserProfile(models.Model):
2424
vehicle_color = models.CharField(max_length=50, blank=True, null=True, verbose_name="Color del vehículo")
2525
vehicle_features = models.TextField(blank=True, null=True, verbose_name="Características adicionales")
2626

27+
# Información de Stripe
28+
stripe_customer_id = models.CharField(max_length=255, blank=True, null=True, verbose_name="ID de Cliente de Stripe")
29+
stripe_account_id = models.CharField(max_length=255, blank=True, null=True, verbose_name="ID de Cuenta Conectada de Stripe")
30+
has_payment_method = models.BooleanField(default=False, verbose_name="Tiene método de pago")
31+
2732
# Preferencias de viaje
2833
MUSIC_CHOICES = [
2934
('any', 'Cualquiera'),

codigo/accounts/templates/accounts/profile.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ <h1 class="profile-title">{{ user.username }}</h1>
5353
<a href="{% url 'reviews:list' %}" class="button button-secondary">
5454
<i class="fas fa-star"></i> Valoraciones
5555
</a>
56+
{% if not is_own_profile %}
57+
<a href="{% url 'reports:create_report' %}?user_id={{ user.id }}" class="button button-secondary">
58+
<i class="fas fa-flag"></i> Reportar
59+
</a>
60+
{% endif %}
5661
</div>
5762
</div>
5863

codigo/accounts/templates/accounts/settings.html

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,85 @@
55

66
{% block extra_styles %}
77
<link rel="stylesheet" href="{% static 'css/pages/settings.css' %}">
8+
<style>
9+
/* Estilos para la sección de pagos */
10+
#section-payment .payment-info {
11+
padding: 15px;
12+
background-color: #f8f9fa;
13+
border-radius: 8px;
14+
}
15+
16+
.status-info {
17+
margin-bottom: 20px;
18+
}
19+
20+
.status-info h3 {
21+
margin-bottom: 15px;
22+
font-weight: 600;
23+
font-size: 1.1rem;
24+
}
25+
26+
.status-item {
27+
display: flex;
28+
align-items: center;
29+
padding: 8px 0;
30+
margin-bottom: 8px;
31+
}
32+
33+
.status-item .icon {
34+
width: 24px;
35+
height: 24px;
36+
border-radius: 50%;
37+
display: flex;
38+
align-items: center;
39+
justify-content: center;
40+
margin-right: 10px;
41+
}
42+
43+
.status-item.success .icon {
44+
background-color: rgba(52, 199, 89, 0.2);
45+
color: #34c759;
46+
}
47+
48+
.status-item.error .icon {
49+
background-color: rgba(255, 59, 48, 0.2);
50+
color: #ff3b30;
51+
}
52+
53+
.status-item.warning .icon {
54+
background-color: rgba(255, 214, 10, 0.2);
55+
color: #b8860b;
56+
}
57+
58+
.payment-actions {
59+
margin-top: 20px;
60+
}
61+
62+
.button.primary {
63+
padding: 10px 20px;
64+
background-color: #007aff;
65+
color: white;
66+
border-radius: 8px;
67+
display: inline-block;
68+
text-decoration: none;
69+
transition: background-color 0.2s;
70+
}
71+
72+
.button.primary:hover {
73+
background-color: #0062cc;
74+
}
75+
76+
.help-text {
77+
margin-top: 10px;
78+
color: #6c757d;
79+
font-size: 0.9rem;
80+
}
81+
82+
.info-text {
83+
color: #34c759;
84+
font-weight: 500;
85+
}
86+
</style>
887
{% endblock %}
988

1089
{% block content %}
@@ -199,6 +278,69 @@ <h2>Preferencias de notificaciones</h2>
199278

200279
<hr>
201280

281+
<div id="section-payment">
282+
<h2>Configuración de pagos</h2>
283+
<div class="payment-info">
284+
<div class="status-info">
285+
<h3>Estado de tu cuenta de pagos</h3>
286+
287+
{% if user_profile.stripe_customer_id %}
288+
<p class="status-item success">
289+
<span class="icon"></span>
290+
<span class="text">Cuenta de cliente Stripe: Configurada</span>
291+
</p>
292+
{% else %}
293+
<p class="status-item error">
294+
<span class="icon"></span>
295+
<span class="text">Cuenta de cliente Stripe: No configurada</span>
296+
</p>
297+
{% endif %}
298+
299+
{% if user_profile.stripe_account_id %}
300+
<p class="status-item success">
301+
<span class="icon"></span>
302+
<span class="text">Cuenta para recibir pagos: Configurada</span>
303+
</p>
304+
{% else %}
305+
<p class="status-item error">
306+
<span class="icon"></span>
307+
<span class="text">Cuenta para recibir pagos: No configurada</span>
308+
</p>
309+
{% endif %}
310+
311+
{% if user_profile.has_payment_method %}
312+
<p class="status-item success">
313+
<span class="icon"></span>
314+
<span class="text">Método de pago: Guardado</span>
315+
</p>
316+
{% else %}
317+
<p class="status-item warning">
318+
<span class="icon">!</span>
319+
<span class="text">Método de pago: No guardado</span>
320+
</p>
321+
{% endif %}
322+
</div>
323+
324+
<div class="payment-actions">
325+
{% if not user_profile.stripe_customer_id or not user_profile.stripe_account_id %}
326+
<a href="{% url 'accounts:setup_payment_account' %}" class="button primary">Configurar cuenta de pagos</a>
327+
<p class="help-text">Configura tu cuenta para poder recibir pagos directamente de otros usuarios.</p>
328+
{% else %}
329+
<p class="info-text">Tu cuenta de pagos está correctamente configurada.</p>
330+
{% if user_profile.stripe_account_id %}
331+
<a href="{% url 'accounts:complete_stripe_onboarding' %}" class="button primary">Completar configuración de Stripe</a>
332+
<p class="help-text">Acepta las condiciones de servicio y añade tu cuenta bancaria para recibir pagos.</p>
333+
{% endif %}
334+
{% if not user_profile.has_payment_method %}
335+
<p class="help-text">Para guardar un método de pago, realiza tu primer pago en la plataforma.</p>
336+
{% endif %}
337+
{% endif %}
338+
</div>
339+
</div>
340+
</div>
341+
342+
<hr>
343+
202344
<div id="section-privacy">
203345
<h2>Preferencias de privacidad</h2>
204346
<form method="post">

0 commit comments

Comments
 (0)