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

# PayCode Connect · C#/.NET

> Conecta tu aplicación de escritorio con una terminal PayCode usando el SDK oficial de .NET.

## Introducción

PayCode Connect es el SDK que permite que una aplicación se conecte de forma directa con una terminal punto de venta física (TPV) de PayCode, sin pasar por un servidor intermedio. La comunicación ocurre en una red punto a punto (P2P), es decir, directamente entre el dispositivo del negocio y la terminal, sin intermediarios. Esto permite enviar comandos y recibir eventos en tiempo real: iniciar cobros, leer códigos de barras, controlar el estado de la conexión, entre otros.

Esta guía cubre la versión para **C# y .NET**. Está pensada para negocios que desarrollan aplicaciones de escritorio en Windows (por ejemplo con WPF, WinUI o MAUI) y que necesitan controlar una terminal PayCode desde su propio software de punto de venta.

<Info>
  Este SDK se distribuye a través de NuGet, el gestor de paquetes de .NET. Si nunca has usado NuGet, solo es la forma en la que se instalan librerías en proyectos de Windows, similar a como npm funciona para JavaScript.
</Info>

## Instalación

El paquete está publicado en [nuget.org](https://www.nuget.org/packages/PaycodeConnect) bajo el nombre `PaycodeConnect`.

```bash theme={null}
dotnet add package PaycodeConnect
```

También puedes instalarlo desde la Package Manager Console de Visual Studio o agregando la referencia directamente en tu archivo de proyecto:

```xml theme={null}
<PackageReference Include="PaycodeConnect" Version="1.2.4" />
```

El paquete requiere .NET 6.0 o superior.

## Configuración inicial (Setup)

Antes de poder conectar con una terminal, es necesario inicializar el SDK. Esto se hace llamando al método `Setup()`, el cual crea un nodo local en el equipo donde corre la aplicación. Este paso solo debe ejecutarse **una vez**, normalmente durante el arranque de la app.

```csharp theme={null}
using PaycodeConnectSDK;

PaycodeConnect node = new PaycodeConnect();
node.Setup();

// Con un callback de finalización
node.Setup((resultCode) =>
{
    // ResultCode.Success si todo salió bien
});
```

## Conexión con la terminal

Para vincular tu aplicación con una terminal física, se genera un ticket de conexión y se muestra como código QR. El SDK incluye un generador de QR integrado que retorna una imagen PNG codificada en base64:

```csharp theme={null}
string ticket = node.GenerateTicket();
string qrBase64 = node.GenerateQRCodeBase64(ticket);
```

Ese código base64 se puede mostrar en cualquier framework de UI. Por ejemplo, en WinUI o UWP:

```csharp theme={null}
// Ejemplo con WinUI / UWP
qrImage.Source = Base64ToImage(qrBase64);

BitmapImage Base64ToImage(string base64)
{
    byte[] bytes = Convert.FromBase64String(base64);
    using MemoryStream ms = new MemoryStream(bytes);
    BitmapImage img = new BitmapImage();
    img.SetSource(ms.AsRandomAccessStream());
    return img;
}
```

Una vez que la terminal PayCode escanea el código QR, la conexión queda establecida.

### Reconexión

El SDK guarda automáticamente el último ticket usado. Esto permite reconectar con la última terminal conocida sin necesidad de volver a escanear un QR:

```csharp theme={null}
node.ReconnectToLastNode((resultCode) =>
{
    // ResultCode.Success si la reconexión fue exitosa
});
```

## Recepción de eventos

PayCode Connect expone eventos de C# para el estado de la conexión, la presencia de la terminal, el ciclo de vida de una transacción con chip (EMV) y la lectura de códigos de barras.

<Note>
  Todos los eventos se entregan sobre el `SynchronizationContext` capturado durante `Setup()`, por lo que es seguro usarlos para actualizar la interfaz en frameworks como WPF, WinUI o MAUI.
</Note>

### Cambio de conexión

Se dispara cuando cambia el estado de conexión con la terminal.

```csharp theme={null}
node.ConnectionChange += (sender, e) =>
{
    bool isConnected = e.Connected;
};
```

### Estado EMV

Se dispara a lo largo de todo el ciclo de vida de una transacción EMV, después de llamar a `StartEMV()`. El callback recibe un objeto `EMVData` cuyos campos se llenan según el estado actual:

| Estado         | Campos                     | Descripción                                                            |
| -------------- | -------------------------- | ---------------------------------------------------------------------- |
| `NotStarted`   | `State`                    | El proceso EMV aún no ha iniciado                                      |
| `AwaitingCard` | `State`                    | La terminal está esperando que se presente una tarjeta                 |
| `Processing`   | `State`                    | La transacción se está procesando                                      |
| `Error`        | `State`, `Message`, `Code` | La transacción falló                                                   |
| `Success`      | `State`, `Transaction`     | La transacción se completó exitosamente                                |
| `Reversal`     | `State`, `Message`, `Code` | Resultado de una reversión: `Code` es `0` si fue exitosa, `1` si falló |

Si se envió un valor de `metadata` al llamar a `StartEMV`, ese mismo valor estará disponible en el campo `Metadata` de cada evento `EMVData` de esa transacción.

```csharp theme={null}
node.EMVStateChange += (sender, e) =>
{
    EMVData data = e.Data;

    // Acceder a la metadata que se envió en StartEMV
    var meta = data.GetMetadata<OrderMetadata>();
    if (meta != null)
    {
        string orderId = meta.OrderId;
        int table = meta.Table;
    }

    switch (data.State)
    {
        case EMVState.AwaitingCard:
            statusText.Text = "Present card on terminal...";
            break;
        case EMVState.Processing:
            statusText.Text = "Processing...";
            break;
        case EMVState.Success:
            statusText.Text = $"Approved! Auth: {data.Transaction?.Authorization}";
            break;
        case EMVState.Error:
            statusText.Text = $"Error {data.Code}: {data.Message}";
            break;
        case EMVState.Reversal:
            statusText.Text = data.Code == 0 ? "Reversal successful" : "Reversal failed";
            break;
    }
};
```

### Presencia

Se dispara con `false` cuando los mensajes dejan de llegar a la terminal conectada, aunque la conexión no se haya perdido por completo. Se dispara con `true` cuando la terminal vuelve a responder. Este evento corresponde al ícono amarillo de advertencia que se muestra en la terminal.

```csharp theme={null}
node.PresenceChange += (sender, e) =>
{
    bool hasPresence = e.Presence;
};
```

### Escáner

Una terminal conectada puede usarse como lector de códigos de barras. Para habilitarlo, primero se debe configurar la propiedad `ScannerPrefixes` (ver [Configurar capacidades](#configurar-capacidades)). Una vez configurado, este evento se dispara cada vez que la terminal escanea un código que coincide con esos prefijos.

```csharp theme={null}
node.TerminalScanner += (sender, e) =>
{
    string barcode = e.Content;
};
```

### Mensajes perdidos

Este evento se dispara en dos escenarios distintos:

1. **Con mensajes** — después de llamar a `RequestLostMessages()`, entrega un arreglo de `EMVData` que no fueron confirmados.
2. **Con una bandera** — después de llamar a `RequestHasLostMessages()`, entrega un booleano que indica si existen mensajes pendientes.

```csharp theme={null}
node.LostMessagesEvent += (sender, e) =>
{
    if (e.Messages != null)
    {
        foreach (EMVData msg in e.Messages)
        {
            // Procesar cada mensaje perdido
        }
    }

    if (e.HasLostMessages.HasValue)
    {
        bool hasLost = e.HasLostMessages.Value;
    }
};
```

## Comandos

Una vez establecida la conexión, la terminal puede controlarse de forma remota a través de los siguientes comandos:

| Comando                                 | Descripción                                 |
| --------------------------------------- | ------------------------------------------- |
| `StartEMV(amount, emvType?, metadata?)` | Inicia una transacción                      |
| `ReverseTransaction(folio)`             | Revierte una transacción previa             |
| `PrintTransaction(folio)`               | Reimprime el comprobante de una transacción |
| `SetCapabilities(capabilities)`         | Configura las capacidades de la terminal    |
| `Authorize(email, password)`            | Autentica contra la terminal                |
| `SetPin(payload)`                       | Establece un PIN en la terminal             |
| `AuthorizePin(pin)`                     | Autentica usando un PIN                     |
| `Logout()`                              | Cierra sesión en la terminal                |
| `RequestLostMessages()`                 | Solicita los mensajes no confirmados        |
| `RequestHasLostMessages()`              | Verifica si existen mensajes pendientes     |
| `ClearLostMessages()`                   | Limpia todos los mensajes pendientes        |
| `Shutdown(restart?)`                    | Desconecta y apaga el nodo                  |

Todos los comandos retornan un `ResultCode` y aceptan opcionalmente un `CompletionCallback` para la operación asíncrona interna.

### Iniciar EMV

Inicia una transacción EMV en la terminal conectada. El parámetro `amount` es un string en pesos mexicanos (MXN). El parámetro opcional `emvType` toma por defecto el valor `EMVType.Combined` y puede configurarse como `Emv` (Visa/Mastercard), `Amex` o `Combined` (detección automática). Para dar seguimiento al ciclo de vida de la transacción hay que suscribirse al evento `EMVStateChange`.

El parámetro opcional `metadata` acepta cualquier objeto serializable, el cual será regresado en cada evento `EMVData` de esa transacción. Esto es útil para asociar contexto propio de la aplicación (por ejemplo, el id de una orden o el número de una mesa) sin necesidad de manejar estado externo.

También existe una variante `StartEMV(amount, emvType, callback)` para pasar un callback sin necesidad de especificar metadata.

```csharp theme={null}
node.StartEMV("150.00");

// Especificando el tipo de EMV
node.StartEMV("150.00", EMVType.Amex);

// Con callback (sin necesidad de pasar null en metadata)
node.StartEMV("150.00", EMVType.Combined, (code) =>
{
    // ResultCode.Success si todo salió bien
});

// Con metadata
node.StartEMV("150.00", EMVType.Combined, new { orderId = "abc-123", table = 5 });

// Con metadata y callback
node.StartEMV("150.00", EMVType.Combined, new { orderId = "abc-123", table = 5 }, (code) =>
{
    // ResultCode.Success si todo salió bien
});
```

### Revertir transacción

Revierte una transacción previamente completada usando su número de folio. El resultado de la reversión se recibe en el evento `EMVStateChange`.

```csharp theme={null}
node.ReverseTransaction("123456789");
```

### Reimprimir transacción

Reimprime el comprobante de una transacción previamente completada, usando su número de folio.

```csharp theme={null}
node.PrintTransaction("123456789");
```

### Configurar capacidades

Configura las capacidades de la terminal. Las opciones disponibles son:

<Warning>
  Cambiar `ForceRemoteControl` o `DisableManualPayments` modifica cómo opera la terminal de inmediato, incluso en producción. Confirma con el negocio antes de habilitarlas.
</Warning>

| Opción                     | Tipo        | Descripción                                                                              |
| -------------------------- | ----------- | ---------------------------------------------------------------------------------------- |
| `ScannerPrefixes`          | `string[]?` | Prefijos de código de barras que activan el evento de escáner                            |
| `PingInternetConnectivity` | `bool?`     | Habilita la verificación de conectividad a internet                                      |
| `ForceRemoteControl`       | `bool?`     | Fuerza a la terminal a entrar en modo de control remoto                                  |
| `DisableManualPayments`    | `bool?`     | Deshabilita la captura manual de pagos en la terminal                                    |
| `LostMessagesSyncMode`     | `string?`   | `"Auto"` o `"Manual"`: controla el comportamiento de sincronización de mensajes perdidos |

```csharp theme={null}
node.SetCapabilities(new Capabilities
{
    ScannerPrefixes = new[] { "PREFIX1", "PREFIX2" },
    ForceRemoteControl = true,
});
```

### Autorizar

Autentica contra la terminal conectada usando correo y contraseña. Si la terminal ya tiene una sesión iniciada, el comando se ignora.

```csharp theme={null}
node.Authorize("user@example.com", "password");
```

### Establecer PIN

Establece un PIN en la terminal para autenticación mediante PIN. Requiere la contraseña de la cuenta para su verificación.

```csharp theme={null}
node.SetPin(new PinPayload { Pin = "1234", Password = "password" });
```

### Autorizar con PIN

Autentica contra la terminal usando un PIN previamente configurado.

```csharp theme={null}
node.AuthorizePin("1234");
```

### Cerrar sesión

Cierra la sesión activa en la terminal conectada.

```csharp theme={null}
node.Logout();
```

### Mensajes perdidos

El sistema de mensajes perdidos permite recuperar mensajes de transacciones con chip (EMV) que no fueron confirmados, por ejemplo debido a una desconexión ocurrida durante la transacción.

<Tip>
  Si configuraste `LostMessagesSyncMode` como `"Auto"`, el SDK sincroniza los mensajes perdidos automáticamente y no necesitas llamar a estos comandos de forma manual. Úsalos solo si elegiste el modo `"Manual"`.
</Tip>

```csharp theme={null}
// Verificar si existen mensajes perdidos
node.RequestHasLostMessages();

// Recuperar todos los mensajes perdidos
node.RequestLostMessages();

// Limpiar todos los mensajes perdidos
node.ClearLostMessages();
```

Los resultados se entregan a través del evento `LostMessagesEvent` (ver [Mensajes perdidos](#mensajes-perdidos-1)).

### Apagar

Desconecta y apaga el nodo actual. Se puede pasar `true` en el parámetro `restart` para que se llame automáticamente a `Setup()` de nuevo después del apagado.

```csharp theme={null}
// Apagar de forma permanente
node.Shutdown();

// Apagar y reiniciar
node.Shutdown(true);
```

## Propiedades

| Propiedad   | Tipo   | Descripción                                               |
| ----------- | ------ | --------------------------------------------------------- |
| `Connected` | `bool` | Indica si hay un nodo remoto conectado actualmente        |
| `NodeSetup` | `bool` | Indica si el nodo local ya fue inicializado con `Setup()` |
| `Presence`  | `bool` | Indica si la terminal conectada está respondiendo         |

## Tipos

### EMVData

Información enviada por la terminal a lo largo del proceso EMV. Los campos se llenan según el `EMVState` actual.

```csharp theme={null}
public struct EMVData
{
    public EMVState State { get; set; }
    public int? Code { get; set; }
    public string? Message { get; set; }
    public Transaction? Transaction { get; set; }
    public string? id { get; set; }
    public bool? lost { get; set; }
    public object? Metadata { get; set; }

    public T? GetMetadata<T>()
    {
        if (Metadata is JsonElement element)
            return element.Deserialize<T>();
        return default;
    }
}
```

El campo `Metadata` contiene la misma información enviada en `StartEMV`. Se puede usar el método auxiliar `GetMetadata<T>()` para deserializarlo al tipo propio de la aplicación:

```csharp theme={null}
// Definir una clase que coincida con la metadata enviada en StartEMV
public class OrderMetadata
{
    public string OrderId { get; set; }
    public int Table { get; set; }
}

// Deserializar dentro del manejador del evento
var meta = emvData.GetMetadata<OrderMetadata>();
```

### Transaction

Detalles de la transacción, recibidos cuando `EMVState` es `Success`.

```csharp theme={null}
public struct Transaction
{
    public string Authorization { get; set; }
    public string Reference { get; set; }
    public string Folio { get; set; }
    public string? Type { get; set; }
    public string? CardBrand { get; set; }
    public string? CardNumber { get; set; }
    public string? Issuer { get; set; }
    public string? Total { get; set; }
    public string? Subtotal { get; set; }
    public string? Tip { get; set; }
    public string? Arqc { get; set; }
    public string? Aid { get; set; }
    public string? Al { get; set; }
    public string? Tvr { get; set; }
    public string? Tsi { get; set; }
}
```

### Capabilities

```csharp theme={null}
public struct Capabilities
{
    public string[]? ScannerPrefixes { get; set; }
    public bool? PingInternetConnectivity { get; set; }
    public bool? ForceRemoteControl { get; set; }
    public bool? DisableManualPayments { get; set; }
    public string? LostMessagesSyncMode { get; set; }
}
```

### PinPayload

```csharp theme={null}
public struct PinPayload
{
    public string Pin { get; set; }
    public string Password { get; set; }
}
```

### EMVState

```csharp theme={null}
public enum EMVState
{
    NotStarted,
    AwaitingCard,
    Processing,
    Success,
    Error,
    Reversal
}
```

### EMVType

```csharp theme={null}
public enum EMVType
{
    Emv,      // Visa / Mastercard
    Amex,     // American Express
    Combined  // Detección automática (valor por defecto)
}
```

### ResultCode

```csharp theme={null}
public enum ResultCode
{
    Success = 0,
    StateUnavailable = 1,
    NodeUnavailable = 2,
    RuntimeUnavailable = 3,
    SenderNotAvailable = 4,
    ConnectionTimeout = 5,
    EndpointOnlineTimeout = 6,
    Utf8Conversion = 7,
    IrohEndpointBind = 8,
    IrohJoin = 9,
    GossipApi = 10,
    GossipNet = 11,
    IrohKeyParse = 12,
    Decode = 13,
    Json = 14,
    Anyhow = 15,
    TryLock = 16,
    Nul = 17,
    SetLogger = 18,
    Panic = 999
}
```

### LostMessagesSyncMode

```csharp theme={null}
public enum LostMessagesSyncMode
{
    Auto,
    Manual
}
```
