Concepto clave
Los reembolsos y disputas son dos mecanismos de protección para clientes que afectan directamente tus ingresos y reputación. Imagina que gestionas un gimnasio: un reembolso es cuando un miembro pide devolución de su cuota mensual porque no pudo asistir, mientras que una disputa es cuando ese miembro contacta a su banco para revertir el cargo alegando fraude. En Stripe, los reembolsos los inicias tú como negocio, mientras que las disputas las inicia el cliente a través de su banco.
La diferencia crítica está en las consecuencias: un reembolso es un acuerdo directo con el cliente, pero una disputa implica costos adicionales (tarifas de $15-25) y puede afectar tu tasa de disputas. Si superas el 0.75% de transacciones disputadas, Stripe puede penalizarte. Piensa en esto como la diferencia entre resolver un problema con un cliente amablemente versus que ese cliente llame a su abogado.
Cómo funciona en la práctica
Cuando un cliente solicita un reembolso, el flujo es: 1) Recibes la solicitud por correo o tu sistema, 2) Verificas la factura y el pago en Stripe Dashboard o API, 3) Decides si aprobar el reembolso parcial o total, 4) Lo procesas y notificas al cliente. Para disputas, el flujo es diferente: 1) Stripe te notifica via webhook, 2) Tienes 7-21 días para presentar evidencia, 3) Reúnes documentos como confirmaciones de envío o registros de acceso, 4) Envías la evidencia y esperas la decisión del banco.
Ejemplo paso a paso para un reembolso: María compra una suscripción anual a tu plataforma de cursos por $120. A los 3 meses pide cancelación y reembolso parcial. Primero, cancelas la suscripción para evitar futuros cargos. Luego, calculas el reembolso proporcional: $120 / 12 meses = $10 por mes, 9 meses restantes = $90. Finalmente, procesas el reembolso de $90 y envías un recibo actualizado.
Codigo en accion
Procesar un reembolso completo via API:
// Antes: Solo cancelar la suscripción
async function cancelSubscription(subscriptionId) {
const subscription = await stripe.subscriptions.update(subscriptionId, {
cancel_at_period_end: true
});
return subscription;
}
// Después: Cancelar Y procesar reembolso
async function handleRefundRequest(paymentIntentId, reason) {
// 1. Crear el reembolso
const refund = await stripe.refunds.create({
payment_intent: paymentIntentId,
reason: reason // 'requested_by_customer' o 'duplicate'
});
// 2. Actualizar tu base de datos
await db.update('invoices', {
status: 'refunded',
refund_id: refund.id
}, { payment_intent_id: paymentIntentId });
// 3. Notificar al cliente
await sendEmail(refund.receipt_email, {
subject: 'Reembolso procesado',
amount: refund.amount / 100
});
return refund;
}Manejar una disputa via webhook:
# Configuración del endpoint webhook
@app.route('/webhook/stripe', methods=['POST'])
def stripe_webhook():
payload = request.get_data(as_text=True)
sig_header = request.headers.get('Stripe-Signature')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, webhook_secret
)
except ValueError:
return 'Invalid payload', 400
except stripe.error.SignatureVerificationError:
return 'Invalid signature', 400
# Manejar evento de disputa
if event['type'] == 'charge.dispute.created':
dispute = event['data']['object']
# 1. Registrar en tu sistema
db.insert('disputes', {
'dispute_id': dispute['id'],
'charge_id': dispute['charge'],
'amount': dispute['amount'],
'status': 'needs_response',
'due_by': dispute['evidence_details']['due_by']
})
# 2. Iniciar proceso de recolección de evidencia
start_evidence_collection(dispute['id'])
return 'Dispute logged', 200
return 'Event received', 200Errores comunes
- Reembolsar sin verificar el estado del pago: Intentar reembolsar un pago que ya fue reembolsado o está en disputa causa errores. Siempre verifica
payment_intent.statusantes de proceder. - Ignorar los plazos de disputas: Cada banco tiene diferentes ventanas (7, 14 o 21 días). Si no envías evidencia a tiempo, pierdes automáticamente.
- No documentar la comunicación con el cliente: En disputas, los correos y chats son evidencia crucial. Guarda todo en tu sistema, no solo en tu bandeja de entrada.
- Reembolsar el monto incorrecto: Para suscripciones, calcula proporcionalmente basado en el uso. Usa
subscription.current_period_startyendpara determinar el periodo no utilizado. - No actualizar el estado en tu base de datos: Después de un reembolso o disputa, actualiza los registros locales para evitar duplicados o estados inconsistentes.
Checklist de dominio
- Puedo crear un reembolso parcial o total via API con manejo de errores
- Sé configurar y verificar webhooks para eventos de disputas
- He implementado un sistema para recolectar y enviar evidencia dentro de los plazos
- Entiendo la diferencia entre
reasonen reembolsos y cómo afecta las métricas - Puedo calcular reembolsos proporcionales para suscripciones basado en el periodo
- He integrado notificaciones automáticas a clientes después de reembolsos
- Sé monitorear mi tasa de disputas en Stripe Dashboard y tomar acciones preventivas
Implementar sistema de reembolsos automáticos con límites
Construye un endpoint que procese reembolsos automáticos con las siguientes reglas:
- Crea una ruta
POST /api/refundsque acepte{ payment_intent_id, reason, amount } - Valida que el reembolso no exceda el 50% del monto original para montos mayores a $100
- Si es una suscripción, calcula el monto proporcional basado en días no usados
- Registra cada reembolso en tu base de datos con timestamp y usuario responsable
- Envía un email de confirmación con el recibo de Stripe adjunto
- Maneja al menos 3 casos de error específicos (pago ya reembolsado, monto inválido, etc.)
Usa el lenguaje de backend de tu preferencia y prueba con datos reales del Sandbox de Stripe.
Pistas- Usa el método
payment_intents.retrieve()para obtener el monto original antes de validar - Para suscripciones, necesitarás buscar la invoice asociada al payment intent primero
- Considera usar una cola de trabajos para el envío de emails así no bloqueas la respuesta HTTP
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.