Saltar al contenido principal

Split (repartir entre receptores)

Tu cliente paga una vez, y ese pago tiene que terminar distribuido entre varias cuentas. Eso es un Split: reparte.

Y lo reparte el banco, no tú. No recibes el total y distribuyes después: en el momento de acreditar, a cada cuenta le llega directamente su parte ya separada. Quien paga no ve nada de esto.

Se arma en tres piezas: consigues el split_recipient_agreement_id de cada receptor, habilitas el reparto en tu acuerdo de liquidación (split: true) y, en cada sesión, indicas la distribución —quién recibe cuánto—. La diferencia se acredita al owner (tú). → Las formas de recibir un pago

Antes de nada: cuál de los dos caminos es el tuyo

Hay dos operaciones que devuelven un split_recipient_agreement_id, y elegir mal cuesta tiempo. No son dos formas de hacer lo mismo: son para dos situaciones distintas.

Tu receptor…OperaciónQué necesitas de él
No tiene cuenta en SPIDIPOST /ext/partnerSus datos bancarios exactos: nombre, cédula o RIF, teléfono y banco
Ya tiene cuenta en SPIDIPOST /ext/split-receiving-agreementsNada. La ejecuta él, sobre su propia cuenta, y te pasa el rcv_

La regla práctica: si le vas a repartir a alguien que no está en SPIDI —el mesonero de tu restaurante, el vendedor de tu marketplace, un proveedor— usas partner y lo das de alta tú, sin que él tenga que hacer nada. Si le repartes a un comercio que ya opera con SPIDI, él genera su identificador y te lo comparte.

partner no crea una cuenta en SPIDI

Crea un receptor de fondos: alguien que puede recibir su parte de un pago, sin usuario ni acceso a la plataforma. No recibe credenciales y no entra a ningún sitio. Lo único que hace falta es que sus datos bancarios sean correctos.

Split: repartir un pago entre varias cuentasGuion: .docx · .yaml

1. Registra el partner (receptor)

curl -X POST https://sim-productos.abiertolab.com/api/v1/ext/partner \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"name": "Partner 1",
"identification": "J-12345678-9",
"contact_email": "partner@ejemplo.com",
"contact_phone": "+58-412-0000000",
"bank_code": "0105",
"payment_phone_or_cnta": "01050000000000000000",
"user_type": "comercial"
}'

user_type admite dos valores: comercial o personal. No depende del tipo de documento que pongas en identification: un RIF puede ir con cualquiera de los dos. Elige según lo que sea el receptor, no según cómo se identifique.

identification debe empezar por letra de tipo: V, J, E, P, G o C. Enviar solo el número devuelve Invalid identification format.

Esta operación hace un cobro de verificación REAL al banco del receptor

No es un alta administrativa. SPIDI lanza un cobro de verificación contra el banco de la persona que estás registrando, y el error lo devuelve el banco, no SPIDI:

RespuestaQué significa de verdad
DATOS DEL CLIENTE NO CORRESPONDE A LA CUENTAEl teléfono existe en ese banco, pero la cédula o el titular no cuadran
Telefono no RegistradoEse teléfono no tiene Pago Móvil en ese banco — casi siempre es el bank_code equivocado

De ahí lo que importa: el banco no se adivina probando. Cada intento toca la cuenta de un tercero que no es tu cliente. Pide los datos completos —banco, teléfono y cédula tal como los tiene en su Pago Móvil— antes de la primera llamada.

Y la operación no es idempotente: repetirla con los mismos datos crea otro partner, con otro rcv_. Guarda el identificador que te devuelve.

Para probar el split sin involucrar a nadie

Puedes repartirte a ti mismo. Genera tu propio rcv_ con el paso 2 —sobre tu cuenta, sin cobro de verificación de por medio— y úsalo en la distribución. La sesión se crea sin problema y ejercitas el reparto completo sin tocar el banco de un tercero.

Y no es solo para probar: es la forma de repartir entre varias cuentas propias.

Con el paso 1 ya tienes lo que hace falta

POST /ext/partner devuelve el split_recipient_agreement_id (prefijo rcv_) en la misma respuesta. Si diste de alta al receptor por ahí, el paso 2 no te toca: sáltalo y ve al paso 3.

2. El otro camino: el receptor genera su propio identificador

Esta operación la ejecuta el receptor, sobre su propia cuenta. No hay ningún campo para indicar de quién es el acuerdo: el comercio se identifica por el token con el que llamas. Por eso no puedes crearlo en nombre de otro — necesitarías el UUID de su cuenta bancaria, que solo tiene él.

curl -X POST https://sim-productos.abiertolab.com/api/v1/ext/split-receiving-agreements \
-H "Authorization: Bearer <token-del-receptor>" -H "Content-Type: application/json" \
-d '{
"title": "Recepción de split",
"description": "Para recibir de marketplace X",
"default_bank_account_id": "<uuid-de-su-cuenta-bancaria>"
}'

Devuelve el rcv_…, que es lo que el receptor te comparte a ti.

  • default_bank_account_id — el UUID de su cuenta bancaria. Es el que se le entrega al crear su usuario en SPIDI; no hay operación para consultarlo, así que tiene que tenerlo a mano.
rules está marcado «En Desarrollo» en el contrato

El campo acepta reglas de liquidación por banco de origen —a qué cuenta destino va su parte según desde qué banco pague el pagador— y la API las guarda y las devuelve. Lo que no hemos podido comprobar es que surtan efecto, porque haría falta pagar desde varios bancos distintos.

Mientras siga marcado así, no diseñes contando con ello. Déjalo vacío y usa default_bank_account_id, que sí sabemos que funciona.

3. Habilita el split en tu acuerdo y detalla la distribución en la sesión

El acuerdo de liquidación solo habilita el split — no lleva los detalles del reparto. Pero es un acuerdo completo, con los mismos campos obligatorios que cualquier otro:

{
"title": "Acuerdo Flexible",
"description": "Acuerdo con distribución de fondos",
"split": true,
"default_bank_account_id": "<uuid-de-tu-cuenta-bancaria>",
"payment_methods": { "immediate_debit": true, "crypto": false, "mobile_payment": true }
}
Dos campos obligatorios que el contrato no marca como tales

default_bank_account_id y, en la sesión, failure_url. Si faltan, la llamada devuelve 400 con "must have required property …", y el contrato no te avisa antes. Es la cuenta donde se acredita tu parte del reparto.

Los detalles de la distribución van en cada sesión de pago (Botón o Solicitud), en el objeto split:

curl -X POST https://sim-productos.abiertolab.com/api/v1/ext/payment-sessions/buttons \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"agreement_id": "<uuid_acuerdo_con_split_true>",
"amount_reference": 100.00,
"currency_reference": "USD",
"identifier_label": "Nro de orden", "identifier": "ORD-1001",
"description": "Compra marketplace",
"success_url": "https://tu-web.example/ok", "failure_url": "https://tu-web.example/fail",
"webhook_url": "https://tu-web.example/webhooks/spidi",
"split": {
"distribution": [
{ "split_recipient_agreement_id": "<rcv_partner_1>", "amount_reference": 10.00, "observations": "Cuotaparte Partner 1" },
{ "split_recipient_agreement_id": "<rcv_partner_2>", "amount_reference": 20.00, "observations": "Cuotaparte Partner 2" }
]
}
}'
  • split.distribution — lista de receptores y su monto para esta transacción. Cada ítem: split_recipient_agreement_id (el receptor del paso 2), amount_reference (cuánto recibe) y observations.
  • La suma de los montos debe ser menor al amount_reference total de la sesión: la diferencia se acredita al owner (tú). Todas las partes se llaman igual —la de cada partner y la tuya son cuotapartes—, porque todas responden a lo mismo: el acuerdo por el que se reparten esa venta. La comisión del banco es otra cosa, y se aplica aparte, sobre cada crédito. → Qué se descuenta
  • split.document (opcional) adjunta evidencia (factura/recibo) para tus partners; SPIDI la transporta, no calcula IVA.

El reparto se aplica en la acreditación (fase 2): el dinero se distribuye a los receptores según lo que enviaste en la sesión. → Botón web · Transacción a dos fases.

Un solo webhook_url, varias llamadas

Configuras un único webhook_url por sesión — el mismo campo que usas en cualquier pago, con o sin split. La diferencia en split es que ese mismo endpoint recibe varias llamadas para una sola sesión, no una:

  • Una por receptor, cuando se le acredita su parte: payment_session.accreditation_to_recipient_completed (marcado "en desarrollo" en el contrato).
  • Una de cierre, cuando termina el reparto completo: payment_session.accredited — la misma que en un pago sin split, aquí con el resumen de lo acreditado a todos los receptores.

No hay una URL por receptor ni un webhook_url distinto por partner: todo llega a tu único endpoint, y distingues cada llamada por su event y por el id del receptor dentro del payload.

Un receptor es una cuenta, no una URL

Un receptor (rcv_…) es un destino de fondos: la cuenta bancaria a la que se acredita su parte (default_bank_account_id en el acuerdo de recepción — ver paso 2). No tiene webhook propio: las notificaciones del split siempre llegan a tu webhook_url, nunca al receptor.

Probar en el simulador

Fuerza ambos tiempos y observa el webhook de acreditación:

curl -X POST https://sim-productos.abiertolab.com/control/sessions/<id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" -d '{"leg":1,"outcome":"paid"}'
curl -X POST https://sim-productos.abiertolab.com/control/sessions/<id>/outcome \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" -d '{"leg":2,"outcome":"accredited"}'
# -> webhook payment_session.accredited (con el resumen de lo acreditado a todos los receptores)

Hoy el simulador emite la llamada de cierre (accredited); la llamada por receptor (accreditation_to_recipient_completed) es parte del contrato pero aún no la reproduce. En producción, prepárate para recibir ambas.

El flujo completo (partner → split-receiving → botón → paid + accredited) está verificado contra el simulador (simulador/test/func/guides.func.test.ts).

Usar el simulador