← Volver al blog

Cobrar a través de una pasarela que pertenece a otro sitio

Publicado el
4 min de lectura
--- vistas

A veces la pasarela de pago no es tuya. Pertenece a un sitio socio, a otra entidad jurídica o a alguien que tiene una licencia que tú no tienes. Hay que enviar allí al comprador, y todo lo que pase después tiene que volver a ti de alguna manera.

Esta integración se escribe mal más a menudo que bien, y falla de dos formas concretas que merece la pena nombrar.

Fallo uno: la llamada que nadie autentica

El sistema remoto necesita avisarte de que el pago salió bien, así que expones un endpoint. Si ese endpoint acepta un POST con un identificador de pedido y un estado y actúa en consecuencia, cualquiera que descubra la URL puede marcar cualquier pedido como pagado. Tu tienda le creerá, enviará la mercancía y no se enterará jamás.

No es una hipótesis. Las URLs de retorno acaban en logs, en el historial del navegador, en tickets de soporte, en bundles de JavaScript.

Se arregla con dos capas, y hacen falta las dos. Un token acredita que quien llama tiene permiso para llamar. Una firma garantiza que la carga no se alteró por el camino:

public function sign(string $body): string
{
    return hash_hmac('sha256', $body, $this->key);
}

public function verify(string $body, string $signature): bool
{
    return hash_equals($this->sign($body), $signature);
}

hash_equals y no ===, porque la comparación de cadenas termina en el primer byte distinto y el tiempo que tarda filtra cuánta parte de la firma era correcta. Es un ataque real, y el arreglo cuesta el nombre de una función.

La firma además debe cubrir el cuerpo en bruto, no el array ya parseado. Parsear y luego firmar deja pasar sin firma todo lo que el parser normalice.

Fallo dos: dos registros de pedido que se separan

El sitio remoto crea su propio pedido para el pago. Ahora la misma compra existe dos veces, y esos dos registros acabarán discrepando, porque discrepa cualquier par de registros en dos sistemas.

El instinto es una tabla de correspondencias: identificador local, identificador remoto, actualizada cuando cambia cualquiera. Funciona hasta que una escritura falla a medias, y entonces la correspondencia está mal de una manera que nada detecta.

Guarda la relación en los propios registros. Cuando se cree el pedido remoto, etiquétalo con el identificador de origen y una marca de fuente, para que cualquiera de los dos lados encuentre al otro con una consulta:

wc_get_orders([
    'meta_query' => [
        ['key' => '_order_id', 'value' => $wcOrderId],
        ['key' => '_source',   'value' => $orderProvider],
    ],
]);

No hay un tercer artefacto que mantener sincronizado, así que no hay nada que pueda desincronizarse. La relación pasa a ser una propiedad de los datos en vez de un registro sobre los datos.

La marca de fuente importa tanto como el identificador: dos sitios socios acabarán mandándote los dos el pedido 1041.

Registra el intercambio, no solo el resultado

Cada petición y cada respuesta de este flujo deberían caer en una tabla: identificador de pedido, identificador remoto, fuente, estado, URL de retorno, carga en bruto.

El motivo son las disputas. Un cliente dice que pagó y tú dices que nunca te enteraste. Sin registro, eso se convierte en dos equipos leyendo dos paneles y discrepando educadamente durante una semana. Con registro es una consulta, y en un minuto sabes si la llamada llegó y qué decía.

Guarda específicamente la carga en bruto. Un resumen parseado te dice lo que entendiste; el cuerpo en bruto te dice lo que te enviaron, y el hueco entre esas dos cosas es justo donde vive esta clase de errores.

Propaga el estado en ambos sentidos

Un reembolso o una cancelación en cualquiera de los lados tiene que llegar al otro, o uno de los dos sistemas queda callado y equivocado. Cuelga un manejador de los cambios de estado locales y empújalos hacia fuera.

Es la parte que más implementaciones se saltan, porque el camino feliz funciona sin ella y el fallo solo aparece semanas después, durante la contabilidad.

La regla general

Cuando el dinero cruza la frontera entre sistemas, da por hecho que la frontera es hostil y que los dos lados van a discrepar. Autentica a quien llama, firma la carga, ata los registros entre sí por sus propios campos y anota cada intercambio. Nada de eso es caro por adelantado, y todo ello es carísimo de añadir a posteriori mientras un cliente espera una respuesta sobre su dinero.

Disponible para colaboración por contrato

Estoy disponible para colaborar por contrato. Si tiene una idea de proyecto interesante, reserve una llamada por Calendly.

Agenda una llamada de 30 min