# Transaction Updates

### Real-time payment status updates via Server-Sent Events | Teya Developer Documentation


```admonition
icon: fas fa-info-circle
title: Scope
content: |
    Transaction Updates are emitted only while a payment is in progress.
    They occur during `status = IN_PROGRESS` and not once a payment reaches final states like `SUCCESSFUL`, `FAILED`, or `CANCELLED`.
    The stream ends with a final snapshot when the payment leaves `IN_PROGRESS`.
```

## How it works (SSE)

Transaction Updates are delivered over **Server-Sent Events (SSE)** from the Payment Requests endpoint. If the payment is active, the connection upgrades to a live stream; when the payment reaches a final state, you receive a final snapshot and the stream closes.

### Endpoint

```http
GET /poslink/v3/payment-requests/{payment-request-id}

Headers:
Authorization: Bearer <access_token>
Accept: text/event-stream
```

### Event Types
- **`full`** - Complete snapshot of the payment request (initial and final)
- **`diff`** - Incremental updates while `IN_PROGRESS`, including `progress_status`

### Stream Behavior
- If already final → server sends one `full` and closes
- If `NEW`/`IN_PROGRESS` → server streams `diff` updates until final, then sends final `full` and closes

## Implementation Guide

### 1️⃣ Subscribe to SSE

```bash
curl -N \
  -H "Authorization: Bearer <access_token>" \
  -H "Accept: text/event-stream" \
  "https://<host>/poslink/v3/payment-requests/<payment-request-id>"
```

### 2️⃣ Handle Events and Update UI

Use the initial full event to render the starting state. On diff events, read `progress_status` and show friendly, staff-facing messages. When you receive a final full (`SUCCESSFUL`, `FAILED`, or `CANCELLED`), stop listening and finalize the UI.

```javascript
const es = new EventSource(`/poslink/v3/payment-requests/${paymentRequestId}`);

es.addEventListener("full", (e) => {
  const data = JSON.parse(e.data);
  renderSnapshot(data);
  if (["SUCCESSFUL","FAILED","CANCELLED"].includes(data.status)) {
    finalizePayment(data);
    es.close();
  }
});

es.addEventListener("diff", (e) => {
  const data = JSON.parse(e.data);
  updateProgressUI(data.progress_status, data.updated_at);
});

es.onerror = () => {
  // Let the browser retry; optionally surface a subtle reconnecting indicator
};
```

### 3️⃣ Map Progress Status to UI Copy

| Progress Status       | Suggested UI Copy        |
|--------------------------|-----------------------------|
| `SENT_TO_PAYMENT_APP`    | Sending to card machine…        |
| `RECEIVED`               | Request received            |
| `WAITING_FOR_CARD_ENTRY` | Waiting for card…           |
| `INSERT_CARD`            | Insert card                 |
| `CARD_PRESENTED`         | Processing card…            |
| `PIN_ENTRY`              | Enter PIN on card machine       |
| `TIP_SELECTION`          | Choose tip on card machine      |
| `APP_SELECTION`          | Select application          |
| `DCC_SELECTION`          | Choose currency on card machine |
| `REMOVE_CARD`            | Remove card                 |
| `UNKNOWN`                | Continuing…                 |

### 4️⃣ Implement Robust Reconnection

- Use exponential backoff (1s → up to 15s) with jitter
- Prevent UI regression using timestamps (`updated_at`)

### 5️⃣ Avoid Legacy Polling

The final full SSE event is authoritative; avoid duplicate calls to any v1 status endpoint for confirmation.

## Using the Teya SDK

```admonition
icon: fas fa-exclamation-triangle
title: Pre-built Integratio
content: |
    If you integrate via the official **Teya SDK**, Transaction Updates are already built in:
     
    - Pre-built design screens and state mapping for in-progress steps
    - Standard callbacks for full and diff events  
    - Stream lifecycle management (connect, retry, finalize)
    
    You don't need to implement your own status mapping or UI from scratch - ensure you're on a recent SDK version that supports SSE-based updates.
```

## Example Sequences

```tabs
    tab:
      title: Successful Sale
      content:
        1. full: NEW<br>
        2. diff: IN_PROGRESS + WAITING_FOR_CARD_ENTRY<br>
        3. diff: IN_PROGRESS + CARD_PRESENTED <br>
        4. diff: IN_PROGRESS + PIN_ENTRY (if applicable)<br>
        5. full: SUCCESSFUL → stream closes
    tab:
      title: Cancelled by Shopper
      content:
        1. full: NEW<br>
        2. diff: IN_PROGRESS + WAITING_FOR_CARD_ENTRY<br>
        3. full: CANCELLED → stream closes
    tab:
      title: Failed After Card Presented
      content:
        1. full: NEW<br>
        2. diff: IN_PROGRESS + CARD_PRESENTED<br>
        3. full: FAILED (with optional status_reason) → stream closes
```

## Best Practices

- **Reflect every in-progress state** in your POS design when integrating directly with the API
- **Future-proofing:** Tolerate unknown `progress_status` values without breaking (show generic message and keep listening). We may add more statuses over time
- **Single source of truth:** Treat the final full event as the authoritative end of the flow; do not call v1 to confirm final outcomes  
- **UX clarity:** Keep messages short and actionable; use an inline stepper or status panel so staff always know "what's next"
- **Resilience:** Implement reconnection with backoff; on resume, rely on `updated_at` to avoid UI going backwards
- **Accessibility & locales:** Provide accessible labels and ensure copy is localizable
- **Observability:** Log transitions (status, progress_status, timestamps) for support and analytics


## Reference (Event Schema)

```json
{
  "payment_request_id": "uuid",
  "requested_amount": { "currency": "string", "amount": 0, "tip": 0 },
  "status": "NEW | IN_PROGRESS | SUCCESSFUL | CANCELLING | CANCELLED | FAILED",
  "transaction_type": "SALE | ...",
  "created_at": "ISO 8601 timestamp",
  "updated_at": "ISO 8601 timestamp", 
  "merchant_reference": "string",
  "status_reason": "string (optional)",
  "epos_instance_id": "string",
  "gateway_payment_id": "string (on SUCCESSFUL)",
  "store_id": "uuid",
  "terminal_id": "string",
  "progress_status": "enum (present when IN_PROGRESS)",
  "metadata": {}
}
```
