Gå til hovedinnhold

# Diller Tap2ID — POS Integration Guide

Skrevet av Knut Hauge

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 follows four steps:

  1. Card read (no charge) — The terminal reads the card and returns a token or member identifier. Nets uses SendJson → member ID from Storebox. Verifone uses Card Acquisition → card token.

  2. Diller lookup — The POS sends the identifier to the Diller API, which returns member details and available coupons.

  3. Apply benefits — The POS applies coupons and discounts to the basket and calculates the final amount (e.g. 349 NOK → 299 NOK after discount).

  4. Payment (card charged) — The terminal processes a standard payment for the discounted total. The customer taps or inserts their 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)

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)

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:

{   "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:

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

Response — OnLocalMode / OnJsonReceived event:

{   "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:

  1. da.dam2ra.getasset.asset.keys[0].cardref → the card reference UUID

  2. da.dam2ra.getasset.asset.attributes[0].payload → a JSON string. Parse it to extract memberId or loyaltyapi.memberId and posapi.userId[0].value

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

Step 2: Query Diller for Member Details

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 and discounts to the cashier, let them select which to apply, and recalculate the basket total.

Step 3: Process Payment

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

After successful payment, sync the transaction to Diller (see Create Transaction below).

Registering a New Card (Nets)

If the Storebox lookup returns no member data, ask the customer for their phone number, search Diller by phone, then link the card using InsertAsset (action 191):

{   "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

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

Tap2ID with 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)

Step 1: Card Acquisition

The SDK 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)

var request = CardAcquisitionRequest.Create(     tokenRequest: true,                 // IMPORTANT: Request tokenization     message: "Please tap card",     presentationMethods: new[] {         CardPresentationMethod.CTLS_CARD,         CardPresentationMethod.CHIP,         CardPresentationMethod.MAG_STRIPE     } );  _transactionManager.RequestCardData2(request); // Wait for CardInformationReceivedEvent callback (up to 60 seconds)

Important: tokenRequest: true is essential. Tokenization must also be enabled on the backend through Verifone support.

Response — CardInformationReceivedEvent:

public override void HandleCardInformationReceivedEvent(CardInformationReceivedEvent evt) {     var cardInfo = evt.CardInformation;     string brand    = cardInfo.PaymentBrand;     string panLast4 = cardInfo.PanLast4;     string expiry   = cardInfo.CardExpiry;      var tokens = cardInfo.Tokens;     foreach (var token in tokens)     {         string type   = token.TokenType;         string value  = token.Value;         string scheme = token.Scheme;     }      string panHandle = cardInfo.PanHandle; }

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.

Step 2: Query Diller for Member Details

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}

Step 3: Process Payment

var payment = Payment.Create();  var amount = VerifoneSdk.Decimal.Create(29900);  // 299.00 NOK payment.AmountTotals.Subtotal = amount; payment.AmountTotals.Total = amount;  payment.Currency = "578";                         // ISO 4217 numeric payment.TransactionType = TransactionType.PAYMENT; payment.Invoice = "INV-2026-001";  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)
public override void HandlePaymentCompletedEvent(PaymentCompletedEvent evt) {     var result = evt.Payment.AuthorizationResult;     var txnId  = evt.Payment.TransactionId;     var auth   = evt.Payment.AuthCode; }

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 below).

Registering a New Card (Verifone)

If the Diller API returns no member for the token, ask for the customer's phone number, search by phone, then register the card:

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, uppercase: VISA, MASTERCARD, AMERICAN EXPRESS

expiry_date

Expiry in YY-MM format (e.g. "26-12" for December 2026)

Note: Terminal expiry formats vary. You may receive YYMM, MMYY, 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:

{   "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 as 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

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

Parameter

Type

Description

storeId

path

Your Diller store ID

cardidentifier

query

The token from card acquisition

Response (member found):

[   {     "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): [] — 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}

Parameter

Type

Description

phone

query

Phone number (e.g. "12345678" or "+4712345678")

Get Member Coupons

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

Response:

[   {     "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   } ]

Register Card to Member

POST /api/v2.0/stores/{storeId}/members/{memberId}/register-card Content-Type: application/json  {   "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

POST /api/v2.0/stores/{storeId}/members/enroll Content-Type: application/json  {   "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 for future Tap2ID recognition.

Create Transaction

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

POST /api/v2.0/stores/{storeId}/members/{memberId}/transactions Content-Type: application/json  {   "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: Nets vs Verifone

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

ANALYTICS token (cryptographic, server-generated)

Diller Lookup

?cardidentifier={storebixMemberId}

?cardidentifier={analyticsToken}

Card Registration

Terminal-side: InsertAsset (action 191)

API-side: POST /members/{id}/register-card

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

  1. POS sends SendJson (GetAsset 193) to terminal

  2. Customer taps card → terminal queries Storebox → returns memberId and cardRef to POS

  3. POS calls GET /members/search?cardidentifier={memberId} → Diller returns member data

  4. POS calls GET /members/{id}/coupons → Diller returns available coupons

  5. POS applies discounts and recalculates total

  6. POS sends TransferAmount (0x30, discounted amount in øre) → customer taps and enters PIN → terminal returns success and authCode

  7. POS calls POST /members/{id}/transactions to sync to Diller

Verifone — Card Recognized

  1. POS initializes SDK and logs in, starts session

  2. POS sends RequestCardData2 (tokenRequest=true) → customer taps card → Verifone Cloud returns ANALYTICS token → terminal returns CardInformation (token, brand, last4, expiry) to POS

  3. POS calls GET /members/search?cardidentifier={analyticsToken} → Diller returns member data

  4. POS calls GET /members/{id}/coupons → Diller returns available coupons

  5. POS applies discounts and recalculates total

  6. POS sends StartPayment (discounted amount, currency) → customer taps and enters PIN → terminal returns PaymentCompleted (txnId, authCode)

  7. POS ends session

  8. POS calls POST /members/{id}/transactions to sync to Diller

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

  1. Card read (no charge) → terminal returns token or cardRef

  2. POS calls GET /members/search?cardidentifier={token} → Diller returns empty result

  3. Cashier asks customer for phone number

  4. POS calls GET /members/search?phone=12345678 → Diller returns member

  5. Register card for future Tap2ID:

    • Nets: POS sends InsertAsset (action 191) to terminal → customer taps → card linked in Storebox

    • Verifone: POS calls POST /members/{id}/register-card with identifier, brand, and expiry

  6. Continue to coupons and payment as normal

FAQ & Troubleshooting

General

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

Q: What if the customer's card is not registered? The POS should fall back to phone number lookup. If a member is found by phone, register the card so that future visits use Tap2ID automatically.

Q: Can I use the same Diller credentials for both terminal types? Yes. Diller credentials are terminal-independent. The only difference is how you obtain the card identifier.

Nets (Baxi) Specific

Q: SendJson returns no member data / empty attributes 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) Ensure the <cardref.001> angle brackets are not escaped. Use a JSON serializer that preserves literal < and > characters.

Q: MethodRejectCode: 7002 (not ready) 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 Ensure the phone number includes the country code prefix (e.g. "4712345678" for Norwegian numbers).

Verifone Specific

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

Q: "Terminal not initialized" error The SDK requires the full initialization sequence: Initialize → Login → Start Session. Re-initialize if the terminal was powered off or the connection dropped.

Q: Currency mismatch / wrong amount charged 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 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 Your access token has expired. Re-authenticate using the /connect/token endpoint. Tokens typically expire after 1 hour.

Q: Card registration returns an error 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.

Svarte dette på spørsmålet?