# Diller Tap2ID — POS Integration Guide

Edited
# Diller Tap2ID — POS Integration Guide


## Table of Contents

1. [Introduction](#introduction)
2. [How Tap2ID Works](#how-tap2id-works)
3. [Tap2ID with Nets (Baxi.NET)](#tap2id-with-nets-baxinet)
   - [Prerequisites](#baxi-prerequisites)
   - [Step 1: Read Card & Identify Member (SendJson)](#baxi-step-1-read-card--identify-member)
   - [Step 2: Query Diller for Member Details & Benefits](#baxi-step-2-query-diller-for-member-details)
   - [Step 3: Apply Discounts & Process Payment (TransferAmount)](#baxi-step-3-process-payment)
   - [Registering a New Card (InsertAsset)](#baxi-registering-a-new-card)
4. [Tap2ID with Verifone](#tap2id-with-verifone)
   - [Prerequisites](#verifone-prerequisites)
   - [Step 1: Read Card & Get Token (Card Acquisition)](#verifone-step-1-card-acquisition)
   - [Step 2: Query Diller for Member Details & Benefits](#verifone-step-2-query-diller-for-member-details)
   - [Step 3: Apply Discounts & Process Payment](#verifone-step-3-process-payment)
   - [Registering a New Card](#verifone-registering-a-new-card)
5. [Diller API Reference](#diller-api-reference)
   - [Authentication](#authentication)
   - [Identify Member by Card Token](#identify-member-by-card-token)
   - [Search Member by Phone](#search-member-by-phone)
   - [Get Member Coupons](#get-member-coupons)
   - [Register Card to Member](#register-card-to-member)
   - [Enroll New Member](#enroll-new-member)
   - [Create Transaction](#create-transaction)
6. [Key Differences: Nets vs Verifone](#key-differences)
7. [Complete Flow Diagrams](#complete-flow-diagrams)
8. [FAQ & Troubleshooting](#faq--troubleshooting)

---

## Introduction

**Tap2ID** allows loyalty members to identify themselves at the point of sale by simply tapping their payment card on the terminal — no phone number, no app, no barcode needed. The POS reads the card, matches it to a Diller loyalty member, fetches their coupons and benefits, applies discounts to the basket, and then charges the final amount — all in a seamless checkout experience.

This guide covers how to integrate Tap2ID into your POS system using two terminal families:

| Terminal | SDK | Card Identification Method |
|----------|-----|--------------------------|
| **Nets (Ingenico/Baxi)** | BAXI.NET SDK (`baxi_dotnet.dll`) | SendJson with DAM/Storebox |
| **Verifone P630** | PaymentSdk (PSDK) | Card Acquisition with tokenization |

Both follow the same high-level flow but use different SDK calls to obtain the card identifier.

---

## How Tap2ID Works

The checkout flow with Tap2ID follows these steps:

```
┌─────────────────────────────────────────────────┐
│  1. CARD READ (no charge)                       │
│     Terminal reads the card and returns          │
│     a token/member identifier                   │
│     • Nets: SendJson → member ID from Storebox  │
│     • Verifone: Card Acquisition → card token   │
└──────────────────┬──────────────────────────────┘
                   ▼
┌─────────────────────────────────────────────────┐
│  2. DILLER LOOKUP                               │
│     POS sends the identifier to Diller API      │
│     → Returns member details + available coupons│
└──────────────────┬──────────────────────────────┘
                   ▼
┌─────────────────────────────────────────────────┐
│  3. APPLY BENEFITS                              │
│     POS applies coupons/discounts to the basket │
│     and calculates the final amount             │
│     Example: 349 NOK → 299 NOK after discount   │
└──────────────────┬──────────────────────────────┘
                   ▼
┌─────────────────────────────────────────────────┐
│  4. PAYMENT (card charged)                      │
│     Terminal processes a normal TransferAmount   │
│     (or equivalent) for the discounted total    │
│     Customer taps/inserts card                  │
└─────────────────────────────────────────────────┘
```

The member must have **pre-registered their card** in Diller . If the card is not recognized, the POS can fall back to phone number lookup and then register the card for future Tap2ID use.

---

## Tap2ID with Nets (Baxi.NET)

### Baxi Prerequisites

- **BAXI.NET SDK** (`baxi_dotnet.dll` v1.14.1 or later) — available from NexiGroup
- Terminal with **Storebox/DAM** support enabled (contact Nets/NexiGroup)
- `PreventLoyaltyFromPurchase` property set to `0` on the BaxiCtrl instance
- `UseExtendedLocalMode` property set to `1`
- Diller API credentials (`client_id`, `client_secret`, `store_id`)

### Baxi Step 1: Read Card & Identify Member

At the start of checkout, send a **SendJson** call to the terminal with a `GetAsset` (action 193) request. This prompts the customer to tap their card. The terminal reads the card and queries Storebox for a linked member ID — **without charging the card**.

**SendJson Request:**

```json
{
  "da": {
    "ver": "1.0",
    "type": 101,
    "action": 193,
    "ra2t": {
      "ver": "1.0",
      "query": [
        {
          "mode": 4,
          "amount": "34900",
          "txntype": "30"
        }
      ]
    },
    "ra2dam": {
      "ver": "2.0",
      "requestid": "20260213143022-a1b2",
      "getasset": {
        "asset": {
          "template": {
            "name": "storebox"
          },
          "keys": [
            { "cardref": "<cardref.001>" }
          ]
        }
      }
    }
  }
}
```

| Field | Description |
|-------|-------------|
| `type: 101` | Asset query message type |
| `action: 193` | GetAsset — read card and fetch linked data |
| `mode: 4` | Transaction + asset lookup mode |
| `amount` | The basket total in **øre** (cents). 349.00 NOK = `"34900"` |
| `txntype: "30"` | Indicates a purchase context |
| `requestid` | A unique request identifier you generate (e.g., timestamp + random) |
| `template.name: "storebox"` | Target the Storebox DAM system |
| `cardref: "<cardref.001>"` | Literal placeholder — the terminal replaces this with the actual card reference after tap |

> **Important**: The `<cardref.001>` value must be sent as a literal string including the angle brackets. Ensure your JSON serializer does not escape the `<` and `>` characters.

**Calling SendJson in code:**

```csharp
var args = new SendJsonEventArgs
{
    JsonString = jsonPayload  // The JSON above as a string
};
_baxi.SendJson(args);
```

**Response — OnLocalMode / OnJsonReceived event:**

The terminal fires `OnLocalMode` (or `OnJsonReceived`) when the customer taps their card. The response contains the DAM data:

```json
{
  "da": {
    "ver": "1.0",
    "type": 101,
    "action": 193,
    "status": "5:0",
    "statustext": "OK",
    "dam2ra": {
      "requestid": "20260213143022-a1b2",
      "responseid": "6b9e963e-eec2-4615-a20f-f47d60185c8e",
      "ver": "2.0",
      "getasset": {
        "asset": {
          "template": "92debb63-0cc5-4570-aeaf-a3f9fb6c0563",
          "keys": [
            { "cardref": "57fb361c-75b6-11eb-a45e-5c4242535400" }
          ],
          "attributes": [
            {
              "payload": "{\"id\":\"800unxobgy...\",\"posapi\":{\"userId\":[{\"type\":\"pan\",\"value\":\"57fb361c-75b6-11eb-a45e-5c4242535400\"}]},\"cardlinkapi\":{...}}"
            }
          ]
        }
      }
    }
  }
}
```

**Extracting the member identifier:**

Navigate the response JSON:

1. `da.dam2ra.getasset.asset.keys[0].cardref` → the card reference UUID (e.g., `"57fb361c-75b6-11eb-a45e-5c4242535400"`)
2. `da.dam2ra.getasset.asset.attributes[0].payload` → a JSON string. Parse it to extract:
   - `memberId` or `loyaltyapi.memberId` — the Diller member ID
   - `posapi.userId[0].value` — the card reference (same as above)

Use the **member ID** to query the Diller API in the next step.

### Baxi Step 2: Query Diller for Member Details

Once you have the member identifier from Storebox, query the Diller API to get the full member profile and available coupons:

```
GET https://api.dillerapp.com/api/v2.0/stores/{storeId}/members/search?cardidentifier={memberId}
Authorization: Bearer {access_token}
```

If the member is found, fetch their coupons:

```
GET https://api.dillerapp.com/api/v2.0/stores/{storeId}/members/{memberId}/coupons/
Authorization: Bearer {access_token}
```

Display the available coupons/discounts to the cashier and let them select which ones to apply. Recalculate the basket total with the discounts.

See [Diller API Reference](#diller-api-reference) below for full endpoint details.

### Baxi Step 3: Process Payment

After applying discounts and calculating the final amount, perform a standard **TransferAmount** call to charge the card:

```csharp
var args = new TransferAmountEventArgs
{
    OperID   = "0000",
    Type1    = 0x30,           // Purchase
    Amount1  = 29900,          // 299 NOK (in øre) — the discounted total
    Type2    = 0x30,
    Amount2  = 0,
    Type3    = 0x30,
    Amount3  = 0,
    HostData = "",
    ArticleDetails = "",
    PaymentConditionCode = "",
    AuthCode = ""
};

_baxi.TransferAmount(args);
// Wait for OnLocalMode event — customer taps/inserts card and enters PIN
```

| Field | Description |
|-------|-------------|
| `Type1 = 0x30` | Purchase transaction |
| `Type1 = 0x31` | Refund transaction |
| `Amount1` | Amount in **øre** (cents). 299.00 NOK = `29900` |

The `OnLocalMode` event fires when the transaction completes, providing the result (`args.Result`), truncated PAN, auth code, timestamp, and terminal ID.

After successful payment, sync the transaction to Diller (see [Create Transaction](#create-transaction)).

### Baxi Registering a New Card

If the Storebox lookup returns no member data (card not registered), you can:

1. Ask the customer for their phone number
2. Search Diller by phone: `GET /api/v2.0/stores/{storeId}/members/search?phone={phone}`
3. If found, link the card to the member using **InsertAsset** (action 191):

**InsertAsset Request:**

```json
{
  "da": {
    "ver": "1.0",
    "type": 101,
    "action": 191,
    "ra2t": {
      "ver": "1.0",
      "query": [
        { "mode": 4 }
      ]
    },
    "ra2dam": {
      "ver": "2.0",
      "requestid": "20260213144500-c3d4",
      "insertasset": {
        "asset": {
          "template": {
            "name": "storebox"
          },
          "keys": [
            { "cardref": "<cardref.001>" }
          ],
          "attributes": [
            { "msisdn": "4712345678" },
            { "memberno": "m_abc123def" }
          ]
        }
      }
    }
  }
}
```

| Field | Description |
|-------|-------------|
| `action: 191` | InsertAsset — associate card with member |
| `msisdn` | Member's phone number with country code (e.g., `"4712345678"` for Norway) |
| `memberno` | Diller member ID (from the phone search result) |
| `cardref: "<cardref.001>"` | Terminal replaces with the tapped card's reference |

The terminal prompts the customer to tap their card. On success, the card → member link is stored in Storebox, and future taps will return the member ID automatically.

**Success response:**

```json
{
  "da": {
    "ver": "1.0",
    "type": 101,
    "action": 191,
    "status": "5:0",
    "statustext": "OK",
    "dam2ra": {
      "requestid": "20260213144500-c3d4",
      "responseid": "...",
      "ver": "2.0",
      "insertasset": {
        "asset": {
          "keys": [{ "cardref": "f37388e0-0a1f-11eb-b596-5c4242535400" }],
          "attributes": [{ "payload": "..." }]
        }
      }
    }
  }
}
```

Check `statustext === "OK"` to confirm the card was registered successfully.

---

## Tap2ID with Verifone

### Verifone Prerequisites

- **Verifone PaymentSdk** (PSDK v3.68.7 or later) — `PaymentSdk-x64-net5.0.dll`
- Verifone P630 terminal (or compatible model)
- **Tokenization** enabled on the Verifone backend for your terminal TID (contact Verifone support)
- Terminal connection via TCP/IP or serial
- Diller API credentials (`client_id`, `client_secret`, `store_id`)

The Verifone integration uses a different approach than Nets. Instead of the DAM/Storebox system, it uses the PaymentSdk's **Card Acquisition** feature to read the card and generate a cryptographic token, which is then used as the Diller identifier.

### Verifone Step 1: Card Acquisition

The SDK must be initialized and a session must be active before performing a card acquisition. The initialization sequence is:

```
1. Initialize SDK  → PaymentSdk.InitializeFromValues(listener, params)
2. Login           → TransactionManager.LoginWithCredentials(credentials)
3. Start Session   → TransactionManager.StartSession(transaction)
4. Card Acquisition → TransactionManager.RequestCardData2(request)
```

**Card Acquisition call:**

```csharp
var request = CardAcquisitionRequest.Create(
    tokenRequest: true,                 // IMPORTANT: Request tokenization
    message: "Please tap card",         // Terminal display message
    presentationMethods: new[] {
        CardPresentationMethod.CTLS_CARD,  // Contactless
        CardPresentationMethod.CHIP,        // Chip insert
        CardPresentationMethod.MAG_STRIPE   // Swipe (fallback)
    }
);

_transactionManager.RequestCardData2(request);
// Wait for CardInformationReceivedEvent callback (up to 60 seconds)
```

> **Important**: `tokenRequest: true` is essential — it tells the Verifone system to generate tokens for the card. Without this, you will not get the identifier needed for Diller. Tokenization must also be enabled on the backend/terminal configuration through Verifone support.

**Response — CardInformationReceivedEvent:**

When the customer taps their card, the SDK fires the `HandleCardInformationReceivedEvent` callback. Extract the card data:

```csharp
// In your CommerceListener2 implementation:
public override void HandleCardInformationReceivedEvent(CardInformationReceivedEvent evt)
{
    var cardInfo = evt.CardInformation;

    // Card details
    string brand    = cardInfo.PaymentBrand;      // "VISA", "MASTERCARD", etc.
    string panLast4 = cardInfo.PanLast4;          // "1234"
    string expiry   = cardInfo.CardExpiry;         // "2612" (YYMM)

    // Tokens — this is what you send to Diller
    var tokens = cardInfo.Tokens;  // Array of token objects
    foreach (var token in tokens)
    {
        string type   = token.TokenType;    // "ANALYTICS", "REUSE", etc.
        string value  = token.Value;         // The token string
        string scheme = token.Scheme;        // "VISA", etc.
    }

    // PanHandle — alternative identifier
    string panHandle = cardInfo.PanHandle;
}
```

**Token types you may receive:**

| Token Type | Description | Use for Diller? |
|------------|-------------|-----------------|
| `ANALYTICS` | Persistent card identifier generated by Verifone backend | **Yes — preferred** |
| `REUSE` | Token for reusing the card in subsequent transactions | Fallback if ANALYTICS not available |

**Use the `ANALYTICS` token** (or `REUSE` as fallback) as the card identifier for Diller member lookup.

### Verifone Step 2: Query Diller for Member Details

With the token from card acquisition, query the Diller API:

```
GET https://api.dillerapp.com/api/v2.0/stores/{storeId}/members/search?cardidentifier={analyticsToken}
Authorization: Bearer {access_token}
```

If the member is found, fetch their coupons:

```
GET https://api.dillerapp.com/api/v2.0/stores/{storeId}/members/{memberId}/coupons/
Authorization: Bearer {access_token}
```

Display coupons to the cashier, apply discounts, and calculate the final amount.

See [Diller API Reference](#diller-api-reference) below for full details.

### Verifone Step 3: Process Payment

After discounts are applied, process payment using the standard PaymentSdk flow:

```csharp
var payment = Payment.Create();

// Amount in minor units (øre/cents)
var amount = VerifoneSdk.Decimal.Create(29900);  // 299.00 NOK
payment.AmountTotals.Subtotal = amount;
payment.AmountTotals.Total = amount;

payment.Currency = "578";                         // ISO 4217 numeric (578=NOK, 752=SEK, 978=EUR)
payment.TransactionType = TransactionType.PAYMENT;
payment.Invoice = "INV-2026-001";

// Card presentation methods (customer will tap/insert again)
payment.RequestedCardPresentationMethods = new[] {
    CardPresentationMethod.CTLS_CARD,
    CardPresentationMethod.CHIP,
    CardPresentationMethod.MAG_STRIPE
};

_transactionManager.StartPayment(payment);
// Wait for PaymentCompletedEvent callback (up to 90 seconds for PIN entry)
```

**PaymentCompletedEvent:**

```csharp
public override void HandlePaymentCompletedEvent(PaymentCompletedEvent evt)
{
    var result = evt.Payment.AuthorizationResult;  // AUTHORIZED, DECLINED, etc.
    var txnId  = evt.Payment.TransactionId;
    var auth   = evt.Payment.AuthCode;

    if (result == AuthorizationResult.AUTHORIZED)
    {
        // Payment successful
    }
}
```

| Currency | ISO 4217 Numeric |
|----------|-----------------|
| NOK | `"578"` |
| SEK | `"752"` |
| EUR | `"978"` |
| DKK | `"208"` |
| GBP | `"826"` |

After successful payment, sync the transaction to Diller (see [Create Transaction](#create-transaction)).

### Verifone Registering a New Card

If the Diller API returns no member for the token (card not registered), you can:

1. Ask the customer for their phone number
2. Search Diller by phone: `GET /api/v2.0/stores/{storeId}/members/search?phone={phone}`
3. If a member is found, register the card token to that member:

```
POST https://api.dillerapp.com/api/v2.0/stores/{storeId}/members/{memberId}/register-card
Authorization: Bearer {access_token}
Content-Type: application/json

{
  "identifier": "tok_analytics_xxxx...",
  "brand": "VISA",
  "expiry_date": "26-12"
}
```

| Field | Description |
|-------|-------------|
| `identifier` | The `ANALYTICS` token value from card acquisition |
| `brand` | Normalized card brand: `"VISA"`, `"MASTERCARD"`, `"AMERICAN EXPRESS"`, etc. |
| `expiry_date` | Expiry in `"YY-MM"` format (e.g., `"26-12"` for December 2026) |

After registration, future Tap2ID lookups with this token will return the member instantly.

> **Note**: The brand should be uppercase. Common mappings: `MC` → `MASTERCARD`, `AMEX` → `AMERICAN EXPRESS`.

> **Note**: Terminal expiry formats vary. You may receive `YYMM` (e.g., `2612`), `MMYY` (e.g., `1226`), or `YY/MM`. Normalize to `YY-MM` before sending to Diller.

---

## Diller API Reference

### Authentication

Diller uses **OAuth2 Client Credentials** flow. Obtain an access token before making API calls:

```
POST https://api.dillerapp.com/connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=api.retailer&client_id={your_client_id}&client_secret={your_client_secret}
```

**Response:**
```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "api.retailer"
}
```

Cache the token and refresh when expired. Use it in all subsequent requests:

```
Authorization: Bearer {access_token}
```

| Environment | Base URL |
|-------------|----------|
| Production | `https://api.dillerapp.com` |
| Pre-release / Testing | `https://api.prerelease.dillerapp.com` |

---

### Identify Member by Card Token

Look up a member using the card token/identifier obtained from the terminal.

```
GET /api/v2.0/stores/{storeId}/members/search?cardidentifier={token}
```

**Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `storeId` | path | Your Diller store ID |
| `cardidentifier` | query | The token from card acquisition (Verifone ANALYTICS token or Nets Storebox member ID) |

**Response (member found):**
```json
[
  {
    "id": "m_nsr714RRBEHX",
    "first_name": "Ola",
    "last_name": "Nordmann",
    "phone": { "country_code": "+47", "number": "12345678" },
    "email": "ola@example.com",
    "stamps": { "current": 5, "needed": 10 },
    "points": { "current": 250 },
    "membership_level": "Gold",
    ...
  }
]
```

**Response (no member found):**
```json
[]
```

If the response is empty, the card is not registered. Proceed to phone number search or enrollment.

---

### Search Member by Phone

```
GET /api/v2.0/stores/{storeId}/members/search?phone={phoneNumber}
```

**Parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `phone` | query | Phone number (e.g., `"12345678"` or `"+4712345678"`) |

Returns the same member object structure as above.

---

### Get Member Coupons

After identifying a member, fetch their available coupons:

```
GET /api/v2.0/stores/{storeId}/members/{memberId}/coupons/
```

**Response:**
```json
[
  {
    "id": "coupon_abc123",
    "name": "20% off everything",
    "type": "percentage",
    "value": 20,
    "min_purchase_amount": 100,
    "expires_at": "2026-03-01T00:00:00Z",
    "is_redeemable": true,
    ...
  }
]
```

Apply eligible coupons to the basket, recalculate the total, and use the discounted amount for the payment step.

---

### Register Card to Member

Register a card token so the member can be identified by Tap2ID in the future. Used for Verifone (and other token-based terminals). For Nets/Baxi, use the terminal's InsertAsset (action 191) instead.

```
POST /api/v2.0/stores/{storeId}/members/{memberId}/register-card
Content-Type: application/json
Authorization: Bearer {access_token}

{
  "identifier": "tok_analytics_xxxx...",
  "brand": "VISA",
  "expiry_date": "26-12"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `identifier` | string | Card token (ANALYTICS token from Verifone) |
| `brand` | string | Card brand, uppercase: `VISA`, `MASTERCARD`, `AMERICAN EXPRESS` |
| `expiry_date` | string | Card expiry in `YY-MM` format |

**Returns:** 200 OK on success.

---

### Enroll New Member

If no member exists for the phone number, enroll a new one:

```
POST /api/v2.0/stores/{storeId}/members/enroll
Content-Type: application/json
Authorization: Bearer {access_token}

{
  "phone": {
    "country_code": "+47",
    "number": "12345678"
  },
  "department_id": "101",
  "consent": true,
  "origin": {
    "system_id": "your-pos-system",
    "employee_id": "1",
    "department_id": "101",
    "channel": "pos"
  },
  "additional_info": {
    "first_name": "Ola",
    "last_name": "Nordmann",
    "email": "ola@example.com"
  }
}
```

**Returns:** 201 Created with the new member object (includes `id`).

After enrollment, register the customer's card using either the Diller API (Verifone) or InsertAsset (Nets) for future Tap2ID recognition.

---

### Create Transaction

After a successful payment with a loyalty member, sync the transaction to Diller so points/stamps are awarded:

```
POST /api/v2.0/stores/{storeId}/members/{memberId}/transactions
Content-Type: application/json
Authorization: Bearer {access_token}

{
  "external_id": "ORDER-12345",
  "total": 299.00,
  "currency": "NOK",
  "created_at": "2026-02-13T14:30:00Z",
  "origin": {
    "system_id": "your-pos-system",
    "employee_id": "1",
    "department_id": "101",
    "channel": "pos"
  },
  "details": [
    {
      "product_id": "SKU-001",
      "product_name": "Example Product",
      "quantity": 1,
      "unit_price": 349.00,
      "total": 349.00
    }
  ],
  "coupons": [
    {
      "coupon_id": "coupon_abc123",
      "discount_amount": 50.00
    }
  ]
}
```

---

## Key Differences

| Aspect | Nets (Baxi) | Verifone |
|--------|-------------|----------|
| **SDK** | BAXI.NET (`baxi_dotnet.dll`) — COM control | PaymentSdk (PSDK) — .NET library |
| **Card Read** | `SendJson` with DAM GetAsset (action 193) | `RequestCardData2` with `tokenRequest: true` |
| **Identifier Type** | Storebox member ID (embedded on card via DAM) | ANALYTICS token (cryptographic, server-generated) |
| **Diller Lookup** | `?cardidentifier={storebixMemberId}` | `?cardidentifier={analyticsToken}` |
| **Card Registration** | Terminal-side: InsertAsset (action 191) writes to Storebox | API-side: `POST /members/{id}/register-card` writes to Diller |
| **Initialization** | `BaxiCtrl.Open()` → ready | Initialize → Login → Start Session → ready |
| **Session Management** | Not required (stateless) | Required (explicit session start/end) |
| **Amount Format** | String in øre for SendJson, integer in øre for TransferAmount | Integer in minor units (øre/cents) |
| **Currency** | Configured on terminal | Passed per-request as ISO 4217 numeric code |
| **OS Requirement** | Windows only (COM/WinForms) | Windows or Linux |
| **Threading** | STA thread required (COM) | Standard async |
| **Payment Call** | `TransferAmount` (type `0x30`) | `StartPayment` with `TransactionType.PAYMENT` |
| **Refund Call** | `TransferAmount` (type `0x31`) | `StartPayment` with `TransactionType.REFUND` |

---

## Complete Flow Diagrams

### Nets (Baxi) — Card Recognized

```
  POS                    Terminal              Storebox          Diller API
   │                        │                     │                  │
   │ SendJson(GetAsset 193) │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │ Customer taps card  │                  │
   │                        │─────────────────────►                  │
   │                        │      memberId       │                  │
   │    { memberId,         │◄─────────────────────                  │
   │      cardRef }         │                     │                  │
   │◄───────────────────────│                     │                  │
   │                        │                     │                  │
   │ GET /members/search?cardidentifier={memberId}                   │
   │────────────────────────────────────────────────────────────────►│
   │                                                { member data }  │
   │◄────────────────────────────────────────────────────────────────│
   │                        │                     │                  │
   │ GET /members/{id}/coupons                                       │
   │────────────────────────────────────────────────────────────────►│
   │                                                { coupons[] }    │
   │◄────────────────────────────────────────────────────────────────│
   │                        │                     │                  │
   │  ── Apply discounts, recalculate total ──    │                  │
   │                        │                     │                  │
   │ TransferAmount(0x30,   │                     │                  │
   │   29900 øre)           │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │ Customer taps + PIN │                  │
   │  { success, authCode } │                     │                  │
   │◄───────────────────────│                     │                  │
   │                        │                     │                  │
   │ POST /members/{id}/transactions                                 │
   │────────────────────────────────────────────────────────────────►│
```

### Verifone — Card Recognized

```
  POS                    Terminal              Verifone Cloud    Diller API
   │                        │                     │                  │
   │ Initialize + Login     │                     │                  │
   │───────────────────────►│                     │                  │
   │ Start Session          │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │                     │                  │
   │ RequestCardData2       │                     │                  │
   │  (tokenRequest=true)   │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │ Customer taps card  │                  │
   │                        │ ────────────────────►                  │
   │                        │   ANALYTICS token   │                  │
   │  CardInformation       │◄─────────────────────                  │
   │  { token, brand,       │                     │                  │
   │    last4, expiry }     │                     │                  │
   │◄───────────────────────│                     │                  │
   │                        │                     │                  │
   │ GET /members/search?cardidentifier={analyticsToken}             │
   │────────────────────────────────────────────────────────────────►│
   │                                                { member data }  │
   │◄────────────────────────────────────────────────────────────────│
   │                        │                     │                  │
   │ GET /members/{id}/coupons                                       │
   │────────────────────────────────────────────────────────────────►│
   │                                                { coupons[] }    │
   │◄────────────────────────────────────────────────────────────────│
   │                        │                     │                  │
   │  ── Apply discounts, recalculate total ──    │                  │
   │                        │                     │                  │
   │ StartPayment           │                     │                  │
   │  (29900 øre, NOK)      │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │ Customer taps + PIN │                  │
   │  PaymentCompleted      │                     │                  │
   │  { txnId, authCode }   │                     │                  │
   │◄───────────────────────│                     │                  │
   │                        │                     │                  │
   │ End Session            │                     │                  │
   │───────────────────────►│                     │                  │
   │                        │                     │                  │
   │ POST /members/{id}/transactions                                 │
   │────────────────────────────────────────────────────────────────►│
```

### Either Terminal — Card Not Recognized (First-Time Registration)

```
  POS                    Terminal              Diller API
   │                        │                     │
   │ Card read (no charge)  │                     │
   │───────────────────────►│                     │
   │  { token/cardRef }     │                     │
   │◄───────────────────────│                     │
   │                        │                     │
   │ GET /members/search?cardidentifier={token}    │
   │──────────────────────────────────────────────►│
   │                                    []  (empty)│
   │◄──────────────────────────────────────────────│
   │                        │                     │
   │  ── Cashier asks customer for phone ──       │
   │                        │                     │
   │ GET /members/search?phone=12345678            │
   │──────────────────────────────────────────────►│
   │                            { member found }   │
   │◄──────────────────────────────────────────────│
   │                        │                     │
   │  ── Register card for future Tap2ID ──       │
   │                        │                     │
   │  NETS: InsertAsset(191)│                     │
   │───────────────────────►│                     │
   │  { OK }                │                     │
   │◄───────────────────────│                     │
   │                        │                     │
   │  VERIFONE:                                    │
   │  POST /members/{id}/register-card             │
   │  { identifier, brand, expiry_date }           │
   │──────────────────────────────────────────────►│
   │  { OK }                                       │
   │◄──────────────────────────────────────────────│
   │                        │                     │
   │  ── Continue to coupons + payment ──         │
```

---

## FAQ & Troubleshooting

### General

**Q: Does the first card read (Tap #1) charge the customer?**  
A: No. Both the Nets SendJson/GetAsset and Verifone Card Acquisition are read-only operations that do not charge the card. The customer is only charged during the explicit payment step (TransferAmount or StartPayment).


**Q: What if the customer's card is not registered?**  
A: The POS should fall back to phone number lookup. If a member is found by phone, register the card (InsertAsset for Nets, register-card API for Verifone) so that future visits use Tap2ID automatically.

**Q: Can I use the same Diller credentials for both terminal types?**  
A: Yes. Diller credentials (`client_id`, `client_secret`, `store_id`) are terminal-independent. The only difference is how you obtain the card identifier.

### Nets (Baxi) Specific

**Q: SendJson returns no member data / empty attributes**  
A: Storebox/DAM must be configured on the terminal. Contact NexiGroup to enable Storebox with the `"storebox"` template for your terminal fleet.

**Q: `MethodRejectCode: 7001` (invalid JSON)**  
A: Ensure the `<cardref.001>` angle brackets are not escaped. Use a JSON serializer that preserves literal `<` and `>` characters.

**Q: `MethodRejectCode: 7002` (not ready)**  
A: The terminal is not in a ready state. Wait for the `OnTerminalReady` event before sending commands. You may need to reinitialize.

**Q: InsertAsset fails with phone number error**  
A: Ensure the phone number includes the country code prefix (e.g., `"4712345678"` for Norwegian numbers, not just `"12345678"`).

### Verifone Specific

**Q: Card acquisition returns no tokens**  
A: Tokenization must be enabled on the Verifone backend for your terminal TID. Contact your Verifone account manager to enable the ANALYTICS token type for card acquisition.

**Q: "Terminal not initialized" error**  
A: The SDK requires the full initialization sequence: Initialize → Login → Start Session. If the terminal was powered off or the connection dropped, you need to re-initialize.

**Q: Currency mismatch / wrong amount charged**  
A: Ensure the currency code is ISO 4217 numeric (e.g., `"578"` for NOK, not `"NOK"`). Amounts must be in minor units (øre/cents).

**Q: Session already open error**  
A: End the current session before starting a new one. The SDK only allows one active session at a time.

### Diller API

**Q: 401 Unauthorized from Diller API**  
A: Your access token has expired. Re-authenticate using the `/connect/token` endpoint. Tokens typically expire after 1 hour.

**Q: Card registration returns an error**  
A: Verify the brand is uppercase (`"VISA"`, not `"visa"`) and the expiry format is `"YY-MM"` (e.g., `"26-12"`, not `"2612"` or `"12/26"`).

---

*For questions about Diller API access or terminal provisioning, contact [support@diller.io](mailto:support@diller.io).*
|

Was this article helpful?

Sorry about that! Care to tell us more?

Thanks for the feedback!

There was an issue submitting your feedback
Please check your connection and try again.