Qué dice GoCardless
| Reason | Tipo | HTTP | Texto de GoCardless |
|---|---|---|---|
idempotent_creation_conflict | invalid_state | 409 | A resource already exists for the supplied idempotency key. |
La página de límites de GoCardless lo completa: si ya existe un recurso con esa clave, la respuesta es un 409 con links.conflicting_resource_id, que señala el recurso que ya está creado. Las claves pueden tener hasta 128 caracteres y GoCardless las recuerda al menos 30 días.

El problema que resuelve
Una petición puede tardar: GoCardless corta las que pasan de 29 segundos, y la red puede cortarse antes. Si Odoo pide un cobro y no recibe respuesta, no sabe si GoCardless lo creó. Repetir la petición sin más es peligroso; la propia documentación de GoCardless avisa de que reintentar sin clave de idempotencia tras un tiempo de espera agotado puede cobrar el mismo pago dos veces.
Con clave, el segundo intento no crea nada: si el primero llegó, GoCardless contesta con este 409 y el identificador del cobro que ya existe.
Cómo lo usa el conector
El conector manda una clave de idempotencia en cada petición que crea un cobro, un reembolso, un reintento o la solicitud del mandato. La clave no es aleatoria. Se calcula con la operación, la transacción de Odoo y el identificador de la base de datos, de modo que:
- repetir el mismo cobro manda exactamente la misma clave;
- una copia de la base para pruebas no choca con la de producción aunque las dos emitan la misma factura.
Cuando llega el 409, el conector lee conflicting_resource_id, pide ese recurso a GoCardless y lo adopta como respuesta. Para ti no hay error: la transacción queda enlazada al cobro que ya existía, y la factura sigue su curso normal.
Cuando GoCardless no contesta
Si la petición se queda sin respuesta, el conector la repite una vez con la misma clave. Sin clave no repite nada. Si tampoco hay respuesta, avisa sin dar el cobro por perdido: «GoCardless no respondió, así que aún no se sabe si aceptó la solicitud. Se volverá a enviar, de forma segura, en menos de una hora.». La transacción queda pendiente, no en error, porque en error Odoo podría generar otro cobro con otra referencia y otra clave.
Ese aviso sale en el idioma de quien lanza el cobro. Si lo lanza una tarea programada, como la renovación de una suscripción o el reenvío del propio conector, se escribe sin nadie delante: en una base de datos creada en castellano sale así, y en una creada en inglés y pasada después a castellano, en inglés: «GoCardless did not answer, so it is not known yet whether it took the request. It will be sent again, safely, within the hour.».

Qué hacer
Con el conector, normalmente nada: el 409 se resuelve solo. Si lo ves en otro programa conectado a la misma cuenta de GoCardless, comprueba dos cosas. La primera, que no estés reutilizando una clave para operaciones distintas. La segunda, que el recurso de conflicting_resource_id sea el que esperabas: si lo es, el reintento ha funcionado como debe. Si no lo es, guarda el request_id de la respuesta: GoCardless pide incluirlo al contactar con su soporte, y el conector lo añade a sus mensajes de error.
¿Un 409 significa que el cliente pagó dos veces?
Al revés. Significa que GoCardless se negó a crear el segundo cobro porque la clave ya tenía uno. El cliente paga una sola vez.
¿Qué pasa si se cambia la referencia de la transacción?
Cambia la clave, y GoCardless lo trata como un cobro nuevo. Por eso el conector deja pendiente, y no en error, un cobro sin respuesta: así no se crea otro con otra referencia.
Con nuestro conector
En Odoo 17, 18, 19 y 20 el conector aplica la regla que GoCardless pide para no cobrar dos veces: clave fija por operación, repetición solo con la misma clave y adopción del recurso existente cuando llega el 409.
Sigue leyendo
Comprobado en el código del conector (versión 17.0, 18.0, 19.0 y 20.0) y en las fuentes enlazadas
Fuentes
- docs.gocardless.com/docs/api-reference/limits
- Código del módulo: flexigo_payment_gocardless: models/payment_provider.py, línea 462 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_provider.py, línea 352 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_transaction.py, línea 68 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_provider.py, línea 391 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_provider.py, línea 357 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_provider.py, línea 373 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_transaction.py, línea 299 en 17.0 (d203ee6), línea 299 en 18.0 (e2ab18b), línea 299 en 19.0 (ce7f5a3) y línea 310 en 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: i18n/es.po, línea 478 en 17.0 (d203ee6), línea 478 en 18.0 (e2ab18b), línea 478 en 19.0 (ce7f5a3) y línea 483 en 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: i18n/es.po, línea 30 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: __manifest__.py, línea 31 en 17.0 (d203ee6), 18.0 (e2ab18b), 19.0 (ce7f5a3) y 20.0 (688d86b)
- docs.gocardless.com/docs/api-reference/responses-and-errors
- Código del módulo: flexigo_payment_gocardless: models/payment_transaction.py, línea 123 en 17.0 (d203ee6), línea 123 en 18.0 (e2ab18b), línea 123 en 19.0 (ce7f5a3) y línea 126 en 20.0 (688d86b)
- Código del módulo: flexigo_payment_gocardless: models/payment_transaction.py, línea 247 en 17.0 (d203ee6), línea 247 en 18.0 (e2ab18b), línea 247 en 19.0 (ce7f5a3) y línea 258 en 20.0 (688d86b)
- github.com/odoo/odoo/blob/17.0/odoo/http.py
