> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pay-me.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ejemplos de integración

> Ejemplos en cURL, Java, C#, Python y Node.js para consumir Pay-me PinPAD.

El sistema central del comercio actúa como **cliente HTTP**. La aplicación Pay-me PinPAD instalada en el Wiseasy P5L actúa como **servidor HTTP** sobre la red local.

Reemplaza `<IP_DEL_PINPAD>` y `<PUERTO>` por la configuración de tu terminal. Estos ejemplos ilustran el contrato HTTP; no son evidencia de pruebas ejecutadas.

<Info>
  Consulta la [referencia de endpoints](/procesamiento-fisico/alignet-transit/referencia-de-endpoints) y el contrato de cada operación antes de adaptar estos ejemplos.
</Info>

## cURL

### Disponibilidad

```bash theme={"system"}
curl --header "Connection: close" "http://<IP_DEL_PINPAD>:<PUERTO>/health"
```

### Autorización

```bash theme={"system"}
curl --header "Connection: close" --request POST "http://<IP_DEL_PINPAD>:<PUERTO>/authorize" \
  --header "Content-Type: application/json" \
  --max-time 110 \
  --data '{
    "operationNumber": "44992528",
    "amount": "100",
    "currency": "604",
    "paymentMethod": "QR",
    "tollData": {
      "plate": "ABC-123",
      "tollPointId": "PEAJE-01",
      "laneId": "VIA-03"
    },
    "additionalFields": {}
  }'
```

Los datos estructurados del peaje van en `tollData`. Se fusionan con `additionalFields`; ante claves coincidentes prevalece `tollData`. Usa `additionalFields` para información complementaria. Para CARD cambia únicamente `paymentMethod` a `"CARD"` en una operación nueva y resuelta de forma independiente; el PinPAD solicitará la tarjeta. En QR, generar el código ya crea la transacción pendiente.

### Consulta del cobro

```bash theme={"system"}
curl --max-time 20 --header "Connection: close" -i "http://<IP_DEL_PINPAD>:<PUERTO>/payments/44992528"
```

Usa `-i` para comprobar `Connection: close` y `Cache-Control: no-store`. Solo los endpoints que devuelven HTTP `202` añaden `Retry-After: 2`; este GET consulta la pasarela y no devuelve `202`.

### Solicitar cancelación

```bash theme={"system"}
curl --header "Connection: close" -i --request POST "http://<IP_DEL_PINPAD>:<PUERTO>/cancel" \
  --header "Content-Type: application/json" \
  --max-time 110 \
  --data '{"operationNumber":"44992528"}'
```

La respuesta informa el resultado de la solicitud de cancelación. Usa el GET del cobro únicamente para recuperarlo ante fallo, timeout o incertidumbre; no existe un GET de cancelación.

| Caso del ejemplo | Resultado esperado | Acción |
| - | - | - |
| Cancelación ejecutada y validada | HTTP `200`, `CANCELLED` con `00`, `16` o `17`. | Reconocer la cancelación sin exigir otra consulta. |
| Cancelación rechazada | HTTP `200`, `DENIED`, incluido el código `14`. | No registrar cancelación exitosa; consultar el cobro original. |
| Cancelación no ejecutada | Respuesta de error, sin valores específicos definidos. | No registrar éxito; consultar el cobro original. |
| Resultado de otra operación | El servicio devuelve un error de validación. | Conservar la referencia solicitada y consultar su cobro. |
| Timeout o resultado incierto | Sin cancelación confirmada; `PENDING` con `P22` sigue pendiente. | Consultar el cobro original; en HTTP `202`, esperar `Retry-After: 2`. |
| Cancelación fallida y cobro CARD `transaction.state: "AUTORIZADO"` en una consulta `200/00` | El cobro original está aprobado. | Evaluar `POST /reversals` con la referencia original; consultar el extorno cuando corresponda. |

En `/cancel`, `DENIED/14` indica abandono por cierre de autorización en curso; `15` indica abandono con intento (QR cancelado). Ninguno confirma cancelación aprobada ni rechazo típico de tarjeta. `CANCELLED` con `16` o `17` es éxito equivalente, validado por el servicio. Si el cobro QR aparece `transaction.state: "AUTORIZADO"` en una consulta `200/00`, conserva el pago aprobado y registra la discrepancia. Ni `/cancel` ni refund devuelven pagos QR; no uses `/reversals` para QR.

## Java 11 o posterior

```java theme={"system"}
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public final class PinpadClient {
    private final String baseUrl;

    public PinpadClient(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    // Recibe JSON serializado con la biblioteca de tu aplicación.
    public HttpResponse<String> authorize(String json) throws Exception {

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/authorize"))
            .header("Content-Type", "application/json")
            .timeout(Duration.ofSeconds(110))
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();

        return HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
            .build().send(request, HttpResponse.BodyHandlers.ofString());
    }

    public HttpResponse<String> query(String operationNumber) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(baseUrl + "/payments/" +
                URLEncoder.encode(operationNumber, StandardCharsets.UTF_8)))
            .timeout(Duration.ofSeconds(20))
            .GET()
            .build();

        return HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
            .build().send(request, HttpResponse.BodyHandlers.ofString());
    }
}
```

En Java, cada envío crea su cliente y usa HTTP/1.1; el servidor responde `Connection: close`. No configures reutilización de conexiones.

## C# con .NET

```csharp theme={"system"}
using System.Net.Http.Json;

public sealed class PinpadClient
{
    private readonly HttpClient _http;

    public PinpadClient(string baseUrl)
    {
        _http = new HttpClient
        {
            BaseAddress = new Uri(baseUrl),
            Timeout = TimeSpan.FromSeconds(110)
        };
        _http.DefaultRequestHeaders.ConnectionClose = true;
    }

    public async Task<(int StatusCode, string Body)> AuthorizeAsync(
        string operationNumber, string amount, string currency, string paymentMethod)
    {
        var payload = new
        {
            operationNumber,
            amount,
            currency,
            paymentMethod
        };

        using var response = await _http.PostAsJsonAsync("/authorize", payload);
        return ((int)response.StatusCode, await response.Content.ReadAsStringAsync());
    }

    public async Task<(int StatusCode, string Body)> QueryAsync(string operationNumber)
    {
        using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(20));
        using var response = await _http.GetAsync(
            $"/payments/{Uri.EscapeDataString(operationNumber)}", timeout.Token);
        return ((int)response.StatusCode, await response.Content.ReadAsStringAsync());
    }
}
```

## Python con requests

```python theme={"system"}
import requests
from urllib.parse import quote

BASE_URL = "http://<IP_DEL_PINPAD>:<PUERTO>"


def authorize(operation_number, amount, currency, payment_method):
    payload = {
        "operationNumber": operation_number,
        "amount": amount,
        "currency": currency,
        "paymentMethod": payment_method,
    }
    response = requests.post(
        f"{BASE_URL}/authorize",
        json=payload,
        headers={"Connection": "close"},
        timeout=110,
    )
    return response.status_code, response.json()


def query_status(operation_number):
    response = requests.get(
        f"{BASE_URL}/payments/{quote(operation_number, safe='')}",
        headers={"Connection": "close"},
        timeout=20,
    )
    return response.status_code, response.json()



```

## Node.js 18 o posterior

```javascript theme={"system"}
const BASE_URL = "http://<IP_DEL_PINPAD>:<PUERTO>";

async function authorize(operationNumber, amount, currency, paymentMethod) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 110_000);

  try {
    const response = await fetch(`${BASE_URL}/authorize`, {
      method: "POST",
      headers: { "Content-Type": "application/json", "Connection": "close" },
      body: JSON.stringify({ operationNumber, amount, currency, paymentMethod }),
      signal: controller.signal,
    });

    return {
      statusCode: response.status,
      body: await response.json(),
      retryAfter: response.headers.get("retry-after"),
    };
  } finally {
    clearTimeout(timeout);
  }
}

async function queryStatus(operationNumber) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 20_000);
  try {
    const response = await fetch(
      `${BASE_URL}/payments/${encodeURIComponent(operationNumber)}`,
      { headers: { "Connection": "close" }, signal: controller.signal },
    );
    return { statusCode: response.status, body: await response.json() };
  } finally {
    clearTimeout(timeout);
  }
}
```

## Reglas para cualquier lenguaje

* Persiste `operationNumber` antes del `POST`.
* Envía `operationNumber` como String de 1–64 caracteres sin espacios; evita `/` para consultarlo por ruta. `amount` acepta entero o String numérico positivo. Moneda y método son opcionales (`604` y `CARD`); el método no distingue mayúsculas.
* Abre una conexión por petición, sin keep-alive; todas las respuestas usan `Connection: close`.
* Configura 110 segundos para los POST, por encima de la espera aproximada de 90 segundos. Cubre los 12–15 segundos de consulta del cobro; los ejemplos usan 20 segundos.
* Conserva por separado el código HTTP y el JSON.
* Confirma autorización con HTTP `200`, `success: true`, `resultCode: "00"` y `result.state: "AUTORIZADO"`. En consulta `200/00`, interpreta `transaction.state`: `00` solo significa encontrada.
* Reconoce cancelación validada con `CANCELLED` y `00`, `16` o `17`. `DENIED/14` y HTTP `200` aislado no confirman éxito.
* Ante cancelación fallida o incierta, consulta el cobro original. Una consulta fallida o incierta (`200/03`, `404/05`, `503`, `504`) no permite otro cobro ni confirma rechazo.
* Una falla de conexión no garantiza recepción de `/cancel` ni cierre de pantalla.
* Los ciclos repiten solo el GET correspondiente; no reenvían `/cancel` ni generan otra venta.
* Ante timeout, desconexión o fallo del backend, muestra **Cancelación no confirmada** y consulta el estado.
* No repitas un extorno exitoso o en curso. Tras `DENIED`, `UNKNOWN` o `ERROR` finales, un nuevo POST puede ejecutar otro intento; consulta y decide antes, sin reintento automático ante incertidumbre.
* No sustituyas un resultado confirmado por un `202 WAITING_RESULT` tardío de `/authorize`.
* Autorización `202` usa `WAITING_RESULT` y `result: null`. La consulta del cobro no tiene `status` propio ni devuelve `202`. Extorno `UNKNOWN` devuelve `502` y es final localmente; revisa si se ejecutó sin asumir éxito.
* El cuerpo compacto de extorno `UNKNOWN` utiliza `message`.
* Conserva los códigos SDK; en cancelación y extorno, `P22` se clasifica como `PENDING`. No extrapoles el modelo propio a la autorización o consulta del cobro.
* Respeta `Retry-After: 2` en HTTP `202`.
* Serializa los cobros dirigidos al mismo terminal.
* No registres datos sensibles de tarjeta.

La solicitud y consulta de extorno están en [Extorno de una venta CARD aprobada](/procesamiento-fisico/alignet-transit/extorno).

<Warning>
  Los ejemplos se concentran en el contrato HTTP. En producción, serializa JSON con la biblioteca estándar del lenguaje, valida las respuestas y persiste el estado antes de ejecutar una acción de negocio irreversible.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.