"> Aller au contenu principal
PHP PHP PHP v1.3.5 MIT

Viva Wallet ISV SDK

qrcommunication/viva-isv-sdk

PHP 8.2+, guzzlehttp/guzzle ^7.8

Installation

composer require qrcommunication/viva-isv-sdk

Requirements: PHP 8.2+ with extensions ext-json and ext-curl.

Required credentials — where to find them in the Viva Wallet Dashboard
Credential Location in Dashboard
clientIdSettings > API Access > ISV OAuth Credentials > Client ID
clientSecretSettings > API Access > ISV OAuth Credentials > Client Secret
merchantIdSettings > API Access > Merchant ID
apiKeySettings > API Access > API Key
resellerIdSettings > API Access > Reseller Credentials > Reseller ID
resellerApiKeySettings > API Access > Reseller Credentials > Reseller API Key

Quick Start

use QrCommunication\VivaIsv\VivaIsvClient;

// 1. Instancier le client avec les 6 credentials
$isv = new VivaIsvClient(
    clientId:       'isv-client-id.apps.vivapayments.com',
    clientSecret:   'isv-client-secret',
    merchantId:     'isv-merchant-uuid',
    apiKey:         'isv-api-key',
    resellerId:     'reseller-uuid',
    resellerApiKey: 'reseller-api-key',
    environment:    'demo', // 'demo' ou 'production'
);

// 2. Créer un compte marchand connecté
$account = $isv->accounts->create(
    email: 'merchant@example.com',
    returnUrl: 'https://myapp.com/onboarding/complete',
);
// => ['accountId' => 'uuid', 'invitation' => ['redirectUrl' => 'https://...']]

// 3. Créer un ordre avec commission ISV
$order = $isv->orders->create(
    connectedMerchantId: $account['accountId'],
    amount: 1500,    // 15,00 EUR
    isvAmount: 100,  // 1,00 EUR de commission
);
// => ['order_code' => 1234567890, 'checkout_url' => 'https://...']

// 4. Rediriger le client vers le checkout
header('Location: ' . $order['checkout_url']);

// 5. Capturer une pré-autorisation
$isv->transactions->capture('preauth-txn-uuid', 'merchant-uuid', amount: 1500);

// 6. Vente sur terminal POS
$session = $isv->terminals->sale(
    terminalId: 16014231,
    amount: 1500,
    isvAmount: 100,
    terminalMerchantId: 'merchant-uuid',
    cashRegisterId: 'POS-CR1',
);
$result = $isv->terminals->pollUntilComplete($session['session_id']);

// 7. Rembourser
$isv->transactions->cancel('txn-uuid', 'merchant-uuid', amount: 500);

// Test de connexion
if ($isv->testConnection()) {
    echo 'Connexion ISV OK';
}

Configuration

Constructor VivaIsvClient

Parameter Type Description
clientIdstringISV OAuth2 Client ID (*.apps.vivapayments.com)
clientSecretstringISV OAuth2 Client Secret
merchantIdstringISV Merchant UUID (for ISV Basic Auth)
apiKeystringISV API Key (for ISV Basic Auth)
resellerIdstringReseller UUID (for Composite Basic Auth)
resellerApiKeystringReseller API Key (for Composite Basic Auth)
environmentstring|Environment'demo' or 'production' (default: 'demo')

Resource architecture

VivaIsvClient
├── $accounts          → ConnectedAccounts   (7 methods)
├── $isvAccounts       → IsvAccounts         (3 methods)
├── $orders            → IsvOrders           (2 methods)
├── $transactions      → IsvTransactions     (5 methods)
├── $terminals         → EcrTerminals        (6 methods)
├── $transfers         → Transfers           (2 methods)
├── $marketplace       → MarketplaceOrders   (2 methods)
├── $nativeCheckout    → NativeCheckoutIsv   (2 methods)
├── $isvWebhooks       → IsvWebhooks         (4 methods)
└── $webhooks          → Webhooks            (3 methods)

36 methods total — 10 resources

Authentication

The SDK handles 3 authentication modes automatically depending on the operation. You only need to provide the 6 credentials to the constructor.

Mode Username Password Used for
ISV OAuth2 (Bearer) clientId clientSecret Accounts, ISV orders, terminals, transfers, marketplace, native checkout, ISV webhooks
ISV Basic Auth merchantId apiKey Legacy operations on the ISV own account
Composite Basic Auth {resellerId}:{connectedMerchantId} resellerApiKey Transactions of connected merchants (capture, recurring, cancellation)
Composite Basic Auth — undocumented format by Viva Wallet
The format {ResellerID}:{ConnectedMerchantID} as username with {ResellerAPIKey} as password was discovered empirically during ISV certification. The SDK builds this header automatically — you only pass the connectedMerchantId to the methods of $isv->transactions.

OAuth2 token — automatic refresh

The Bearer token is obtained automatically and refreshed before expiration (60-second margin). To force a manual refresh:

$isv->invalidateToken();

Resource Reference

1 ConnectedAccounts $isv->accounts

Management of merchant accounts connected to the ISV platform. Creation, retrieval, KYB onboarding, verification, update and deletion.

MethodSignatureReturn
createcreate(string $email, string $returnUrl, ?string $partnerName, ?string $logoUrl)array{accountId, invitation}
getget(string $accountId)array (account details)
listlist()array (paginated list)
isVerifiedisVerified(string $accountId)bool
onboardingUrlonboardingUrl(string $accountId)?string
updateupdate(string $accountId, array $attributes)array
deletedelete(string $accountId)array
// Créer un compte connecté avec branding
$account = $isv->accounts->create(
    email: 'merchant@example.com',
    returnUrl: 'https://myapp.com/onboarding/complete',
    partnerName: 'Ma Plateforme',
    logoUrl: 'https://myapp.com/logo.png',
);

// Rediriger le marchand vers l'onboarding KYB
header('Location: ' . $account['invitation']['redirectUrl']);

// Vérifier le statut KYB
if ($isv->accounts->isVerified($account['accountId'])) {
    echo 'Le marchand peut recevoir des paiements';
}

// Récupérer l'URL d'onboarding (null si déjà vérifié)
$url = $isv->accounts->onboardingUrl($account['accountId']);

// Lister tous les comptes connectés
$accounts = $isv->accounts->list();

// Consulter un compte
$details = $isv->accounts->get($account['accountId']);

// Mettre à jour
$isv->accounts->update($account['accountId'], ['email' => 'new@example.com']);

// Supprimer
$isv->accounts->delete($account['accountId']);

2 IsvAccounts $isv->isvAccounts

ISV accounts via the /isv/v1/ namespace with custom branding options (primary color, logo).

MethodSignatureReturn
createcreate(string $email, string $returnUrl, ?string $partnerName, ?string $primaryColor, ?string $logoUrl)array{accountId, invitation}
getget(string $accountId)array
listlist()array
$account = $isv->isvAccounts->create(
    email: 'merchant@example.com',
    returnUrl: 'https://myapp.com/onboarding/done',
    partnerName: 'Ma Plateforme',
    primaryColor: '#0052FF',
    logoUrl: 'https://myapp.com/logo.png',
);

$details = $isv->isvAccounts->get($account['accountId']);
$all     = $isv->isvAccounts->list();

3 IsvOrders $isv->orders

Creation of Smart Checkout payment orders for connected merchants with ISV commission.

MethodSignatureReturn
create create(string $connectedMerchantId, int $amount, int $isvAmount, ?string $customerDescription, ?string $merchantReference, bool $allowRecurring, bool $preauth) array{order_code, checkout_url}
checkoutUrl checkoutUrl(int $orderCode) string
$order = $isv->orders->create(
    connectedMerchantId: 'merchant-uuid',
    amount: 1500,                    // 15,00 EUR
    isvAmount: 100,                  // 1,00 EUR de commission
    customerDescription: 'Consultation bien-être',
    merchantReference: 'INV-2026-001',
    allowRecurring: true,            // Tokeniser la carte pour les récurrents
    preauth: false,
);

echo $order['order_code'];    // 1234567890
echo $order['checkout_url'];  // https://demo.vivapayments.com/web/checkout?ref=...

// Reconstruire l'URL à partir d'un code existant
$url = $isv->orders->checkoutUrl(1234567890);
Rules for isvAmount :
  • isvAmount must be <= amount — otherwise InvalidArgumentException.
  • The connected merchant uses their default payment source — never pass a sourceCode.

4 IsvTransactions $isv->transactions

Operations on transactions of connected merchants. The SDK automatically uses Composite Basic Auth for all calls to this resource.

MethodSignatureReturn
getget(string $transactionId, string $connectedMerchantId)array
listByDatelistByDate(string $connectedMerchantId, string $date)array
capturecapture(string $transactionId, string $connectedMerchantId, int $amount, ?int $isvAmount)array
recurringrecurring(string $initialTransactionId, string $connectedMerchantId, int $amount, ?int $isvAmount, ?string $sourceCode)array
cancelcancel(string $transactionId, string $connectedMerchantId, ?int $amount, ?string $sourceCode)array
// Consulter une transaction
$txn = $isv->transactions->get('txn-uuid', 'merchant-uuid');

// Lister les transactions d'une journée
$transactions = $isv->transactions->listByDate('merchant-uuid', '2026-03-18');

// Capturer une pré-autorisation
$isv->transactions->capture(
    transactionId: 'preauth-txn-uuid',
    connectedMerchantId: 'merchant-uuid',
    amount: 1500,
    isvAmount: 100,
);

// Paiement récurrent (à partir d'une transaction initiale tokenisée)
$isv->transactions->recurring(
    initialTransactionId: 'initial-txn-uuid',
    connectedMerchantId: 'merchant-uuid',
    amount: 1500,
    isvAmount: 100,
);

// Remboursement total
$isv->transactions->cancel('txn-uuid', 'merchant-uuid');

// Remboursement partiel (5,00 EUR)
$isv->transactions->cancel('txn-uuid', 'merchant-uuid', amount: 500);
Prerequisite: The option "Allow recurring payments and pre-auth captures via API" must be enabled in Settings > API Access of the ISV account.

5 EcrTerminals $isv->terminals

ISV Cloud POS payment terminals. Search, sale, session polling, abort.

MethodSignatureReturn
searchsearch(?string $merchantId, ?int $statusId, ?string $sourceCode)array
salesale(int $terminalId, int $amount, int $isvAmount, string $terminalMerchantId, string $cashRegisterId, ?string $merchantReference, int $currencyCode, ?string $sessionId)array{session_id, success}
getSessiongetSession(string $sessionId)array
listSessionslistSessions(string $date)array
abortabort(string $sessionId, string $cashRegisterId)array
pollUntilCompletepollUntilComplete(string $sessionId, int $timeoutSeconds, int $intervalMs)array
use QrCommunication\VivaIsv\Enums\EcrEventId;

// Rechercher les terminaux d'un marchand
$terminals = $isv->terminals->search(merchantId: 'merchant-uuid');

// Vente POS ISV
$session = $isv->terminals->sale(
    terminalId: 16014231,
    amount: 1500,
    isvAmount: 100,
    terminalMerchantId: 'merchant-uuid',
    cashRegisterId: 'PratiConnect-CR1',
    merchantReference: 'INV-2026-001',
);

echo $session['session_id']; // UUID de session

// Polling jusqu'au résultat (défaut : 120s timeout, 3s intervalle)
$result = $isv->terminals->pollUntilComplete($session['session_id']);

// Interpréter le résultat avec l'enum
$eventId = EcrEventId::tryFrom($result['eventId']);
if ($eventId?->isSuccessful()) {
    echo 'Transaction réussie : ' . $result['transactionId'];
} else {
    echo 'Échec : ' . $eventId?->label();
}

// Annuler une session active
$isv->terminals->abort('session-uuid', 'PratiConnect-CR1');

// Consulter une session
$session = $isv->terminals->getSession('session-uuid');

// Lister les sessions d'une journée
$sessions = $isv->terminals->listSessions('2026-03-18');
Important notes:
  • Pre-authorization is not supported via ISV Cloud Terminal — use Smart Checkout with preauth: true.
  • Abort uses GET (not DELETE) — an undocumented quirk of the Viva API. The SDK handles this automatically.
  • The sessionId is auto-generated if not provided.
  • The SDK builds isvDetails automatically.

6 Transfers $isv->transfers

Sending and reversing fund transfers to connected accounts.

MethodSignatureReturn
sendsend(string $targetAccountId, int $amount, ?string $sourceWalletId, ?string $transactionId, ?string $description)array{transferId}
reversereverse(string $transferId, ?int $amount)array{transferId}
// Envoyer des fonds à un vendeur
$transfer = $isv->transfers->send(
    targetAccountId: 'seller-account-uuid',
    amount: 1000,                    // 10,00 EUR
    transactionId: 'txn-uuid',       // Lier à une transaction existante
    description: 'Commission mars 2026',
);

echo $transfer['transferId'];

// Annuler totalement un transfert
$isv->transfers->reverse('transfer-uuid');

// Annuler partiellement (5,00 EUR)
$isv->transfers->reverse('transfer-uuid', amount: 500);

7 MarketplaceOrders $isv->marketplace

Marketplace orders with automatic transfer to the seller. The platform fee is the difference between amount and sellerAmount.

MethodSignatureReturn
create create(int $amount, string $sellerAccountId, int $sellerAmount, ?string $customerDescription, ?string $merchantReference, ?string $sourceCode, bool $preauth) array{order_code, checkout_url, platform_fee}
cancel cancel(string $transactionId, ?int $amount, bool $reverseTransfers, bool $refundPlatformFee) array
// Créer un ordre marketplace
$order = $isv->marketplace->create(
    amount: 1500,                    // 15,00 EUR total
    sellerAccountId: 'seller-uuid',
    sellerAmount: 1200,              // 12,00 EUR au vendeur
    customerDescription: 'Achat marketplace',
    merchantReference: 'MP-2026-001',
);

echo $order['order_code'];
echo $order['checkout_url'];
echo $order['platform_fee'];  // 300 = 3,00 EUR de commission plateforme

// Remboursement total avec reversal des transferts
$isv->marketplace->cancel('txn-uuid');

// Remboursement partiel sans rembourser la commission plateforme
$isv->marketplace->cancel(
    transactionId: 'txn-uuid',
    amount: 500,
    reverseTransfers: true,
    refundPlatformFee: false,
);

8 NativeCheckoutIsv $isv->nativeCheckout

Native server-to-server payment for connected merchants, without Smart Checkout redirection.

MethodSignatureReturn
createChargeToken createChargeToken(string $connectedMerchantId, int $amount, string $paymentData, int $paymentMethodId) array{chargeToken}
createTransaction createTransaction(string $connectedMerchantId, string $chargeToken, int $amount, int $isvAmount, int $currencyCode, ?string $merchantTrns, ?string $customerTrns, bool $preauth) array{transactionId, statusId}

2-step flow:

  1. The client collects card data via the Viva JS SDK and obtains paymentData (encrypted client-side).
  2. Your server creates a charge token via createChargeToken(), then executes the transaction via createTransaction().
// Étape 1 : créer un charge token
$token = $isv->nativeCheckout->createChargeToken(
    connectedMerchantId: 'merchant-uuid',
    amount: 1500,
    paymentData: $encryptedCardData,  // Du JS SDK Viva
    paymentMethodId: 0,               // 0 = carte par défaut
);

// Étape 2 : exécuter la transaction
$txn = $isv->nativeCheckout->createTransaction(
    connectedMerchantId: 'merchant-uuid',
    chargeToken: $token['chargeToken'],
    amount: 1500,
    isvAmount: 100,
    currencyCode: 978,                // EUR
    merchantTrns: 'INV-2026-001',
    customerTrns: 'Consultation bien-être',
);

echo $txn['transactionId'];
echo $txn['statusId'];

9 IsvWebhooks $isv->isvWebhooks

Creation, listing, update and deletion of ISV webhook subscriptions.

MethodSignatureReturn
createcreate(string $url, string $eventType, ?string $description)array (with webhookId)
listlist()array
updateupdate(string $webhookId, string $url, ?string $eventType)array
deletedelete(string $webhookId)array
// Créer un webhook
$webhook = $isv->isvWebhooks->create(
    url: 'https://myapp.com/webhooks/viva',
    eventType: 'transaction.payment.created',
    description: 'Notifications de paiement',
);

// Lister
$webhooks = $isv->isvWebhooks->list();

// Modifier
$isv->isvWebhooks->update(
    webhookId: $webhook['webhookId'],
    url: 'https://myapp.com/webhooks/viva-v2',
    eventType: 'transaction.refund.created',
);

// Supprimer
$isv->isvWebhooks->delete($webhook['webhookId']);

10 Webhooks $isv->webhooks

Verification of the initial Viva Wallet GET request and parsing of POST payloads (21 event types).

MethodSignatureReturn
verificationResponseverificationResponse(string $verificationKey)array{StatusCode, Key}
parseparse(string $rawBody)array{event_type, event_type_id, event_data}
isKnownEventWebhooks::isKnownEvent(int $eventTypeId) (static)bool
// GET — vérification initiale
$verificationKey = config('services.viva.verification_key');
return response()->json(
    $isv->webhooks->verificationResponse($verificationKey)
);
// => {"StatusCode": 0, "Key": "votre-cle"}

// POST — parsing des événements
$event = $isv->webhooks->parse(file_get_contents('php://input'));

echo $event['event_type'];     // 'transaction.payment.created'
echo $event['event_type_id'];  // 1796
$data = $event['event_data'];

// Pattern Laravel Controller
match ($event['event_type']) {
    'transaction.payment.created' => $this->handlePayment($event['event_data']),
    'transaction.refund.created'  => $this->handleRefund($event['event_data']),
    'account.connected'           => $this->handleNewAccount($event['event_data']),
    default => null,
};

// Vérifier si un eventTypeId est connu (méthode statique)
use QrCommunication\VivaIsv\Resources\Webhooks;

if (Webhooks::isKnownEvent(1796)) {
    echo 'Événement reconnu';
}

Enums

EcrEventId — Cloud Terminal result codes

use QrCommunication\VivaIsv\Enums\EcrEventId;

$event = EcrEventId::tryFrom($session['eventId']);
$event->isSuccessful(); // true si SUCCESS (0)
$event->isTerminal();   // true si état final (pas IN_PROGRESS)
$event->shouldPoll();   // true si IN_PROGRESS (1100)
$event->label();        // 'Transaction successful', 'Declined', etc.
ValueConstantDescription
0SUCCESSTransaction successful
1003TERMINAL_TIMEOUTTerminal timed out
1006DECLINEDTransaction declined
1016ABORTEDTransaction aborted
1020INSUFFICIENT_FUNDSInsufficient funds
1099GENERIC_ERRORGeneric error
1100IN_PROGRESSIn progress — continue polling
6000BAD_PARAMSInvalid parameters

TransactionEventId — detailed decline codes

use QrCommunication\VivaIsv\Enums\TransactionEventId;

$decline = TransactionEventId::tryFrom($session['transactionEventId']);
echo $decline->label();       // 'Insufficient funds'
echo $decline->testAmount();  // 9951 (montant pour déclencher ce déclin en demo)
ValueConstantTest amount (cents)
10001REFER_TO_ISSUER
10003INVALID_MERCHANT
10004PICKUP_CARD
10005DO_NOT_HONOR
10006GENERAL_ERROR9906
10012INVALID_TRANSACTION
10013INVALID_AMOUNT
10014INVALID_CARD9914
10030FORMAT_ERROR
10041LOST_CARD
10043STOLEN_CARD9920
10051INSUFFICIENT_FUNDS9951
10054EXPIRED_CARD9954
10055INCORRECT_PIN
10057NOT_PERMITTED_CARDHOLDER9957
10058NOT_PERMITTED_TERMINAL
10061WITHDRAWAL_LIMIT9961
10062RESTRICTED_CARD
10063SECURITY_VIOLATION
10065ACTIVITY_LIMIT
10068LATE_RESPONSE
10070CALL_ISSUER
10075PIN_TRIES_EXCEEDED
10200UNMAPPED

Environment

use QrCommunication\VivaIsv\Enums\Environment;

// Pass as string or enum — both are accepted
$isv = new VivaIsvClient(..., environment: 'demo');
$isv = new VivaIsvClient(..., environment: Environment::PRODUCTION);

Error Handling

The SDK defines 3 exceptions in the QrCommunication\VivaIsv\Exceptions namespace:

RuntimeException
└── VivaException         (base — httpStatus, responseBody, getErrorCode(), getErrorText())
    ├── ApiException      (erreurs API 4xx/5xx)
    └── AuthenticationException  (erreurs OAuth2 — httpStatus = 401)
use QrCommunication\VivaIsv\Exceptions\ApiException;
use QrCommunication\VivaIsv\Exceptions\AuthenticationException;

try {
    $order = $isv->orders->create('merchant-uuid', 1500, isvAmount: 100);
} catch (AuthenticationException $e) {
    // Credentials ISV invalides
    echo $e->getMessage(); // 'ISV OAuth2 authentication failed: ...'
} catch (ApiException $e) {
    // Erreur API (400, 404, 500, etc.)
    echo $e->getMessage();       // Message d'erreur
    echo $e->httpStatus;         // Code HTTP
    echo $e->getErrorCode();     // Code Viva (ErrorCode)
    echo $e->getErrorText();     // Texte Viva (ErrorText)
    print_r($e->responseBody);   // Body JSON complet
}
The capture() and recurring() methods throw ApiException if ErrorCode !== 0 in the Viva Wallet response.

Webhook Events (21)

IDType
1796transaction.payment.created
1797transaction.refund.created
1798transaction.payment.cancelled
1799transaction.reversal.created
1800transaction.preauth.created
1801transaction.preauth.completed
1802transaction.preauth.cancelled
1810pos.session.created
1811pos.session.failed
1812transaction.price.calculated
1813transaction.failed
1819account.connected
1820account.verification.status.changed
1821account.transaction.created
1822command.bank.transfer.created
1823command.bank.transfer.executed
1824transfer.created
1825obligation.created
1826obligation.captured
1827order.updated
1828sale.transactions.file

Sandbox Testing

Use environment: 'demo' to test without real transactions.

Test card

FieldValue
Number4111 1111 1111 1111
ExpiryAny future date
CVV111

Amounts that trigger declines in demo mode

use QrCommunication\VivaIsv\Enums\TransactionEventId;

// Obtenir le montant de test pour un type de déclin
$amount = TransactionEventId::INSUFFICIENT_FUNDS->testAmount(); // 9951

$order = $isv->orders->create('merchant-uuid', $amount);
Amount (cents)Triggered decline
9951Insufficient funds (INSUFFICIENT_FUNDS)
9954Expired card (EXPIRED_CARD)
9920Stolen card (STOLEN_CARD)
9957Card not permitted for cardholder (NOT_PERMITTED_CARDHOLDER)
9961Withdrawal limit exceeded (WITHDRAWAL_LIMIT)
9906General error (GENERAL_ERROR)
9914Invalid card (INVALID_CARD)