"> Aller au contenu principal
TypeScript TypeScript v1.0.0 MIT

Viva Cloud Terminal TypeScript SDK

@qrcommunication/viva-cloud-terminal-sdk

Node.js 18+

Quoi de neuf en v1.0.0 What's new in v1.0.0 v1.0.0

✨ Version initiale. SDK TypeScript isomorphe pour la Cloud Terminal API marchand (/ecr/v1/) de Viva Wallet : piloter un terminal de paiement physique depuis votre back-office (vente, capture preauth, remboursements, rebate, sessions, polling). Fonctionne en Node 18+, navigateur, Deno, edge runtimes et React Native. — Initial release. Isomorphic TypeScript SDK for the merchant Cloud Terminal API (/ecr/v1/). GitHub Release v1.0.0 →
  • Client VivaCloudTerminalClient — point d'entrée unique avec auth OAuth2 client_credentials automatique (Bearer token caché et rafraîchi 60s avant expiration).
  • Ressource Devices : search() (POST /ecr/v1/devices:search).
  • Ressource Transactions : sale(), capturePreauth(), refund(), unreferencedRefund(), fastRefund(), rebate(), createAction(), getAction().
  • Ressource Sessions : get(), listByDate(), abort(), pollUntilComplete().
  • pollUntilComplete() disponible aussi comme raccourci sur le client.
  • Constantes EcrEventId + helpers (ecrEventIdFrom, isSuccessfulEvent, isInProgress, ecrEventLabel).
  • Isomorphe, zéro dépendance (utilise le fetch natif). Montants en centimes, payloads en camelCase.
Périmètre : ce SDK couvre uniquement les endpoints marchand /ecr/v1/. Pour la variante ISV / Partner (/ecr/isv/v1/), utiliser un SDK distinct. — Merchant /ecr/v1/ only.

Installation

Installez le SDK via npm, pnpm ou yarn. Requiert Node 18.17+.

Install via npm, pnpm or yarn. Requires Node 18.17+.

npm install @qrcommunication/viva-cloud-terminal-sdk
# ou
pnpm add @qrcommunication/viva-cloud-terminal-sdk
# ou
yarn add @qrcommunication/viva-cloud-terminal-sdk

Démarrage rapide Quick Start

import { VivaCloudTerminalClient } from "@qrcommunication/viva-cloud-terminal-sdk";

const viva = new VivaCloudTerminalClient({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
  environment: "demo", // "demo" (sandbox) ou "production"
});

// 1. (recommandé) confirmer que le terminal est Live avant de transiger
const devices = await viva.devices.search({ statusId: 1 });

// 2. initier une vente sur le terminal (montant en centimes)
const sale = await viva.transactions.sale({
  terminalId: "16000010",
  amount: 1170, // 11,70 EUR
  cashRegisterId: "CR-01",
  merchantReference: "order-42",
});

// 3. poller jusqu'à ce que le client termine (ou décline) sur le terminal
const result = await viva.pollUntilComplete(sale.sessionId);

if (result.success === true) {
  // result.transactionId, result.amount, ...
}

Configuration

Le constructeur VivaCloudTerminalClient accepte un objet d'options VivaCloudTerminalOptions.

Option Type Défaut Description
clientId string requis Client ID OAuth2 (grant client_credentials)
clientSecret string requis Secret OAuth2
environment Environment "demo" "demo" (sandbox) ou "production"
timeoutMs number 30000 Timeout des requêtes en millisecondes
fetch typeof fetch global Implémentation fetch custom (polyfill / mock)
Sécurité : ne jamais embarquer votre clientSecret dans un bundle navigateur / public. Exécutez le SDK sur un serveur (ou edge) que vous contrôlez et proxifiez les requêtes depuis le client. — Never ship your clientSecret to a browser/public bundle.

Méthodes utilitaires & propriétés publiques

viva.devices        // DevicesResource
viva.transactions   // TransactionsResource
viva.sessions       // SessionsResource

viva.getConfig();                          // CloudTerminalConfig résolu
viva.invalidateToken();                    // Forcer un nouveau handshake OAuth
viva.pollUntilComplete(sessionId, opts?);  // Raccourci vers sessions.pollUntilComplete()

Chaque méthode transactions.* retourne un TransactionResult :

interface TransactionResult {
  sessionId: string;                  // pollez-le pour le résultat
  response: Record<string, unknown>;  // body API brut (souvent vide en succès)
}

Authentification Authentication

La Cloud Terminal API utilise un seul mode d'authentification : OAuth2 client_credentials → Bearer token. Le SDK récupère et met en cache le token automatiquement (rafraîchi 60s avant expiration).

Étape Hôte Auth
Token accounts.vivapayments.com/connect/token (demo : demo-accounts…) Basic client_id:client_secret, grant_type=client_credentials
Appels API api.vivapayments.com/ecr/v1/… (demo : demo-api…) Authorization: Bearer {token}
CRITIQUE : tous les payloads /ecr/v1/ sont en camelCase. — All /ecr/v1/ payloads use camelCase keys.

Ressources

1 Devices viva.devices

Découverte des terminaux POS (EFT POS) et de leur statut. Recommandé en pré-flight avant toute transaction (surtout sur WAN) pour confirmer que le terminal est Live (statusId 1).

search(params?: DeviceSearchParams): Promise<JsonObject>

Rechercher les terminaux POS — POST /ecr/v1/devices:search.

Paramètre Type Défaut Description
statusIdnumberFiltrer par statut (0 = WareHouse, 1 = Live, 2 = Ready To Ship, 3 = In Stock, 4 = Pending Key Injection, 5 = Lost, 6 = Broken)
sourceCodestringCode d'identification personnalisé assigné au terminal par le marchand

Retourne : Promise<JsonObject> — chaque entrée : terminalId, statusId, sourceCode, virtualTerminalId

const devices = (await viva.devices.search({ statusId: 1 })) as unknown as Device[];

for (const device of devices) {
  console.log(`${device.terminalId} — statut ${device.statusId}`);
  // 16000010 — statut 1
}

2 Transactions viva.transactions

Vente, capture de pré-autorisation, remboursements, rebate et actions sur le terminal. Les champs optionnels partagés sont définis par TransactionCommonParams. Montants toujours en centimes.

interface TransactionCommonParams {
  merchantReference?: string;       // défaut: valeur "SDK-..." générée
  currencyCode?: string;            // défaut: "978" (EUR)
  customerTrns?: string;
  showTransactionResult?: boolean;
  showReceipt?: boolean;
  sessionId?: string;               // auto-généré (UUID v4) si omis
  extra?: Record<string, unknown>;  // champs API additionnels fusionnés tels quels
}
Body vide sur succès. La plupart des endpoints /ecr/v1/transactions:* répondent HTTP 200 sans body — c'est le succès du dispatch vers le terminal, pas le résultat du paiement. Chaque méthode retourne un TransactionResult : pollez sessionId pour le résultat réel. — Empty body on success: poll the session for the actual payment outcome.

sale(params: SaleParams): Promise<TransactionResult>

Initier une vente — POST /ecr/v1/transactions:sale.

Paramètre Type Défaut Description
terminalIdstring | numberrequisID du terminal cible (ex : "16000010")
amountnumberrequisMontant à autoriser, en centimes
cashRegisterIdstringrequisIdentification de la caisse (défini par le marchand)
tipAmountnumber0Pourboire en centimes (incompatible avec preauth)
preauthbooleanFlag de pré-autorisation (nécessite activation Viva)
paymentMethodstringMéthode de paiement affichée (ex : "CardPresent")
skipSurchargebooleanDésactiver la surcharge pour cette transaction
merchantReferencestring"SDK-{sessionId}"Référence marchand (texte libre)
currencyCodestring"978"Code ISO 4217 numérique en string
customerTrnsstringRéférence client (texte libre)
showTransactionResultbooleanAfficher le résultat sur le terminal
showReceiptbooleanAfficher le reçu + résultat sur le terminal
sessionIdstringauto (UUID)UUID de session
extraobject{}Champs API additionnels (ex : fiscalisationData)

Retourne : Promise<TransactionResult>

const sale = await viva.transactions.sale({
  terminalId: "16000010",
  amount: 2599, // 25,99 EUR
  cashRegisterId: "CR-01",
  customerTrns: "Table 12",
  showReceipt: true,
});

const final = await viva.pollUntilComplete(sale.sessionId, { timeoutSeconds: 90 });

capturePreauth(params: CapturePreauthParams): Promise<TransactionResult>

Capturer une transaction pré-autorisée — POST /ecr/v1/transactions:preauth-completion. Capture le montant retenu par la pré-autorisation identifiée par parentSessionId. CapturePreauthParams ajoute parentSessionId, terminalId, amount, cashRegisterId aux champs communs.

Retourne : Promise<TransactionResult>

// Pré-autoriser (nécessite preauth activé sur le compte Viva)
const preauth = await viva.transactions.sale({
  terminalId: "16000010",
  amount: 5000,
  cashRegisterId: "CR-01",
  preauth: true,
});
await viva.pollUntilComplete(preauth.sessionId);

// Plus tard, capturer le montant final (peut être inférieur à l'autorisé)
const capture = await viva.transactions.capturePreauth({
  parentSessionId: preauth.sessionId,
  terminalId: "16000010",
  amount: 4200,
  cashRegisterId: "CR-01",
});
await viva.pollUntilComplete(capture.sessionId);

refund(params: RefundParams): Promise<TransactionResult>

Rembourser une vente, en référençant la session originale — POST /ecr/v1/transactions:refund. Un nouveau sessionId est généré pour le remboursement ; parentSessionId référence la vente originale. RefundParams ajoute parentSessionId, terminalId, amount, cashRegisterId aux champs communs.

Retourne : Promise<TransactionResult>

const refund = await viva.transactions.refund({
  parentSessionId: sale.sessionId,
  terminalId: "16000010",
  amount: 1170,
  cashRegisterId: "CR-01",
});
await viva.pollUntilComplete(refund.sessionId);

unreferencedRefund(params: StandaloneRefundParams): Promise<TransactionResult>

Remboursement autonome (non référencé), non lié à une vente originale — POST /ecr/v1/transactions:unreferenced-refund. StandaloneRefundParams ajoute terminalId, amount, cashRegisterId aux champs communs.

Retourne : Promise<TransactionResult>

const refund = await viva.transactions.unreferencedRefund({
  terminalId: "16000010",
  amount: 1500,
  cashRegisterId: "CR-01",
});
await viva.pollUntilComplete(refund.sessionId);

fastRefund(params: StandaloneRefundParams): Promise<TransactionResult>

Remboursement rapide (swift) sur Visa / Mastercard / Maestro — POST /ecr/v1/transactions:fast-refund. Mêmes paramètres (StandaloneRefundParams) que unreferencedRefund().

Retourne : Promise<TransactionResult>

const refund = await viva.transactions.fastRefund({
  terminalId: "16000010",
  amount: 1170,
  cashRegisterId: "CR-01",
});
await viva.pollUntilComplete(refund.sessionId);

rebate(params: StandaloneRefundParams): Promise<TransactionResult>

Rabais vers une carte Visa / Mastercard / Maestro — POST /ecr/v1/transactions:rebate. Mêmes paramètres (StandaloneRefundParams) que unreferencedRefund().

Retourne : Promise<TransactionResult>

const rebate = await viva.transactions.rebate({
  terminalId: "16000010",
  amount: 500,
  cashRegisterId: "CR-01",
});
await viva.pollUntilComplete(rebate.sessionId);

createAction(payload: CreateActionParams): Promise<JsonObject>

Créer une action à invoquer sur un terminal (ex : aade-fim-control) — POST /ecr/v1/actions. Le payload est transmis tel quel (camelCase) et doit inclure terminalId, cashRegisterId, actionType et request.

Retourne : Promise<JsonObject> (notamment actionId)

const action = await viva.transactions.createAction({
  terminalId: "16000010",
  cashRegisterId: "XDE384678UY",
  actionType: "aade-fim-control",
  request: { token: "ECR0210U/RCFB77000041/CMAC_K:299BFD51...:CC5FFF" },
});

console.log(action.actionId); // f7287549-e93a-4d33-b936-000027d326d0

getAction(actionId: string): Promise<JsonObject>

Récupérer le résultat d'une action — GET /ecr/v1/actions/{actionId}. Retourne HTTP 202 (body vide → {}) tant que l'action est en cours, ou le résultat une fois terminée.

Retourne : Promise<JsonObject> (successfullyProcessed, response, …)

const result = await viva.transactions.getAction(
  "f7287549-e93a-4d33-b936-000027d326d0",
);

if (result.successfullyProcessed === true) {
  console.log(result.response); // { status: "success", message: "..." }
}

3 Sessions viva.sessions

Récupération, listing, abort et polling des sessions. Une session représente le cycle de vie d'une transaction sur un terminal. Après avoir initié une transaction, pollez la session par son id pour observer son résultat (eventId, success, transactionId, …).

get(sessionId: string): Promise<Session>

Récupérer une session par son id — GET /ecr/v1/sessions/{sessionId}.

Retourne : Promise<Session> (success, eventId, transactionId, amount, …)

const session = await viva.sessions.get(sale.sessionId);

console.log(session.eventId);       // 0 = succès
console.log(session.transactionId); // UUID de la transaction Viva

listByDate(params: ListSessionsParams): Promise<JsonObject>

Lister toutes les sessions d'une date — GET /ecr/v1/sessions?date=YYYY-MM-DD.

Paramètre Type Défaut Description
datestringrequisDate au format YYYY-MM-DD
aadeAutonomouslyOnlybooleanFiltrer aux sessions AADE-autonomes uniquement (Grèce)

Retourne : Promise<JsonObject>

const sessions = await viva.sessions.listByDate({ date: "2026-06-06" });

abort(sessionId: string, cashRegisterId: string): Promise<JsonObject>

Annuler une session active — DELETE /ecr/v1/sessions/{sessionId}?cashRegisterId=…. Seule la caisse ayant créé la transaction peut l'annuler.

Retourne : Promise<JsonObject>

await viva.sessions.abort(sale.sessionId, "CR-01");

pollUntilComplete(sessionId: string, options?: PollOptions): Promise<Session>

Poll une session jusqu'à un état terminal ou expiration du timeout. Continue tant que eventId vaut IN_PROGRESS (1100), en dormant intervalMs entre les tentatives. S'arrête aussi sur un eventId inconnu (fail-safe). Disponible sur la ressource sessions et comme raccourci sur le client.

Paramètre Type Défaut Description
sessionIdstringrequisUUID de la session à poller
options.timeoutSecondsnumber120Temps d'attente total maximum (secondes)
options.intervalMsnumber3000Délai entre deux tentatives (millisecondes)

Retourne : Promise<Session> — l'état final. En cas de timeout : { success: false, eventId: 1003, message: "SDK poll timeout" }.

import { ecrEventIdFrom, isSuccessfulEvent } from "@qrcommunication/viva-cloud-terminal-sdk";

const result = await viva.pollUntilComplete(sale.sessionId, { timeoutSeconds: 90 });

const event = ecrEventIdFrom(result.eventId);
if (event !== null && isSuccessfulEvent(event)) {
  // Paiement confirmé
}

Enums & helpers

EcrEventId

Interprétation de l'eventId retourné dans la réponse de poll de session.

eventIdConstanteSens
0SUCCESSTransaction réussie
1003TERMINAL_TIMEOUTTimeout terminal
1006DECLINEDRefusée
1016ABORTEDAnnulée
1020INSUFFICIENT_FUNDSFonds insuffisants
1099GENERIC_ERRORErreur générique
1100IN_PROGRESSEn cours (continuer à poller)
6000BAD_PARAMSParamètres incorrects

Helpers

HelperSignatureDescription
ecrEventIdFrom(value: number | null | undefined) => EcrEventId | nullRetourne l'événement connu ou null si non reconnu
isSuccessfulEvent(eventId: EcrEventId) => booleantrue si succès
isInProgress(eventId: EcrEventId) => booleantrue tant que IN_PROGRESS (1100)
isTerminalEvent(eventId: EcrEventId) => booleantrue si état final (≠ IN_PROGRESS)
ecrEventLabel(eventId: EcrEventId) => stringLibellé lisible
import {
  ecrEventIdFrom,
  isSuccessfulEvent,
  isInProgress,
  ecrEventLabel,
} from "@qrcommunication/viva-cloud-terminal-sdk";

const event = ecrEventIdFrom(result.eventId);
if (event !== null) {
  isSuccessfulEvent(event); // true en cas de succès
  isInProgress(event);      // true tant que IN_PROGRESS (1100)
  ecrEventLabel(event);     // libellé lisible
}

Environment

ConstanteHôte OAuth (token)Hôte API (/ecr/v1/)
DEMOhttps://demo-accounts.vivapayments.comhttps://demo-api.vivapayments.com
PRODUCTIONhttps://accounts.vivapayments.comhttps://api.vivapayments.com

Gestion des erreurs Error Handling

Toutes les erreurs héritent de VivaError.

Error
└── VivaError                  (classe de base)
    ├── ApiError               (erreurs HTTP 4xx / 5xx)
    └── AuthenticationError    (OAuth2 invalide — 401)
MembreTypeDescription
httpStatusnumberCode HTTP de la réponse
getErrorText()string | nullMessage d'erreur Viva lisible
getErrorCode()number | nullCode d'erreur Viva, si présent
import {
  ApiError,
  AuthenticationError,
  VivaError,
} from "@qrcommunication/viva-cloud-terminal-sdk";

try {
  await viva.transactions.sale({
    terminalId: "16000010",
    amount: 1170,
    cashRegisterId: "CR-01",
  });
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Handshake OAuth échoué (client id/secret invalides)
  } else if (error instanceof ApiError) {
    error.httpStatus;     // ex : 409
    error.getErrorText(); // message Viva lisible
    error.getErrorCode(); // code Viva, si présent
  } else if (error instanceof VivaError) {
    // Classe de base pour toutes les erreurs SDK
  }
}

Carte de test Test Card

Environnement demo (sandbox).

ChampValeur
Numéro de carte4111 1111 1111 1111
CVV111
Mot de passe 3DSSecret!33

Montants de déclin courants : 9951 (fonds insuffisants), 9954 (carte expirée), 9920 (carte volée), 9957 (non autorisé), 9961 (limite de retrait).