Sistema de respuesta automática a disputas de Stripe con PDFs de evidencia
De Wikiprompt, la enciclopedia libre de prompts
Sistema de respuesta automática a disputas de Stripe con PDFs de evidencia Una especificación completa de construcción para un sistema automatizado de disputas de contracargos de Stripe: envío automático por webhook, PDFs de evidencia con capturas de pantalla del producto, un worker de sincronización por CLI y un mini panel de administración.
Medios relacionados
Contenido del PromptGuardar
🌐
Construya un sistema de respuesta automática de disputas para Stripe que:
1. Recopilación de evidencia compartida (app/dispute_evidence.php)
Cree un archivo compartido con funciones utilizadas tanto por el webhook como por el trabajador de sincronización. Esto evita duplicar la lógica de evidencia.
Funciones clave:
- getDisputeUserPlan($user, $stripe) - obtiene el plan de suscripción del usuario desde la API de Stripe (fuente de verdad, ya que el campo de plan en la base de datos local se limpia al cancelar). Recurre a los campos de la base de datos local si la llamada a Stripe falla. Mapea los ID de productos a nombres de planes e incluye precio/intervalo y estado de cancelación.
- collectDisputeEvidence($stripe, $user, $email, $charge, $photosDb) - recopila toda la evidencia de texto y archivos, devuelve un array listo para enviar a Stripe.
- generateServiceDocPdf($stripe, $user, $email, $photos_done, $recent_photos, $total_amount_paid) - genera un PDF con información del cliente, resumen de uso, tabla de actividad reciente y hasta 6 imágenes reales de productos (redimensionadas a JPEG de 500px de ancho / calidad 75 para mantenerse bajo el límite de carga de archivos de 5MB de Stripe). Devuelve tanto el ID de archivo de Stripe como los datos PDF sin procesar.
Importante: obtenga total_amount_paid de la API de cargos de Stripe (suma de cargos exitosos no reembolsados) en lugar de confiar en la base de datos local que puede ser nula o estar desactualizada.
2. Manejador de webhook (en stripe_webhook.php)
Capture los eventos charge.dispute.created. Cuando llegue una disputa:
- Obtenga los objetos de disputa, cargo y cliente de Stripe
- Busque al usuario en la base de datos local por stripe_customer_id
- Guarde la disputa en una base de datos SQLite de disputas (campos: dispute_id, charge_id, payment_intent_id, stripe_customer_id, user_id, email, amount, currency, reason, status, epoch_created, epoch_evidence_submitted, evidence_json, stripe_response, epoch_resolved, outcome)
- Llame a collectDisputeEvidence() para recopilar toda la evidencia (texto + cargas de archivos)
- Envíe la evidencia a Stripe mediante $stripe->disputes->update($dispute_id, ['evidence' => $evidence])
- Envíe una notificación de Telegram de que llegó una nueva disputa y la evidencia se envió automáticamente
También capture los eventos charge.dispute.updated y charge.dispute.closed para rastrear los resultados de las disputas (ganadas/perdidas) en la base de datos y enviar notificaciones de Telegram con el resultado.
3. Campos de evidencia enviados a Stripe
Campos de TEXTO (escriba cadenas directamente):
- product_description - describa qué es el producto/servicio
- customer_name / customer_email_address - del objeto de cliente de Stripe
- access_activity_log - registro de uso detallado: fecha de registro, número de elementos/acciones realizadas, última fecha de actividad, plan de suscripción (de Stripe), plataforma, monto total pagado (de Stripe), actividad reciente con marcas de tiempo
- uncategorized_text - el argumento de "por qué deberíamos ganar": el cliente se registró en X, usó activamente el servicio haciendo Y cosas, monto total pagado, el servicio se entregó digitalmente/al instante, el cliente nunca nos contactó para un reembolso antes de disputar
- refund_policy_disclosure / cancellation_policy_disclosure - cuándo se presentaron las políticas (durante el pago, siempre accesibles en /legal)
- refund_refusal_explanation / cancellation_rebuttal - prueba de uso y falta de contacto previo
- service_date - fecha del cargo (formato Y-m-d)
Campos de CARGA DE ARCHIVOS (cargue el archivo a Stripe primero mediante $stripe->files->create(['purpose'=>'dispute_evidence', 'file'=>fopen($path,'r')]), luego pase el ID file_xxxxx devuelto):
- receipt - obtenga el PDF de la factura directamente de Stripe ($stripe->invoices->retrieve($charge->invoice)->invoice_pdf), descárguelo y cárguelo como evidencia de disputa
- service_documentation - genere un PDF que contenga: sección de información del cliente, resumen de uso del servicio, tabla de actividad reciente y hasta 6 imágenes/capturas de pantalla reales del producto que el cliente recibió. Redimensione las imágenes antes de incrustarlas (500px de ancho, calidad JPEG 75) para mantenerse bajo el límite de 5MB de Stripe.
También guarde ambos PDFs en su almacenamiento de archivos (por ejemplo, Cloudflare R2, S3) con nombres de archivo hash para que no sean adivinables pero visibles desde el panel de administración. Almacene las URL de almacenamiento en evidence_json como _receipt_r2_url y _service_doc_r2_url.
4. Trabajador de sincronización CLI (workers/syncDisputes.php)
Un script que obtiene TODAS las disputas existentes de la API de Stripe (paginado con $stripe->disputes->all(['limit' => 100]) y starting_after para paginación), las guarda en la base de datos local de disputas, y para cualquier que aún tenga estado needs_response o warning_needs_response y no haya tenido evidencia enviada aún, envía automáticamente la evidencia usando la función compartida collectDisputeEvidence(). Esto es necesario porque el webhook solo captura disputas futuras, no las existentes. Ejecutar solo mediante CLI (php workers/syncDisputes.php). Guarda un archivo de caché JSON con los resultados de sincronización para que el panel pueda mostrar la última hora de sincronización.
5. Mini panel (disputes.php con autenticación ?key=)
Una página HTML simple protegida por el parámetro de consulta ?key= que muestra:
- Cajas de estadísticas: disputas totales, pendientes, ganadas, perdidas, disputadas en los últimos 30 días (monto + recuento), disputadas en los últimos 12 meses (monto + recuento), total $ disputado
- Una nota que muestra el comando de sincronización CLI y la última hora de sincronización desde la caché
- Un formulario de prueba donde ingresa un stripe_customer_id para previsualizar qué evidencia se enviaría (sin enviar realmente)
- Una tabla de todas las disputas: fecha, correo electrónico, monto, motivo, estado (insignias de color), estado de envío de evidencia, enlaces tanto a la vista de detalle como al panel de Stripe
Vista de detalle (action=view&id=dispute_id): muestra toda la información de la disputa, enlace a Stripe, y un botón "Regenerar evidencia", enlaces de archivos PDF, IDs de carga de archivos de Stripe, todos los campos de evidencia de texto.
Regenerar evidencia (action=regen&id=dispute_id): regenera los PDFs de recibo y documentación de servicio y los carga al almacenamiento de archivos, actualiza evidence_json con nuevas URL de PDF. Use fastcgi_finish_request() para enviar la respuesta HTTP inmediatamente (redirigir de vuelta a la página de detalle con un aviso de "regenerando en segundo plano"), luego continúe generando los PDFs en segundo plano para evitar tiempos de espera del frontend.
Agregue una reescritura de nginx para la página (por ejemplo, rewrite ^/disputes/?$ /disputes.php). Asegúrese de que esté en el archivo de configuración de nginx correcto (verifique a cuál apunta el enlace simbólico en sites-enabled).
6. Notificaciones de Telegram
- Nueva disputa: "{site name} - Nueva disputa de {email} por ${amount} ({reason}). Evidencia enviada automáticamente a Stripe. {stripe_dashboard_link}"
- Evidencia fallida: "{site name} - Nueva disputa de {email} por ${amount} ({reason}). El envío de evidencia FALLÓ: {error}"
- Disputa ganada/perdida con el resultado y enlace
- Error de permisos de BD: "{site name} - ERROR DE BD DE DISPUTAS: {error} - verifique los permisos en data/disputes.db"
7. Asegúrese de que estos eventos de webhook de Stripe estén habilitados en el panel de Stripe: charge.dispute.created, charge.dispute.updated, charge.dispute.closed
8. Permisos de base de datos
El archivo disputes.db debe ser escribible por el usuario del servidor web (por ejemplo, www-data). Si lo crea desde CLI como root, corrija la propiedad para que coincida con sus otros archivos de BD. PHP-FPM se ejecuta como un usuario diferente al de root.
9. Dependencias
- FPDF (setasign/fpdf) para generación de PDF, instalado mediante composer require setasign/fpdf
- Extensión GD para redimensionamiento de imágenes (generalmente ya instalada)
- SDK de PHP de Stripe (ya instalado si tiene webhooks de Stripe)
- SDK de AWS S3 para cargas R2/S3 (ya instalado si usa Cloudflare R2)
Iniciá sesión para ver el prompt completo
Continuar con:
Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad
Uso
Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.
Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.
Referencias
- Categoría: Prompts de coding
- Fuente: https://x.com/levelsio/status/2041132289065631988
Discusión
0 comentarios