---
title: Integración del método de pago
slug: custom-integrations
description: Learn how to integrate an e-commerce platform with ADDI using this comprehensive document. It covers authentication, HTTP resource usage, handling communication failures, and temporary issues. Find out how ADDI offers staging and production environments f
createdAt: 2023-09-27T15:49:10.171Z
---

A continuación, se describe el flujo de comunicación y las reglas de negocio para integrar Addi como método de pago en un e-commerce.

**Notas generales:**

- **Ambientes:** Addi cuenta con entornos de *Staging* y *Producción*. Las credenciales de Staging se solicitan a [soporte\_aliados@addi.com](mailto\:soporte_aliados@addi.com). Las de Producción son únicas e intransferibles y se entregan tras la certificación de la integración.
- **Idempotencia:** Los recursos HTTP de la API son idempotentes (garantizan que un crédito se origina solo una vez) y permiten reintentos seguros en caso de fallos 5xx.
- **Moneda y Documento:** Solo se opera en Pesos Colombianos (COP) y con personas naturales (Tipo de documento CC).

### Flujo de la Integración

![](https://archbee-doc-uploads.s3.amazonaws.com/MCXyezaW9WrT4cGwqpD-5/dzI2xmCUTCCDCEgbCfzy7_oun9apuro35ulna6id6etflujoe-commerce-spa.png)

***

### 1. Verificación de Montos y Descuentos (Config Endpoint)

En el checkout, al listar los métodos de pago, se debe consumir este endpoint para determinar si Addi debe estar disponible y si aplica algún descuento visual.

- **Endpoint:** GET [`https://channels-public-api.addi.com/allies/ally_slug/config?requestedamount=xxxx`](https://channels-public-api.addi.com/allies/ally_slug/config?requestedamount=xxxx) *(Reemplazar ally\_slug y xxxx con los datos correspondientes del comercio).*
- **Ejemplo&#x20;**

:::ApiMethodV2
```json
{
  "name": "Get Config",
  "method": "GET",
  "url": "https://channels-public-api.addi.com/allies/ally_slug/config?requestedamount=xxxx",
  "description": "Obtener configuración del comercio con el ally slug y el monto para solicitar el crédito",
  "tab": "examples",
  "examples": {
    "languages": [
      {
        "id": "TYeIVXpQzkUc2pmF0krK9",
        "language": "curl",
        "code": "curl --location 'https://channels-public-api.addi.com/allies/miallyslug/config?requestedamount=80000' \\\n--header 'Accept: application/json' \\\n--header 'Content-Type: application/json'",
        "customLabel": ""
      }
    ],
    "selectedLanguageId": "TYeIVXpQzkUc2pmF0krK9"
  },
  "results": {
    "languages": [
      {
        "id": "DUzMcyVr0Xg2RCwFqkbLV",
        "language": "200",
        "code": "{\n  \"minAmount\": 10000,\n  \"maxAmount\": 10000000,\n  \"policy\": {\n    \"discount\": 0.2,\n    \"productType\": \"ADDI_PAGO\",\n    \"policyMaxAmount\": 800001,\n    \"isVisible\": true\n  },\n  \"widgetConfig\": {\n    \"widgetVersion\": \"ADDI_TEMPLATE_FLEX\",\n    \"widgetShowPreapproval\": true\n  },\n  \"checkoutConfig\": {\n    \"version\": \"BNPL_BNPN_V1\"\n  },\n  \"isActiveAlly\": true,\n  \"isActivePayNow\": true\n}"
      },
      {
        "id": "VB5gFXfldE-TUJOGX8vVX",
        "language": "404",
        "code": "{\n  \"code\": \"007-017\",\n  \"message\": \"The ally does not exist.\"\n}"
      }
    ],
    "selectedLanguageId": "DUzMcyVr0Xg2RCwFqkbLV"
  },
  "request": {
    "pathParameters": [],
    "queryParameters": [
      {
        "name": "ally_slug",
        "kind": "required",
        "type": "string",
        "description": "Identificador del comercio proporcionado por Addi.",
        "": "Identificador del comercio proporcionado por Addi."
      },
      {
        "name": "requestedamount",
        "kind": "required",
        "type": "number",
        "description": "Monto total de la compra para la solicitud del credito con Addi.",
        "": "required"
      }
    ],
    "headerParameters": [],
    "bodyDataParameters": [],
    "formDataParameters": []
  },
  "currentNewParameter": {
    "label": "Query Parameter",
    "value": "queryParameters"
  },
  "hasTryItOut": false,
  "autoGeneratedAnchorSlug": "get-config",
  "legacyHash": "BuMHkwhYtHP3IhHtktvxp"
}
```
:::

- **Manejo de topes:** Si el monto del carrito está fuera del rango permitido (minAmount y maxAmount), **no se recomienda ocultar el método de pago**. Lo ideal es deshabilitarlo y mostrar un *disclaimer* (ej. "Aplica solo para compras entre X y Y"). Esta práctica es directa, resuelve dudas y fomenta que el cliente agregue más productos al carrito para alcanzar el monto mínimo.
- **Manejo de descuentos:** El valor de discount es puramente visual (ej. 0.02 = 2%). **No se debe modificar el valor total aplicando el descuento antes de enviar la petición.** Se debe enviar el monto original en la solicitud de transacción; Addi se encarga de aplicar el descuento internamente según la negociación.

***

### 2. Autenticación

La seguridad se maneja vía Addi Auth con autenticación OAuth(IdaaS). Se requiere obtener un JSON Web Token (JWT) antes de consumir los demás endpoints.

- **Documentación:&#x20;**[API Auth Docs](https://api-docs.addi-staging.com/auth/#/authentication)
- **Mejor práctica:&#x20;**&#x53;e debe generar **un token nuevo por cada intento de transacción**. Reutilizar un mismo token para múltiples transacciones simultáneas puede causar errores debido a tiempos de expiración cruzados.

:::Iframe{iframeHeight="0" code="<!--<div contenteditable=&#x22;true&#x22; class=&#x22;ab-code-editor code-editor break-inside-avoid max-w-full w-full grow relative flex flex-col&#x22;><div class=&#x22;flex w-full bg-gray-700 justify-between break-after-avoid rounded-t-md&#x22;><div class=&#x22;flex w-300 flex-1 overflow-x-auto&#x22;><div class=&#x22;flex items-center&#x22;><div class=&#x22;flex&#x22; data-rbd-droppable-id=&#x22;language-droppable&#x22; data-rbd-droppable-context-id=&#x22;56419&#x22;><div class=&#x22;flex&#x22; data-rbd-draggable-context-id=&#x22;56419&#x22; data-rbd-draggable-id=&#x22;kNklSqK8_I_LfzXbQBH41&#x22;><div class=&#x22;flex items-center py-2.5 px-4 cursor-pointer text-white bg-gray-800 rounded-t&#x22;>Credentials</div></div></div></div></div></div><div class=&#x22;bg-gray-800 w-full h-full flex-col rounded-b-md shadow overflow-y-auto p-4 w-full break-inside-avoid&#x22; style=&#x22;color:#CCC;font-size:14px;&#x22; id=&#x22;credentials&#x22;><b>Client ID=</b>{{CLIENT_ID}}&#xA;<b>Client Secret=</b>{{CLIENT_SECRET}}</div></div>&#xA;&#xA;<script>&#xA;(() => {&#xA;  const credentials = document.querySelector('#credentials');&#xA;  const newText = credentials.innerHTML.replace('{{CLIENT_ID}}', addiClientId).replace('{{CLIENT_SECRET}}', addiClientSecret);  &#xA;  credentials.innerHTML = newText;&#xA;})()&#xA;</script>-->&#xA;<div id=&#x22;credentials&#x22; data-slate-node=&#x22;element&#x22; data-slate-void=&#x22;true&#x22; id=&#x22;S61Gk4FtjxYi_ydgghEu_&#x22; class=&#x22;mb-8 ab-plugin-wrapper transition delay-75 w-full ease-out&#x22;><div class=&#x22;flex-col&#x22;><div contenteditable=&#x22;false&#x22; class=&#x22;ab-code-editor code-editor select-none&#x22; style=&#x22;padding-top: 46px;&#x22;><div class=&#x22;ab-code-editor-language-bar&#x22;><div class=&#x22;hidden xl:block&#x22;></div><div class=&#x22;flex justify-between&#x22;><div class=&#x22;flex w-300 flex-1 overflow-x-auto&#x22;><div class=&#x22;flex items-center&#x22;><div class=&#x22;flex&#x22; data-rbd-droppable-id=&#x22;language-droppable&#x22; data-rbd-droppable-context-id=&#x22;1&#x22;><div class=&#x22;flex&#x22; data-rbd-draggable-context-id=&#x22;1&#x22; data-rbd-draggable-id=&#x22;XgP2zuaCUGUKsKd_rBran&#x22;><div class=&#x22;ab-code-language-label ab-selected&#x22;>Credentials</div></div></div></div></div></div></div><div class=&#x22;ab-prism-editor pt-2 w-full break-inside-avoid&#x22;><div class=&#x22;ab-prism-editor-wrap w-full h-full read-only&#x22;><div data-overlayscrollbars-initialize=&#x22;&#x22; class=&#x22;pb-2 px-4 flex flex-col max-h-full h-full min-h-[36px]&#x22; data-overlayscrollbars=&#x22;host&#x22;><div class=&#x22;os-size-observer&#x22;><div class=&#x22;os-size-observer-listener ltr&#x22;></div></div><div data-overlayscrollbars-contents=&#x22;&#x22; data-overlayscrollbars-viewport style=&#x22;margin-right: 0px; margin-bottom: 0px; margin-left: 0px; top: 0px; right: 0px; left: 0px; padding: 0px; width: calc(100% + 0px);&#x22;><div class=&#x22;ab-react-simple-code-editor&#x22; style=&#x22;position: relative; text-align: left; box-sizing: border-box; padding: 0px;&#x22;><div class=&#x22;language-plain ab-pre&#x22; aria-hidden=&#x22;true&#x22; style=&#x22;margin: 0px; border: 0px; background: none; box-sizing: inherit; display: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; line-height: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; word-break: keep-all; overflow-wrap: break-word; white-space: pre; position: relative; pointer-events: none; padding: 0px;&#x22;>Client ID={{CLIENT_ID}}&#xA;Client Secret={{CLIENT_SECRET}}<br></div><textarea class=&#x22;npm__react-simple-code-editor__textarea&#x22; readonly=&#x22;&#x22; autocapitalize=&#x22;off&#x22; autocomplete=&#x22;off&#x22; autocorrect=&#x22;off&#x22; spellcheck=&#x22;false&#x22; data-gramm=&#x22;false&#x22; style=&#x22;margin: 0px; border: 0px; background: none; box-sizing: inherit; display: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; line-height: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; word-break: keep-all; overflow-wrap: break-word; white-space: pre; position: absolute; top: 0px; left: 0px; height: 100%; min-width: 100%; resize: none; color: inherit; overflow: hidden; -webkit-font-smoothing: antialiased; -webkit-text-fill-color: transparent; padding: 0px; width: 629px;&#x22;>Client ID={{CLIENT_ID}}&#xA;Client Secret={{CLIENT_SECRET}}</textarea><style>&#xA;/**&#xA; * Reset the text fill color so that placeholder is visible&#xA; */&#xA;.npm__react-simple-code-editor__textarea:empty {&#xA;  -webkit-text-fill-color: inherit !important;&#xA;}&#xA;&#xA;/**&#xA; * Hack to apply on some CSS on IE10 and IE11&#xA; */&#xA;@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {&#xA;  /**&#xA;    * IE doesn't support '-webkit-text-fill-color'&#xA;    * So we use 'color: transparent' to make the text transparent on IE&#xA;    * Unlike other browsers, it doesn't affect caret color in IE&#xA;    */&#xA;  .npm__react-simple-code-editor__textarea {&#xA;    color: transparent !important;&#xA;  }&#xA;&#xA;  .npm__react-simple-code-editor__textarea::selection {&#xA;    background-color: #accef7 !important;&#xA;    color: transparent !important;&#xA;  }&#xA;}&#xA;</style></div></div><div class=&#x22;os-scrollbar os-scrollbar-horizontal os-theme-dark os-scrollbar-auto-hide os-scrollbar-handle-interactive os-scrollbar-cornerless os-scrollbar-unusable os-scrollbar-auto-hide-hidden&#x22;><div class=&#x22;os-scrollbar-track&#x22;><div class=&#x22;os-scrollbar-handle&#x22; style=&#x22;width: 100%;&#x22;></div></div></div><div class=&#x22;os-scrollbar os-scrollbar-vertical os-theme-dark os-scrollbar-auto-hide os-scrollbar-handle-interactive os-scrollbar-cornerless os-scrollbar-unusable os-scrollbar-auto-hide-hidden&#x22;><div class=&#x22;os-scrollbar-track&#x22;><div class=&#x22;os-scrollbar-handle&#x22; style=&#x22;height: 100%;&#x22;></div></div></div></div></div></div></div><div data-slate-spacer=&#x22;true&#x22; style=&#x22;height: 0px; color: transparent; outline: none; position: absolute;&#x22;><span data-slate-node=&#x22;text&#x22;><span data-slate-leaf=&#x22;true&#x22;><span data-slate-zero-width=&#x22;z&#x22; data-slate-length=&#x22;0&#x22;>﻿</span></span></span></div></div></div>&#xA;<script>&#xA;    (() => {&#xA;    const credentials = document.querySelector('#credentials');&#xA;    const newText = credentials.innerHTML.replaceAll('{{CLIENT_ID}}', addiClientId).replaceAll('{{CLIENT_SECRET}}', addiClientSecret);  &#xA;    credentials.innerHTML = newText;&#xA;    })()&#xA;</script>"}

:::

***

### 3. Creación de la Aplicación (Transacción)

Este endpoint se utiliza para iniciar el proceso de pago. La transacción se creará inicialmente en estado "pendiente".

- **Documentación:** [Create Online Loan Application](https://api-docs.addi-staging.com/integration/#/online%20application/createOnlineLoanApplication)
- **Order ID:** Es posible usar cualquier formato (UUID, números, letras), pero **debe ser único por cada intento de compra**. Si una persona falla o abandona el proceso y vuelve a intentar con el mismo carrito, se debe generar y enviar un Order ID distinto.
- **Redirección (Status 301):** El endpoint devuelve un estado HTTP 301 (sin body) con un header Location que contiene la URL de Addi.

:::BlockQuote
**🛑 Importante sobre la redirección:** Muchos *frameworks* siguen el redireccionamiento **301** de manera automática, lo que suele resultar en pérdida de parámetros o en un falso status **200**. Para un comportamiento estable, se sugiere **desactivar la redirección automática** en el código, capturar el status 301 y usar la URL provista para redirigir al cliente manualmente.
:::

***

### 4. Reporte de Resultado (Webhook / Callback) y Redirección

Addi notificará al servidor del comercio el estado final de la transacción de forma asíncrona, previo a devolver al cliente al e-commerce. Los tiempos de decisión toman generalmente entre 20 segundos y 10 minutos (depende de cada usuario).

- **Configuración de URLs:** Tanto la URL de respuesta para el Webhook como la URL de redirección del cliente **no son estáticas ni se configuran en un panel global**. Deben definirse dinámicamente por cada transacción enviando las llaves **`callbackUrl`** y **`redirectionUrl`** dentro del *body* (JSON) de la petición al momento de crear la transacción (descrito en el punto 3).
- **Documentación:** [Online Application Callback](https://api-docs.addi-staging.com/integration/#/online%20application%20callback/)
- **Estados posibles (Enum):** approved, rejected, declined, abandoned.
- **Redirección dinámica (redirectionUrl):** Una vez finalizado el proceso en Addi, el cliente es retornado a la tienda. Esta URL debe ser dinámica: si el estado fue approved, se debe dirigir al cliente a la página de agradecimiento (*Thank You Page*). Para cualquier otro estado, se le debe redirigir a la página del carrito para permitir un nuevo intento de compra con Addi o con otro método de pago.
- **Monto aprobado:** Addi **no realiza aprobaciones parciales**. El campo approvedAmount siempre será 0, a menos que la transacción sea aprobada; en dicho escenario, el monto será exactamente igual al valor solicitado.
- **Tiempo máximo de expiración de una transacción:** 2 horas.
- **Requisitos del Webhook (callbackUrl):**
  - Debe contar con autenticación básica (*Basic Auth*). Las credenciales deben ser obtenidas del portal de aliados y configuradas en su sistema previo al paso a Producción.
    - Estas credenciales las pueden encontrar en la sección de integraciones, en el modulo Credenciales de notificación

::Image[]{src="https://api.archbee.com/api/optimize/MCXyezaW9WrT4cGwqpD-5/TO7dBba1NUV_nOpMu3Eql-20260528-154012.png" size="90" width="2806" height="1555" darkWidth="2806" darkHeight="1555" position="center" showCaption="false"}

:::Paragraph{listStyleType="disc" indent="2"}
Para que los sistemas de Addi marquen la notificación de la transacción como exitosa, **el endpoint del comercio debe responder con un status HTTP 200 y retornar exactamente el mismo cuerpo (body) en formato JSON** que recibió en la petición.
:::

- **Reintentos:** En caso de que el Webhook falle o la comunicación no se pueda establecer, Addi marcará el envío como incompleto y realizará reintentos cada 30 minutos durante 24 horas. Si el problema persiste, el equipo de soporte puede forzar el envío de forma semi-automática.

***

### 5. Cancelaciones / Reembolsos

Proveemos un endpoint dedicado para reversar compras previamente aprobadas (requiere autenticación con JWT).

- **Documentación:&#x20;**[Online application cancellation](https://api-docs.addi-staging.com/integration/#/online%20application%20cancellation)
- **Total vs Parcial:** Si se envía un monto que coincide con el valor original de la transacción, el sistema asumirá una cancelación total. Si el monto es menor, se procesará como una cancelación parcial.
- **Límites:** Solo se permite realizar **una (1)** solicitud de cancelación por transacción, ya sea total o parcial.
- **Soporte:** En caso de requerir una corrección o necesitar más de una cancelación parcial sobre una misma orden, se debe realizar una solicitud formal contactando al equipo de soporte a través de [soporte\_aliados@addi.com](mailto\:soporte_aliados@addi.com).

***

