# Redsys Popup — Arreglo Registro de Ventas

## Causa raíz identificada

El ciclo de vida del pago tenía una **condición de carrera**:

- El formulario y el IPN de Redsys llegan al servidor casi simultáneamente
- El código anterior esperaba que el IPN llegase PRIMERO (hasta 15s con `sleep()`)
- En entornos dev/staging Redsys no puede llamar a una URL local → el IPN nunca llega → timeout → error de validación

## Arquitectura final implementada

```
1. Usuario hace clic en "Pagar con Tarjeta"
   → AJAX get_params_ajax:
      - Genera order_id único (timestamp + microsegundo)
      - set_transient('redsys_pending_{order_id}', {form_id, amount}, 1hr)
      - Devuelve parámetros firmados de Redsys al JS

2. JS abre popup → usuario paga en Redsys

3. Redsys llama IPN → handle_ipn():
      - Valida firma HMAC
      - set_transient('redsys_ipn_result_{order_id}', {...}, 1hr)
      - Busca entry por meta '_redsys_order_id':
           - Si existe y está Paid → no hace nada
           - Si existe y no está Paid → llama complete_payment()
           - Si no existe → deja el transient para que authorize() lo lea

4. closer.php carga en la ventana popup → postMessage('redsys_ok') al padre

5. JS recibe mensaje (espera 3s), inyecta hidden inputs y envía el formulario

6. GF procesa el formulario:
   → Llama authorize():
      - Lee gf_redsys_order_id_{form_id} del POST
      - Lee gf_redsys_token_{form_id}: debe empezar por "paid_"
      - get_transient('redsys_pending_{order_id}') → si no existe: error "expirado"
      - Consume el pending transient (one-time use)
      - Si el IPN ya llegó: usa transaction_id y amount reales del IPN
      - Si el IPN no llegó aún: usa order_id como transaction_id y amount del pending
      - Valida amount coincida con total del formulario (±0.05)
      - Retorna is_authorized=true con captured_payment

   → GF llama process_capture() → complete_payment() (del padre, sin override):
      - GFAPI::update_entry → entry queda como 'Paid'
      - insert_transaction → VENTA REGISTRADA EN STATISTICS ✅
      - Notas, hooks, notificaciones

   → gform_after_submission → save_order_id_to_entry():
      - gform_update_meta(entry_id, '_redsys_order_id', order_id)
      (Permite que un IPN tardío identifique la entry)
```

## Cómo probar

### Pago exitoso
1. Abre el formulario con el campo Redsys
2. Rellena todos los campos y haz clic en **"Pagar con Tarjeta"**
3. Paga con tarjeta de prueba Redsys: `4548 8120 4940 0004`, cualquier caducidad, CVV `123`
4. La ventana se cierra y el formulario muestra la **página de confirmación**

### Verificar resultado
| Dónde | Qué debe aparecer |
|---|---|
| **GF → Entradas** | UNA entry con icono 🟢 **Paid** y `transaction_id` |
| **Notas de la entry** | "Payment has been completed. Amount: X. Transaction Id: Y." |
| **GF Dashboard / Sales** | La venta sumada en el total de ingresos del período |

### Si falla
Activar logging de GF en `wp-config.php`:
```php
define('GF_LOGGING_ENABLED', true);
```
Revisar **GF → Registros** para ver qué devuelve `authorize()`.

> **Nota:** El pending transient tiene 1 hora de vida. Si pasan más de 60 min entre el clic de "Pagar" y el envío del formulario, expirará y aparecerá el error "enlace de pago expirado". Esto es comportamiento correcto por seguridad.
