API Multi Pagos

GoSoft · Módulo Realty

API Multi Pagos

Integración para que Multi Pagos consulte contratos de venta de inmuebles, liste las cuotas pendientes de un cliente, registre el cobro y lo reverse. El pago registrado por esta API genera exactamente el mismo asiento contable que un cobro hecho desde la web.

Controlador app/Controllers/APIMultiPagos.php Formato JSON · UTF-8 Zona horaria America/La_Paz Moneda USD contrato · BOB asiento

Convenciones

Los cinco endpoints comparten la misma envoltura de respuesta y el mismo modo de identificar al cliente: el subdominio determina la base de datos.

URL base

Las rutas cuelgan de la raíz del host, sin prefijo /api.

Producción
https://{empresa}.gosoft.app/api-multipagos-...

Envoltura de respuesta

Toda respuesta —éxito o error— devuelve HTTP 200 con este cuerpo. El estado real de la operación viaja en el campo code, no en el código HTTP.

Estructura
{
  "msj":  "Success",   // texto legible: "Success" o "Error: ..."
  "data": { },         // carga util; en error suele ser {"NULO": null}
  "code": 200          // 200 | 404 | 409 | 500  <-- este es el estado a evaluar
}
Importante

No uses el status HTTP para decidir si el pago se registró. Evalúa siempre body.code === 200. Un fallo de validación llega igualmente como HTTP 200 con code: 404.

Autenticación

Los cinco endpoints están publicados sin filtro de sesión ni token: no requieren cabecera Authorization. El control de acceso depende hoy de la red y del host, por lo que conviene restringir por IP de origen del lado de Multi Pagos.

Cabeceras

CabeceraValorNota
Content-Typeapplication/jsonObligatoria en los cuatro POST. Sin ella el cuerpo se lee vacío y la API responde 404.
Acceptapplication/jsonRecomendada.

Flujo de integración

El orden importa: el código de cuota que se envía al cobrar sólo se obtiene del paso 2, y sólo es válido mientras la cuota siga con saldo.

PASO 1

Identificar al cliente

Se busca por carnet, número de contrato o celular y se obtienen sus contratos vigentes.

PASO 2

Listar cuotas

Para el contrato elegido se piden las cuotas con saldo, ya con la mora calculada al día.

PASO 3

Confirmar el cobro

Se envían las cuotas cobradas y el número de transacción. GoSoft genera el comprobante.

PASO 4

Anular si corresponde

Con el mismo número de transacción se revierte el comprobante y todo lo derivado.


ENDPOINT 01

Forma de búsqueda

GET /api-multipagos-forma-busqueda

Handshake inicial. Confirma el código de comercio y devuelve el modo de búsqueda por defecto.

Parámetros (query string)

CampoTipoDescripción
proyecto_idstringreqCódigo de comercio o proyecto asignado a Multi Pagos.

Ejemplo de llamada

cURL
curl "https://demo.gosoft.app/api-multipagos-forma-busqueda?proyecto_id=1042"
Respuesta200
{
  "msj": "Success",
  "data": {
    "searchType": "1",
    "searchField": "",
    "commerceInfo": {
      "proyecto_id": "1042"
    }
  },
  "code": 200
}
Sin proyecto_id404
{
  "msj": "Error: No código de proyecto",
  "data": { "NULO": null },
  "code": 404
}
Nota

searchType: "1" significa «buscar por documento de identidad». El valor es fijo: la API no valida el proyecto_id contra la base, sólo lo devuelve.

ENDPOINT 02

Buscar contratos

POST /api-multipagos-buscar-contrato

Devuelve los contratos de venta asociados a un cliente. Acepta tres criterios de búsqueda.

Cuerpo (JSON)

CampoTipoDescripción
searchTypeintreq 1 documento de identidad · 2 número de contrato · 3 celular
searchFieldstringreq Valor a buscar. Coincidencia exacta, sin comodines.
commerceInfoobjectopc Se acepta y se ignora. Sirve para trazabilidad del lado de Multi Pagos.

Ejemplo de llamada

cURL · búsqueda por carnet
curl -X POST "https://demo.gosoft.app/api-multipagos-buscar-contrato" \
  -H "Content-Type: application/json" \
  -d '{
        "searchType": 1,
        "searchField": "6842193",
        "commerceInfo": { "proyecto_id": "1042" }
      }'
Respuesta200
{
  "msj": "Success",
  "data": {
    "clientName": "MARIA FERNANDA GUTIERREZ ROJAS",
    "Clientdocument": "6842193",
    "contracts": [
      {
        "id": "4821",
        "uen": "INMOBILIARIA NORTE",
        "project": "Urbanizacion Los Tajibos",
        "uen_id": "7",
        "uv": "12",
        "mzno": "34",
        "number": "07",
        "lote": "07",
        "description": "Lote 07 - 360 m2",
        "person_id": "9134",
        "person": "Maria Fernanda Gutierrez Rojas",
        "document": "6842193",
        "movil": "71234567",
        "email": "mf.gutierrez@gmail.com",
        "status_det": "Vigente",
        "codproyecto": "1042",
        "codgrupo": "URB-TAJIBOS",
        "penalty_value": "0.50",
        "penalty_calculation": "1"
      }
    ]
  },
  "code": 200
}

Campos de contracts[]

CampoDescripción
idNúmero de contrato. Es el contractCode / contractNumber de los endpoints siguientes.
uenUnidad de negocio propietaria del proyecto.
projectNombre del proyecto o urbanización.
uv · mzno · number · loteUbicación del inmueble: unidad vecinal, manzano y lote. lote repite number.
person · document · movil · emailDatos del titular del contrato.
status_detEstado legible: Vigente, Finalizado, Pendiente, Pagado, Reservado.
codproyecto · codgrupoIdentificadores del proyecto contenedor.
uen_id · penalty_value · penalty_calculationSólo se incluyen cuando searchType = 2.
Cliente sin contratos200
{
  "msj": "Success",
  "data": {
    "clientName": null,
    "Clientdocument": null,
    "contracts": []
  },
  "code": 200
}
Faltan campos404
{
  "msj": "Error: Tiene que enviar: searchType y searchField",
  "data": { "NULO": null },
  "code": 404
}
Ojo

Un cliente sin contratos devuelve code: 200 con contracts: [], no un error. Valida siempre que el arreglo tenga elementos antes de continuar.

Los contratos revertidos, retenidos, devueltos, anulados y eliminados nunca aparecen en la lista.

ENDPOINT 03

Ver cuotas

POST /api-multipagos-ver-cuotas

Lista las cuotas con saldo pendiente del plan de pagos vigente, con la mora ya calculada al día de hoy.

Cuerpo (JSON)

CampoTipoDescripción
contractCodeint | stringreq Número de contrato obtenido en el endpoint 02 (contracts[].id).

Ejemplo de llamada

cURL
curl -X POST "https://demo.gosoft.app/api-multipagos-ver-cuotas" \
  -H "Content-Type: application/json" \
  -d '{ "contractCode": 4821 }'
Respuesta200
{
  "msj": "Success",
  "data": {
    "fees": [
      {
        "code": "118742",
        "date": "2026-06-05",
        "name": "Periodo 14, Proyecto: Urbanizacion Los Tajibos, UV 12, Mzno 34, Nro 07",
        "pagado_usd": "0.00",
        "amount": "187.75",
        "currency": "USD"
      },
      {
        "code": "118743",
        "date": "2026-07-05",
        "name": "Periodo 15, Proyecto: Urbanizacion Los Tajibos, UV 12, Mzno 34, Nro 07",
        "pagado_usd": "50.00",
        "amount": "135.00",
        "currency": "USD"
      }
    ]
  },
  "code": 200
}

Campos de fees[]

CampoDescripción
codeCódigo de cuota. Es el feeCode que se envía al confirmar el pago.
dateFecha de vencimiento de la cuota (YYYY-MM-DD).
nameEtiqueta lista para mostrar al cliente en pantalla o recibo.
pagado_usdLo ya abonado a esa cuota. Mayor a cero indica un pago parcial previo.
amountSaldo a cobrar en USD, con mora incluida si corresponde.
currencySiempre USD.
Contrato al día200
{
  "msj": "Success",
  "data": { "fees": [] },
  "code": 200
}
Contrato inexistente404
{
  "msj": "Error: No existe contrato",
  "data": { "NULO": null },
  "code": 404
}
Cómo se arma la lista

Sólo se devuelven cuotas con saldo mayor a 0.9 USD; los redondeos residuales se consideran pagados. El amount se recalcula en cada llamada porque la mora crece con los días: no lo caches.

ENDPOINT 04

Confirmar pago

POST /api-multipagos-confirmar-pago

Registra el cobro: genera el comprobante de ingreso, imputa las cuotas, calcula IVA y comisiones. Todo dentro de una transacción de base de datos.

Cuerpo (JSON)

CampoTipoDescripción
payerobjectreqDatos de quien paga en ventanilla. Se guardan como glosa del comprobante.
payer.namestringreqNombre completo.
payer.idNumberstringreqCarnet de identidad.
payer.phonestringreqCelular de contacto.
contractNumberint | stringreqNúmero de contrato.
paymentDetailsarrayreqCuotas cobradas. Al menos un elemento.
paymentDetails[].feeCodeintreqfees[].code del endpoint 03.
paymentDetails[].feeAmountdecimalreqMonto cobrado en USD. Puede ser menor al saldo (pago parcial).
transactionNumberstringreqIdentificador único de Multi Pagos. Es la llave para anular después.
tcdecimalopcTipo de cambio USD→BOB. Si se omite se usa el del último pago registrado.

Ejemplo de llamada

cURL
curl -X POST "https://demo.gosoft.app/api-multipagos-confirmar-pago" \
  -H "Content-Type: application/json" \
  -d '{
        "payer": {
          "name": "Maria Fernanda Gutierrez Rojas",
          "idNumber": "6842193",
          "phone": "71234567"
        },
        "contractNumber": 4821,
        "paymentDetails": [
          { "feeCode": 118742, "feeAmount": 187.75 },
          { "feeCode": 118743, "feeAmount": 135.00 }
        ],
        "transactionNumber": "MP-2026-0000184523",
        "tc": 6.97
      }'
Pago registrado200
{
  "msj": "Success",
  "data": {
    "transactionNumber": 90412,
    "persisted": true
  },
  "code": 200
}
Qué devuelve transactionNumber

No es el número que enviaste: es el número de comprobante interno de GoSoft (voucher). Guárdalo para conciliación. Para anular, en cambio, se usa el transactionNumber original de Multi Pagos.

persisted: true garantiza que el comprobante fue verificado en la base antes de confirmar la transacción.

Pago duplicado

Antes de escribir nada, la API busca un comprobante con el mismo número de transacción, tipo de documento, fondo y fecha. Si lo encuentra rechaza el pago y devuelve el comprobante existente.

Duplicado detectado409
{
  "msj": "Error: Ya existe un pago con ese nro_document/document_type/fondo/fecha",
  "data": {
    "duplicates": [
      {
        "voucher_id": 90412,
        "contract_id": 4821,
        "document_type": "Transaccion",
        "nro_document": "MP-2026-0000184523",
        "fullname": "Maria Fernanda Gutierrez Rojas - CI:6842193 - CEL:71234567",
        "date": "2026-08-20",
        "amount_bob": 2249.53,
        "amount_usd": 322.75
      }
    ]
  },
  "code": 409
}
Reintentos

El 409 es la señal de que el cobro ya está registrado: trátalo como éxito idempotente y toma el voucher_id de duplicates[0].

La detección incluye la fecha, así que un mismo transactionNumber reenviado al día siguiente generaría un segundo comprobante. No reintentes cruzando la medianoche.

Errores de configuración y fallos

Contrato no hallado404
{
  "msj": "Error: No se encontraron contratos",
  "data": { "NULO": null },
  "code": 404
}
Parametros contables500
{
  "msj": "Error: Parametros contables de realty no configurados (uen_id=7)",
  "data": { "NULO": null },
  "code": 500
}
Excepción interna · se revirtió todo500
{
  "msj": "Error interno: Undefined property: stdClass::activo_id",
  "data": {
    "error_class": "ErrorException",
    "error_msg": "Undefined property: stdClass::activo_id",
    "error_file": "RealtyPaymentProcessor.php",
    "error_line": 148,
    "rollback": "ejecutado - 0 cambios"
  },
  "code": 500
}
Atomicidad

Toda la escritura vive dentro de una transacción. Si algo falla —validación, contabilidad o una excepción de PHP— se ejecuta rollback y no queda ninguna fila insertada. El campo rollback lo confirma. Un 500 siempre puede reintentarse con el mismo transactionNumber.

ENDPOINT 05

Anular pago

POST /api-multipagos-anular-pago

Reversa el cobro: anula el comprobante, libera las cuotas imputadas y cancela en cascada las comisiones que ese pago hubiera generado.

Cuerpo (JSON)

CampoTipoDescripción
multipagoTransactionstringreq El transactionNumber enviado al confirmar el pago. Coincidencia exacta.
reasonstringopc Motivo de la anulación. Queda registrado en el comprobante.
userstring | intopc Usuario que anula. Si es un username o id existente, se usa para trazar la cascada de comisiones.

Ejemplo de llamada

cURL
curl -X POST "https://demo.gosoft.app/api-multipagos-anular-pago" \
  -H "Content-Type: application/json" \
  -d '{
        "multipagoTransaction": "MP-2026-0000184523",
        "reason": "Reverso solicitado por el cliente en ventanilla",
        "user": "multipagos"
      }'
Anulado200
{
  "msj": "Success",
  "data": {
    "Control": "Comprobantes de ingresos anulados: 90412 - (1)",
    "Status": true
  },
  "code": 200
}
Nada que anular200
{
  "msj": "Success",
  "data": {
    "Control": "No se encontrato Comprobantes con ese numero de multipagoTransaction",
    "Status": true
  },
  "code": 200
}
Cómo saber si realmente se anuló

Status es true siempre que Control traiga texto, incluso cuando no encontró comprobantes. La verificación fiable es leer Control: si empieza con «Comprobantes de ingresos anulados», la reversión ocurrió.

Anular dos veces la misma transacción es seguro: la segunda llamada no encuentra comprobantes activos y no hace nada.

Qué revierte la cascada

El comprobante de ingreso y sus detalles pasan a anulado · se eliminan las imputaciones a las cuotas, con lo que el saldo vuelve a quedar pendiente · se anulan las comisiones generadas y sus cuentas por pagar · si alguna comisión ya se había pagado al promotor, se anula también ese egreso.


Tabla de errores

Todos llegan como HTTP 200; el valor de la columna code es el del cuerpo de la respuesta.

codemsjCausa y qué hacer
404Error: No código de proyectoFalta proyecto_id en la query string.
404Error: Tiene que enviar: searchType y searchFieldCuerpo incompleto o sin cabecera Content-Type: application/json.
404Error: Tiene que enviar: contractCodeFalta el número de contrato.
404Error: No existe contratoEl contrato no existe, o está anulado, revertido, retenido, devuelto o eliminado.
404Error: Tiene que enviar: payer, contractNumber, paymentDetails, transactionNumberFalta alguno de los cuatro campos obligatorios del cobro.
404Error: No se encontraron contratosEl contractNumber no corresponde a un contrato cobrable.
409Error: Ya existe un pago con ese nro_document...Cobro ya registrado hoy. Idempotente: usa el voucher_id devuelto.
500Error: Usuario de sistema no configuradoFalta el usuario interno que firma los cobros por API. Reportar a GoSoft.
500Error: Parámetros contables de realty no configuradosLa UEN del contrato no tiene cuenta de activo definida. Reportar a GoSoft.
500Error: company_id=... no existe en la DBConfiguración inconsistente de la empresa. Reportar a GoSoft.
500Error interno: ...Excepción no prevista. Se hizo rollback: se puede reintentar tal cual.

Reglas de negocio

Comportamientos del lado de GoSoft que conviene conocer para integrar sin sorpresas.

Tipo de cambio

Las cuotas están en USD y la contabilidad en BOB. El tipo de cambio se resuelve en este orden:

  • paymentDetails[0].tc, si viene dentro del detalle;
  • tc del cuerpo principal;
  • el tipo de cambio del último pago registrado en el sistema;
  • 6.97 como último recurso.

Enviar tc explícitamente es lo recomendable: deja el cobro autocontenido y auditable.

Pagos parciales

Si feeAmount es menor al saldo de la cuota, GoSoft prorratea automáticamente capital e IVA en la misma proporción y deja la cuota con saldo. La glosa del comprobante pasa a decir «Parcial Periodo N» sin que Multi Pagos tenga que indicarlo.

Detalles descartados

Un elemento con feeCode menor o igual a cero, con feeAmount no positivo, o cuyo código de cuota no exista, se ignora en silencio y el resto del pago continúa. Verifica que el monto del comprobante coincida con lo cobrado en caja.

Valores fijos del comprobante

ConceptoValor
Tipo de documentoTransaccion
Número de documento= transactionNumber
Origen del comprobantesource_comp = 3
Fondo de ingresofondo_id = 292
Cuenta de activosegún parámetros de la UEN
Fecha del comprobantefecha del servidor (La Paz)
DescripciónGenerado desde Multi Pagos

Recomendaciones de integración

  • Pide las cuotas (endpoint 03) inmediatamente antes de cobrar: la mora se recalcula cada día.
  • Usa un transactionNumber único e inmutable por cobro; es la única llave para anular.
  • Registra siempre el voucher_id devuelto: es el número que aparece en los reportes contables del cliente.
  • Ante un timeout sin respuesta, reintenta el mismo cobro el mismo día: el 409 evita el duplicado.
  • Evalúa body.code, nunca el status HTTP.