FORMAT: 1A HOST: http://pos.prelive.qnips.com/api/pos/v3 # qnips Web API v3 Diese Seite beschreibt, wie Sie die qnips-Funktionen in Ihre Software einbinden können. Die API ist grob in 5 Bereiche unterteilt, die unabhängig voneinander implementiert werden können, sodass auch eine Teilimplementierung der qnips-Funktionalität möglich ist. Bereich | Beschreibung ---|--- **Registrierung neuer API-Clients** | Hier wird beschrieben, wie ein neuer Client sich an der API registriert und automatisch in die virtuelle Organisationsstruktur eines Händlers einordnet. **Warenkorb-Handling** | Echtzeit-Berechnung und Reporting von Rabattaktionen und Treue-Kampagnen auf Grundlage eines Warenkorbs sind Themen in diesem Bereich. **Sortimentspflege** | Hiermit lassen sich Produktdaten ins qnips-System einspielen, was folgende Funktionsbereiche ermöglicht: Produkt- und Warengruppenbasierende Treue-Kampagnen und individualisierbare Sofortrabatte, digitale Speisepläne und Speisekarten, Bestellfunktionalität. **Kunden-/Guthaben-/Geschenk-Karten** | Um die Vorteile der qnips-Funktionen nicht ausschließlich den Smartphone-Nutzern vorenthalten zu müssen, können diese auch mit einer beliebig gearteten Kundenkarte (Magnetkarte, NFC-/RFID-Transponder, Barcode-/QR-Code-Karten etc.) als Mittel zur Identifizierung eines Kunden-Profils und Bereitstellung der qnips-Angebote umgesetzt werden. Kundenkarten können in unserem System mit Guthaben aufgeladen werden, welches dann als Zahlungsmittel genutzt werden kann. **Mobile Payment** | qnips erlaubt bequeme mobile Zahlungen an Ihrem POS über diverse etablierte Zahlungsdienste.

Let's get started! # Grundlegendes Unsere API ist als REST-API realisiert. Nutzen Sie daher eine HTTP-Client-Bibliothek, um die entsprechenden GET, POST, PUT und DELETE Operationen auf einzelnen Ressourcen unserer API auszuführen. Die verfügbaren Ressourcen lassen sich über eindeutige URLs aufrufen. Beispiel: `GET https://{pos.prelive.qnips.com}/api/{v3}/{brands}/{123}` Werte in geschweiften Klammern haben jeweils folgende Bedeutung: | Code | Beschreibung | | --- | --- | |`{pos.prelive.qnips.com}` |Das ist die Haupt-Domain unserer API. Tauschen Sie diesen Wert auf produktiv eingesetzten Systemen gegen `pos.api.qnips.com` aus | |`{v3}` |Version der Schnittstelle. Aktuell ist 'v3' zu nutzen. | |`{brands}` |Name der aufzurufenden Ressource, auf welche Sie zugreifen wollen. Folgende Ressourcen sind vorhanden: merchants, trackingUnits, baskets, sortiments, products, tokens | |`{123}` |`id` des durch den Ressourcen-Namen spezifizierten Objekts, welches ausgeliefert werden soll. | ## Requests Jede Ressource unserer API ist mit zwei speziellen obligatorischen HTTP-Headern geschützt: | Header-Name| Beschreibung | | --- | --- | |`DevKey`|Hier ist Ihr DeveloperKey einzutragen, den Sie bei uns beziehen können. | |`trackingUnitId`|Jeder einzelne Client, der die qnips API aufrufen möchte, muss sich zunächst am qnips System registrieren (siehe *[Client-Registrierung](#introduction/client-registrierung)*), wodurch er in den Besitz einer eindeutigen `trackingUnitId` kommt, die hier bei jedem weiteren Call anzugeben ist.| ### Optionale Header Weiterhin gibt es einige optionale Header, mit denen z.B. das Wunschformat (JSON oder XML) für den Datenaustausch angegeben werden kann. | Header-Name| Beschreibung | | --- | --- | |`Accept`| Hiermit geben Sie an, ob Sie die Ergebnisse eines Calls im XML- oder JSON-Format erwarten. Erlaubt sind `application/xml` und `application/json`. Default ist `application/xml`.| |`Content-Type`| Hierüber geben Sie an, ob der Content im Body Ihrer POST- und PUT-Requests XML oder JSON ist. Erlaubt sind `application/xml` und `application/json`. Default ist `application/xml`.| |`Accept-Encoding`| Zur Reduzierung des Traffics und zur Beschleunigung der Interaktion mit der qnipsAPI unterstützt diese die gezippte Übertragung der Inhalte. Wird dieser Header im Request mit **'gzip'** gesetzt, so ist der Content im Response gezippt.| |`Content-Encoding`| Bei manchen POST-Calls ist es sinnvoll, auch den Inhalt des Request-Bodys gezippt zu übertragen. Setzen Sie in diesem Fall diesen Header mit **'gzip'**. Dadurch merkt das Backend, dass der Request gezippt ist und entzippt es vor der Verarbeitung| |`SecurityToken`| Manche Ressourcen sind zusätzlich mit einem dynamisch zu berechnenden Security-Token geschützt, um besonders sensible Teile der API zu schützen. Aus welchen Teilen des Requests ein solcher Token zu bilden ist, ist direkt bei der Spezifikation der Ressource beschrieben. Der Berechnungsalgorithmus ist stets derselbe und ist [hier](#introduction/grundlegendes/secutiry-token) beschrieben.| Bitte platzieren Sie im Body von Requests stets ein in `utf-8` kodiertes XML/JSON. Beachten Sie auch dass im XML bestimmte Zeichen ebenfalls nicht als Werte für XML-Tags erlaubt sind, sodass ggf. ein URL-Escaping der Werte statt finden soll *Beispiel: `Drinks&Food` müsste mindestens zu `Drinks%26Food` umgewandelt werden.*

## Responses API-Responses sind dabei normale HTTP-Responses mit einem HTTP-Statuscode, Response-Headern und ggf. Response-Body Es werden folgende HTTP-Statuscodes verwendet: | Code| Beschreibung | | --- | --- | |`200`| **Success** - Markiert die erfolgreiche Ausführung. | |`400`| **Bad Request** - Request kann aufgrund von fehlgeschlagenen Validierungen bzw. unerfüllten Vorbedingungen nicht durchgeführt werden. Bitte verarbeiten Sie bei solchen Fehlern die ErrorId im Response-Body, da in manchen Fällen eine Wiederholung des Requests Sinn macht. | |`401`| **Unauthorized** - DevKey, trackingUnitId oder Security-Token ist/sind gar nicht bzw. falsch angegeben. | |`500`| **Server error** - Während der Verarbeitung ist etwas schief gelaufen. Im Body stehen idR mehr Details zum Fehler, die für Logs und Entwickler gedacht sind, sich jedoch nicht zur Ausgabe am UI eignen.| Im Erfolgsfall wird im Response-Body gegebenenfalls ein als XML oder JSON (abhängig vom gesetzten `Content-Type`-Header) serialisiertes Objekt ausgeliefert

## Security-Token Da bei Calls der qnips-WebAPI-Ressourcen die Parameter im Klartext übertragen werden, können auch bei hinreichender SSL-Verschlüsselung die Man-in-the-Middle-Angriffe nicht vollständig ausgeschlossen werden. Daher wird qnips die Übertragung von sicherheitsrelevanten Parametern über einen sog. `SecurityToken` schützen, der vor dem Request dynamisch zu berechnen und dem Request als Header beizufügen ist und im qnips-Backend vor der Verarbeitung des Requests validiert wird. Dieser SecurityToken ist gesichert durch: + einen, den dritten unbekannten, Berechnungsalgorithmus + einen je `trackingUnit` individuellen Geheimschlüssel (`checksumSecret`), der bei der Registrierung der trackingUnit zugewiesen wird (siehe [GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)) Jede, mit einem SecurityToken geschützte Ressource definiert, für welche Bestandteile der Eingabeparameter ein `SecurityToken` zu bilden ist. Die Methode zur Berechnung des SecurityTokens nimmt also einen String an und liefert den Token ebenfalls als String zurück. Hier der Algorithmus für diese Methode: + einen String mit `input` + `„qPnsS14“` + `checksumSecret“` bilden, mit: + `input`: der Wert, für den die Prüfsumme berechnet werden soll + `„qPnsS14“`: ein konstanter String + `checksumSecret`: Schlüssel, welcher der`trackingUnit` bei der Registrierung zugewiesen wurde Beispiel mit `input`=„123456“ und `checksumSecret`=„af87b1“: **„123456qPnsS14af87b1“** + Für das Ergebnis aus dem ersten Schritt einen MD5-Hash bilden. Für den Wert aus dem oben gezeigten Beispiel, würde der MD5-Hash folgender sein: **„1283ff82ae8e9ace636449a519d99fa9“** + Dieser Hash ist der `SecurityToken`. Packen Sie ihn nun in den `SecurityToken`-Header des Requests, damit der Request an unserem Backend verarbeitet werden kann.


# Begriffsdefinition Diese API-Beschreibung nutzt einige Begriffe, die wie folgt zu verstehen sind: * **merchant (Händler)** - ein Händler ist ein durch seinen eindeutigen qnips-Usernamen (z.B. 'max@mustermann.de') differenzierbarer Träger von einem oder mehreren Verkaufsstandorte (*outlet*). Die *outlets* können ihrerseits in einer oder mehreren Handelsmarke (*brand*) eingeordnet sein. * **brand (Handelsmarke)** - eine Handelsmarke ist in der Regel ein Verbund mehrerer Standorte (*outlet*), die unter einer einheitlichen Marke bzw. Corporate Identity betrieben werden (z.B. 'Pizza Hut'). Ein *merchant* kann dabei Besitzer von mehreren solchen *brands* sein und sie als eigenständige Einheiten in qnips verwalten. * **outlet (Verkaufsstandort)** - ein Verkaufsstandort ist in der Regel eine Filiale, die durch ihre eindeutige Adresse von anderen Filialen (bzw. Verkaufsstandorten) unterschieden wird. * **trackingUnit (POS-Terminal)** - gibt es in einem *outlet* mehrere Orte, an denen Rechnungen mit eigenständigen Rechnungsnummernkreisen abgewickelt werden können, so ist jeder solche Ort als sog. *trackingUnit* in unserem System eigenständig zu registrieren. Wird die Rechnungsstellung auch bei mehreren Terminals zentral abgewickelt, so reicht es nur die zentrale Einheit als *trackingUnit* zu registrieren. * **sortiment (Produktstamm)** - Ein Produktstamm ist eine Organisationseinheit für die vom *merchant* als Reward-fähig markierte Verkaufsartikel, die am POS in einen *basket* gebucht werden können. Solche Artikel müssen dem qnips-System durch einen Upload bekannt gemacht werden, damit der *merchant* einen Reward auf diese Artikel definieren kann. * **basket (Warenkorb)** - das ist ein Objekt, welches die Daten über die vom Konsumenten gekauften Positionen beinhaltet, so wie sie im Kontext einer Rechnung in einer *trackingUnit* erfasst wurden * **consumer (Konsument)** - damit ist ein Endkunde gemeint, der in einem *store* eines *merchants* einen *basket* erwirbt und dieses Kauf-Ereignis ( *purchase* ) entweder über einen Scan des QR-Codes oder über Identifikation am POS mit seinem Konsumenten-Profil im qnips-System verknüpft * **consumerIdentToken** - damit ist ein auf einem beliebigen Trägermedium gespeicherter, beliebig gearteter `Token` gemeint, der eindeutig mit einem *consumer*-Profil verknüpft ist und somit dafür geeignet ist, den Konsumenten eindeutig zu identifizieren. Dieser Token kann z.B. in einer Kundenkarte gespeichert sein, oder in Form eines gedruckten oder virtuellen Barcodes oder QR-Codes existieren, der vom POS-System entweder eingescannt oder über ein anderes Trägermedium eingelesen werden kann.


# Client-Registrierung Jede Instanz in Ihrem System, die eigenständig eine Rechnung erzeugen und abschließen kann, muss eine eindeutige `trackingUnitId` vom qnips-System erfragen. Dazu ist, sofern der Client noch nicht über eine trackingUnitId verfügt, einmalig ein *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)*-Call auszuführen und das Ergebnis des Calls in der aufrugenden Instanz dauerhaft zu persistieren. Dabei ist es wichtig, für jede zu registrierende *trackingUnit* einen Bezug zum `outlet` herzustellen. Dazu muss muss beim *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)*-Call eine `outletId` angegeben werden. Diese kann auf zweierlei Wegen geholt werden: * **[GET merchants-Info](#reference/merchants/merchant-info-abrufen/get-merchants-info)** - Rufen Sie mit diesem Call die für einen `merchant` in unserem System verfügbaren `outlets` ab und lassen Sie das gewünschte `outlet` auf dem UI auswählen * **[POST outlet](#reference/merchants/outlet-erstellen/post-outlet)** - Verfügt Ihr System über die Adresse des Standorts, in welchem es eingesetzt wird, kann ein `outlet` auch aus Ihrem System heraus über einen API-Call neu angelegt werden. Im Response auf diesen Call finden Sie die benötigte `outletId` Wird die Kasse irgendwann an einen anderen Standort verbracht, sollte die neue `outletId` unserem System über Standortänderung entweder über einen *[POST trackingUnits](#reference/trackingunits/registrieren/post-trackingunits)*-Call mitgeteilt werden.


# Warenkorb-Handling Mit Warenkorb-Handling ist eine Echtzeitberechnung von möglichen im qnips-System definierten Rabatten und Treuepunkten auf einen bestimmten Warenkorb gemeint. Dieser Prozess setzt sich aus Calls auf zwei Ressourcen unserer API zusammen: * **[POST basket](#reference/baskets/basket-hochladen/post-basket)** - damit wird der Warenkorb unter Nutzung einer eindeutigen ID eingereicht, mögliche Rabatte und Treuepunkte berechnet und im Response an die Kasse zurückgegeben. Ein Warenkorb kann mehrfach neu eingereicht werden, wobei jede Neueinreichung die Berechnung der Rabatte und Treuepunkte neu in Gang setzen würde. * **[POST basket-grant](#reference/baskets/rewards-als-granted-markieren/post-basket-grant)** - damit wird ein Warenkorb als abgeschlossen markiert. So markierte Warenkörbe lassen sich nicht neu berechnen. qnips bietet folgende Reward-Typen an: + Umsatzbasierte Coupons (z.B. ab 20€ Umsatz 10% Rabatt) + Produktbasierte Coupons (z.B. 20% auf alle Herrenstiefel) + Coupons auf Produktkombinationen (z.B. Schnürsenkel zum Herrenschuh zum halben Preis) + Mengencoupons (z.B. 3 zum Preis von 2) + Umsatz- und/oder produktbasierte Punkte-Sammelsysteme mit flexibel gestaltbaren Rabatten Der vollständige Prozess der Reward-Anrechnung sieht wie folgt aus: * *1. Erfassen Sie den Warenkorb* * *2. Führen Sie ggf. eine Konsumenten-Identifizierung durch*
Lesen Sie dazu von einer am POS vom Konsumenten vorgelegten Kundenkarte oder Smartphone über ein geeignetes Lesegerät (z.B. Imager, Magnetkartenleser, NFC/RFID-Leser etc. den darin gespeicherten Kundenidentifier aus und speichern Sie diesen als sogenannten *consumerIdentToken* zwiscen. * *3. Rufen Sie [POST basket](#reference/baskets/basket-hochladen/post-basket) auf*
Der Response auf diesen POST könnte wie folgt aussehen: { "Rewards":[ { "ProductId": "20019", "Name": "30% auf Großes Kaffeegetränk und einen Donut", "GrossRewardValue": 0.72, "CouponId": 683, "RewardTriggeringPositionId": 1, "RewardType": 1 }, { "ProductId": "151", "Name": "30% auf Großes Kaffeegetränk und einen Donut", "GrossRewardValue": 0.53, "CouponId": 683, "RewardTriggeringPositionId": 2, "RewardType": 1 } ], "LoyaltyInfo": [ { "Name": "Ein Punkt für jedes Heißgetränk", "LoyaltyId": 171, "Details": [ { "PreviousPoints": 7, "NewPoints": 2, "PointsInScheme": 10, "GrossRewardValue": 0, "RewardTriggeringPositionId": 1 } ] } ] } * *4. Verarbeiten Sie `Rewards` im Response*
Sie können nun die Rewards direkt in die Rechnung einarbeiten und den Rechnungsbetrag entsprechend reduzieren. * *5. Warenkorb als abgeschlossen markieren*
Solange ein basket nicht abgeschlossen (`granted`) ist, kann er beliebig oft neu eingereicht werden. Mit jeder Neueinreichung würden die Rewards neu berechnet werden und die alten ersetzen. Ist jedoch aus der Sicht des Konsumenten ein finaler Stand erreicht und der Rechnungsbetrag beglichen, muss der basket in unserem System als `granted` markiert werden. Erst dadurch wird unser System eine Notification über einen getätigkten Kauf an den Konsumenten verschicken (per Email oder per Push, wenn der Konsument ein qnips-App-Nutzer ist). Rufen Sie dazu *[POST basket-grant](#reference/baskets/rewards-als-granted-markieren/post-basket-grant)* auf


# Sortimentspflege Unter Sortimentspflege verstehen wir den initialen Upload sowie das Updaten der Produkt-/Artikel-Daten in unserem System. Die Sortimentspflege ist eine Voraussetzung für folgende qnips-Features: + produktbezogene Rewards Für produktbezogene Rewards ist es ausreichend, nur solche Produkte (bzw. Produktgruppen) an das qnips-System zu übermitteln, für die der merchant einen Reward einrichten will. Hierbei reicht es, wenn die Artikel in Ihrer Minimalform (nur PLU und Name, sowie ggf. eine Warengruppenzuordnung) eingereicht werden. + Menükarten-Anzeige in der App inkl. Allergen-/Nährwert-/Zusatzstoffe-Angaben Speziell für Gastronomie und Gemeinschaftsverpflegung im Speziellen bieten wir die Funktion eines umfangreichen, LMIV-konformen Speiseplans mit eigenem Allergen-/Zusatzstoff-Management auf Artikelebene. Verfügt Ihr System über solche Angaben, können sie ebenfalls an unserem System hochgeladen werden, sodass das Allergen-/Zusatzstoff-Management weiterhin in Ihrer Software verbleibt und lediglich die Zusammenstellung des Speiseplans über uns erfolgen kann *HINWEIS: Implementieren Sie das Feature bitte so, dass der merchant letztendlich entscheiden kann, in welchem Umfang ein Sortiment an qnips hochgeladen wird, indem Sie in Ihrem System z.B. eine Möglichkeit schaffen, die an qnips zu übertragenen Artikel als solche zu markieren.* ## Sortimente Artikeldaten sind im qnips-System in so genannten `sortimenten` organisiert. Ein sortiment kann von mehreren `trackingUnits` an einem oder auch an unterschiedlichen Standorten gemeinsam verwendet werden. Das einzige Unterscheidungsmerkmal, ob ein Artikel von mehreren trackingUnits im selben Sortiment gepflegt werden kann, ist die Eindeutigkeit seiner PLU → ist unter der selben PLU auf zwei trackingUnits ein unterschiedlicher Artikelname erfasst, so müssen diese Artikel in unterschiedliche Sortimente geladen werden. Rufen Sie dazu [GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}](#reference/sortiments/sortimentid-besorgen) auf. ## Upload Die Artikeldaten lassen sich sowohl einzeln als auch per BulkUpload hochladen bzw. updaten: + [POST / PUT / DELETE sortiments/{qnipsSortimentId}/group](#reference/sortiments/warengruppen-bearbeiten) + [POST / PUT / DELETE sortiments/{qnipsSortimentId}/article](#reference/sortiments/artikel-bearbeiten) + [POST sortiments/{qnipsSortimentId}/bulkUpload?changedArticlesOnly={boolean}](#reference/sortiments/bulkupload)


# Kundenkarten Kundenkarten sind neben QR-Code-Scans eine weitere Möglichkeit, einen `basket` mit einem bestimmten Konsumenten-Profil zu verknüpfen. Das Verfahren dabei ist recht einfach: am POS wird ein beliebig gearteter Identifier von irgendeinem vom Konsumenten am POS vorgezeigten Identifier-Träger ausgelesen und als `consumerIdentToken` zusammen mit einem `basket` an unserem System eingeschickt. Daher ist es für uns unwichtig, ob dieser Token + auf einem Magnetstreifen oder NFC-Chip einer Plastikkarte + im Barcode oder QR-Code einer Papierkarte + in einem RFID-Transponder + als virtueller Token auf dem Smartphone des Konsumenten + oder sonstiges gespeichert ist. Genauso unwichtig ist es, von wem ein solcher Token generiert worden ist, solange er unveränderbar und eindeutig genug ist, um seinen Halter von anderen Konsumenten unterscheiden zu können. ## Kundenkarte registrieren Eine Kundenkarte lässt sich daher einfach realisieren, indem der in der Karte gespeicherte Karten-Identifier als sogenannter `token` in unserem System bekannt gemacht wird. Lesen Sie dazu den Identifier von einer Karte aus, die Sie an den Konsumenten als Kundenkarte aushändigen wollen und führen Sie [POST tokens/{id}](#reference/tokens/token-registrieren/post-tokens%2F%7Bid%7D%3Ftokentype%3D%7Btokentype%7D) unter Angabe dieses Identifiert als `{id}` aus. ## Kundenkarte sperren Sperrung von Kundenkarten kann vom Merchant entweder direkt im Webportal vorgenommen werden, oder, wenn Ihre Software die führende Verwaltungsstelle für Kundenkarten darstellt, über einen API-Call uns mitgeteilt werden. Führen Sie dazu eine der beiden Aktionen aus: + [POST tokens/{id}/lock](#reference/tokens/token-sperren/post-tokens%2F%7Bid%7D%2Flock) Diese Aktion macht eine Karte komplett unbrauchbar. + bzw. [POST tokens/{id}/dispose](#reference/tokens/token-freigeben/post-tokens%2F%7Bid%7D%2Fdispose) Entbindet die Karte lediglich von einem Konsumenten-Profil, die Karte selbst steht jedoch für eine neue Registrierung und eine Verwendung durch einen anderen Konsumenten weiterhin zur Verfügung ## Karte mit Guthaben aufladen Kundenkarten können mit einem Guthaben aufgeladen werden. Die Aufladung kann einerseits vom Konsumenten über unser Konsumenten-Portal aufgeladen werden (z.B. mittels einer Überweisung oder via PayPal). Andererseits wäre es sinnvoll, eine Auflademöglichkeit auch am POS zu schaffen. Stellen Sie dazu einem Konsumenten eine Rechnung aus, ziehen Sie das Geld ein und buchen Sie das Guthaben auf seine Kundenkarte, indem Sie den `token` seiner Kundenkarte erfassen und den folgenen Call durchführen: [POST tokens/{id}/addBalance?balanceToAdd={balanceToAdd}](#reference/tokens/guthaben-aufladen/post-tokens%2F%7Bid%7D%2Faddbalance%3Fbalancetoadd%3D%7Bbalancetoadd%7D) ## Guthaben abheben Liegt auf einer Kundenkarte ein Guthaben und wird von Ihrem System ein `basket` mit dem `token` dieser Kundenkarte eingeschickt, so wird der Response zum [POST basket/{basketId}?consumerIdentToken={token1}](#reference/baskets/basket-hochladen/post-baskets%2F%7Bbasketid%7D%3Fconsumeridenttoken%3D%7Btoken1%7D%26giftcardid%3D%7Btoken2%7D) die Höhe dieses Guthabens enthalten. Wird der Anteil dieses Guthabens mit dem Rechnungsbetrag verrechnet, so rufen Sie [POST tokens/{id}/withdrawBalance?balanceToWithdraw={balanceToWithdraw}](#reference/tokens/guthaben-abheben/post-tokens%2F%7Bid%7D%2Fwithdrawbalance%3Fbalancetowithdraw%3D%7Bbalancetowithdraw%7D) auf.


# Geschenkkarten Eine `Geschenkkarte` wird in unserem System komplett wie eine `Kundenkarte` behandelt, mit dem einzigen Unterschied, dass der Halter einer Geschenkkarte im Unterschied zum Halter einer Kundenkarte keinen öffentlichen Zugang über das Konsumenten-Portal zu den Aktivitäten (Auflade- und Entlade-Vorgänge, mit der Karte gekaufte und ggf. bezahlte `baskets` etc.) erhält. *Implementierungstechnisch ist eine `Geschenkkarte` jedoch komplett zur `Kundenkarte` identisch*


# Group merchants Wie im Kapitel [Client-Registrierung](#introduction/client-registrierung) beschrieben, muss sich jeder Client an unserem System zunächst als eine `trackingUnit` registrieren. Für diese Registrierung wird eine sogenannte `outletId` benötigt, die uns hilft, die `trackingUnit` sofort zu einer realen Filiale/Standort eines `merchants` zuzuordnen. Eine `outletId` lässt sich auf zwei unterschiedlichen Wegen besorgen: + Abrufen von bereits existierenden `brands` und `outlets`, um eine zu registrierende `trackingUnit` in einem bestehenden `outlet` einzuordnen. Bauen Sie dies wie folgt ein: * Fragen Sie bei der qnips-Aktivierung den Usernamen des qnips-Accounts ab * Führen Sie *[GET merchants/{qnipsUserName}/info](#reference/merchants/merchant-info-abrufen/get-merchants/{qnipsusername}/info)* aus und stellen Sie das Ergebnis im UI dar * Lassen Sie den Bediener eine `outletId` auswählen * Registrieren Sie die `trackingUnit` über *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)* + Verfügt Ihre Software über Standortinformationen, bestehend mind. aus PLZ, Stadt, Straße und Straßennummer, ist es möglich, einen automatischen Export der Standorte aus Ihrem in unser System zu ermöglichen. Bauen Sie dies ggf. wie folgt ein: * Fragen Sie bei der qnips-Aktivierung den Usernamen des qnips-Accounts ab * Führen Sie *[GET merchants/{qnipsUserName}/info](#reference/merchants/merchant-info-abrufen/get-merchants/{qnipsusername}/info)* aus und stellen Sie das Ergebnis im UI dar * Lassen Sie den Bediener eine `brandId` auswählen * Erstellen Sie in der Brand einen neuen Outlet über *[POST merchants/{qnipsUserName}/outlets](#reference/merchants/outlet-erstellen/post-merchants/{qnipsusername}/outlets)* * Registrieren Sie die `trackingUnit` über *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)* ## Merchant Info abrufen [/merchants/{qnipsUserName}/info] ### GET merchants-Info [GET] Im Response erhalten Sie eine Liste der `brands`, welche für den `merchant` mit dem angegebenen `qnipsUserName` in unserem System hinterlegt sind. Dabei kann in jedem brand-Objekt eine Liste der `outlets` mit jeweils einer Id und einem Namen stecken. Beispiel: [ { "brandId": 1, "brandName": "Caesar's", "outlets": [ { "Id": 1, "Name": "Caesar's Hemmingen - (Hemmingen, Rathausplatz 6A)" }, { "Id": 2, "Name": "Caesar's Hannover - (Hannover, Weidendamm 8)" } ] } ] | Rückgabe | Beschreibung | | --- | --- | |`brandId`|Id des Brands.| |`brandName`|Name des Brands, welchen Sie z.B. in einer DropDown-Box im UI zur Selektion anzeigen könnten.| |`outlet.Id`|Id des Standort-Eintrags (Filiale) .| |`outlet.Name`|Name der Filiale, welchen Sie z.B. in einer DropDown-Box im UI zur Selektion anzeigen könnten.| Sie können nun entweder unter Nutzung einer vom Nutzer selektierten `brandId` einen neuen Outlet anlegen (siehe *[POST merchants/{qnipsUserName}/outlets](#reference/merchants/outlet-erstellen/post-merchants/{qnipsusername}/outlets)*) oder unter Nutzung einer selektierten `outletId` mit direkter Registrierung einer `trackingUnit` fortfahren (siehe *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)*) + Parameters + qnipsUserName (string, `hans.m%C3%B6ller@firma.de`) ... Lassen Sie den Merchant seinen `qnipsUserName` eintragen und fügen Sie diesen hier ein. Der Wert sollte URL-konform escaped werden, da er Umlaute enthalten kann, z.B. `'hans.möller@firma.de' -> 'hans.m%C3%B6ller@firma.de'`. + Request + Headers Accept: application/json DevKey: {Ihr Key} + Response 200 [ { "brandId": 1, "brandName": "Caesar's", "outlets": [ { "Id": 1, "Name": "Caesar's Hemmingen - (Hemmingen, Rathausplatz 6A)" }, { "Id": 2, "Name": "Caesar's Hannover - (Hannover, Weidendamm 8)" } ] } ] + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey nicht angegeben oder nicht vorhanden + Response 404 // HTTP-NotFound: qnipsUserName unbekannt oder hat keine brands ## Outlet erstellen [/merchants/{qnipsUserName}/outlets?brandId={brandId}] ### POST outlet [POST] Der Body des Requests zum Anlegen eines neuen Outlets kann folgende Parameter übernehmen (alle optional): { "Name":"Demo Filiale", "Description":"Demo Filiale", "Street":"Schulenburger Landstraße 156", "City":"Hannover", "PostalIndex":"30419", "CountryIso":"DE", "PublicTelefon":"0511-12345", "PublicEmail":"example@example.de", "WebsiteUrl":"http://...", "FacebookUrl":"http://...", "TwitterUrl":"http://...", } | Feld | Beschreibung | | --- | --- | |`Name`|**string : optional** - Name der Filiale. Dieser Wert wir in den Smartphone-Apps für die Öffentlichkeit sichtbar sein.| |`Description`|**string : optional** - Längere Beschreibung zur Filiale sofern vorhanden.Dieser Wert wir in den Smartphone-Apps für die Öffentlichkeit sichtbar sein.| |`Street`|**string : optional** - Straßenname mit Straßennummer| |`City`|**string : optional** - Stadtname| |`PostalIndex`|**string : optional** - Postleitzahl| |`CountryIso`|**string : optional** - 2-buchstabiger Länder-Kürzel nach ISO 3166 .| |`PublicTelefon`|**string : optional** - Dieser Wert wir in den Smartphone-Apps als Kontakttelefon für alle sichtbar sein.| |`PublicEmail`|**string : optional** - Dieser Wert wir in den Smartphone-Apps als Kontaktemail für alle sichtbar sein.| |`WebsiteUrl`|**string : optional** - Url zur Website der Filiale, wenn vorhanden.| |`FacebookUrl`|**string : optional** - Url zur Facebook-Page der Filiale, wenn vorhanden.| |`TwitterUrl`|**string : optional** - Url zur Twitter-Page der Filiale, wenn vorhanden.| Im Response wird ein Objekt mit einer `Id` und einem `Name` zu dem neu erstellten `outlet`-Objekt geliefert. Beispiel: { Id: 10125, Name: "Caesar's Hannover - (Hannover, Weidendamm 8)", } Mit dieser kann dann eine Registrierung der trackingUnit vorgenommen werden (siehe *[GET trackingUnits](#reference/trackingunits/registrieren/get-trackingunits)*) + Parameters + qnipsUserName (string, `hans.m%C3%B6ller@firma.de`) ... Lassen Sie den Merchant seinen `qnipsUserName` eintragen und fügen Sie diesen hier ein. Der Wert sollte URL-konform escaped werden, da er Umlaute enthalten kann, z.B. `'hans.möller@firma.de' -> 'hans.m%C3%B6ller@firma.de'`. + brandId (long, `123`) ... Hier sollte die ID des ausgewählten Brands stehen, in welchem das Outlet angelegt werden soll + Request + Headers Accept: application/json DevKey: {Ihr Key} + Body { "Name":"Demo Filiale", "Description":"Demo Filiale", "Street":"Schulenburger Landstraße 156", "City":"Hannover", "PostalIndex":"30419", "CountryIso":"DE", "PublicTelefon":"0511-12345", "PublicEmail":"example@example.de", "WebsiteUrl":"http://...", "FacebookUrl":"http://...", "TwitterUrl":"http://...", } + Response 200 { Id: 10125, Name: "Caesar's Hannover - (Hannover, Weidendamm 8)", } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey nicht angegeben oder nicht vorhanden + Response 404 // HTTP-NotFound: qnipsUserName unbekannt oder hat keine brands #Group trackingUnits ## Registrieren [/trackingUnits?qnipsUserName={qnipsUserName}&outletId={outletId}&sortimentId={sortimentId}&instanceName={instanceName}] ### GET trackingUnits [GET] Jeder Call auf diese Ressource wird immer einen neuen, sich vom vorherigen Call unterscheidenden Identifier, die sog. `trackingUnitId` sowie ein `checksumSecret` im Response liefern. | Rückgabe | Beschreibung | | --- | --- | |`trackingUnitId`|Diese Id werden Sie bei jedem weiteren Call im `TrackingUnitId-Request-Header` setzen müssen, ohne welchen jeder Call von unserem Backend abgewiesen würde.| |`checksumSecret`|Diesen `Secret` werden Sie zur Bildung einer Prüfziffer benötigen, die bei einigen Calls unserer API als Sicherheitskriterium mit übermittelt werden muss.| Rufen Sie diese Ressource daher in jedem zu registrierenden Terminal nur einmal auf, persistieren Sie die beiden Werte aus dem Response und sorgen Sie dafür, dass der `checksumSecret` geheim bleibt. + Parameters + qnipsUserName (`hans.m%C3%B6ller@firma.de`) ... Lassen Sie den Merchant seinen qnips-Username eintragen und geben Sie diesen hier an. Der Wert sollte URL-konform escaped werden, da er Umlaute enthalten kann: *hans.möller@firma.de -> hans.m%C3%B6ller@firma.de* + outletId (long, `123`) ... Hier bitte die Id des outlets angeben, in welchem die trackingUnit zu registrieren ist. + sortimentId (optional, long, `234`) ... Hier bitte optional die Id des Sortiments angeben, auf welchem die trackingUnit arbeitet + instanceName (optional, string, `Kasse%20im%20Gang%201`) ... Lassen Sie den Merchant im Zuge der qnips-Aktivierung einen Namen für die zu aktivierende Instanz eintragen und geben Sie diese hier an. Der hier eingegebene Name wird im qnips-Webportal für den Merchant sichtbar sein und hilft dem Merchant, diese Instanz ggf. korrekt einzuordnen. Der Wert sollte URL-konform escaped werden, da er z.B. Umlaute und Leerzeichen enthalten kann: *'Kasse im Gang 1' -> 'Kasse%20im%20Gang%201'* + Request + Headers Accept: application/json DevKey: {Ihr Key} + Response 200 { "trackingUnitId" = "1234567890", "checksumSecret" = "def567", } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 10001: unbekannter qnipsUserName // 10002: kein Instanz-Name angegeben ### POST trackingUnits [POST] Wird eine Kasse in eine andere Filiale gebracht, oder auf ein anderes Sortiment umgeschaltet, sollte dies unserem Systemüber einen POST-Call auf die trackingUnits-Ressource mitgeteilt werden. Damit ordnen wir die trackingUnit entsprechend neu ein. Im Erfolgsfall wird ein leerer HTTP-200-Response geliefert + Parameters + qnipsUserName (`hans.m%C3%B6ller@firma.de`) ... Lassen Sie den Merchant seinen qnips-Username eintragen und geben Sie diesen hier an. Der Wert sollte URL-konform escaped werden, da er Umlaute enthalten kann: *hans.möller@firma.de -> hans.m%C3%B6ller@firma.de* + outletId (long, `123`) ... Hier bitte die Id des outlets angeben, in welchem die trackingUnit zu registrieren ist. + sortimentId (optional, long, `234`) ... Hier bitte optional die Id des Sortiments angeben, auf welchem die trackingUnit arbeitet + instanceName (optional, string, `Kasse%20im%20Gang%201`) ... Lassen Sie den Merchant im Zuge der qnips-Aktivierung einen Namen für die zu aktivierende Instanz eintragen und geben Sie diese hier an. Der hier eingegebene Name wird im qnips-Webportal für den Merchant sichtbar sein und hilft dem Merchant, diese Instanz ggf. korrekt einzuordnen. Der Wert sollte URL-konform escaped werden, da er z.B. Umlaute und Leerzeichen enthalten kann: *'Kasse im Gang 1' -> 'Kasse%20im%20Gang%201'* + Request + Headers Content-Type: application/json DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden #Group baskets ## Basket hochladen [/baskets/{basketId}?consumerIdentToken={token1}&giftCardId={token2}] ### POST basket [POST] Ein basket ist der Inhalt einer Rechnung und stellt die Grundlage für die meisten qnips-Funktionen dar. Das Ergebnis dieses Calls beinhaltet immer die möglichen `Rewards`, für die im basket enthaltenen Produkte. Diese errechneten `Rewards` stellen ein vorläufiges Ergebnis fest und müssen von Ihrer Software als `granted` markiert werden, sobald der Rechnungsbetrag voll beglichen ist, damit die Rewards wirksam werden und dem consumer als eingelöst gemeldet werden können (siehe *[POST baskets/{basketId}/grant](#reference/baskets/rewards-als-granted-markieren/post-baskets%2F%7Bbasketid%7D%2Fgrant%3Fredeemedinpos%3D%7Bredeemedinpos%7D)*) Wurden beim Call ein *consumerIdentToken* oder eine *giftCardId* angegeben, so kann der Response auch die Information über potentiell vorhandene Guthaben entweder auf dem Konsumenten-Profil oder auf der Geschenk-Karte enthalten. Solange ein basket nicht `granted` ist, kann er über den erneuten Call auf diese Ressource beliebig oft geupdated werden, z.B. weil kurzerhand noch eine Position nachgebucht oder gestrichen wurde (ein häufiger Fall in der Gastronomie). Jeder neue Call wird dabei eine erneute Berechnung der Rewards anstoßen und ausliefern. #### Parameter im Request-Body Da die Bedeutung der meisten Properties über den Namen klar sein sollte, hier nur einige wenige Erklärungen zu den erklärungsbedürftigen Properties. | Name | Beschreibung | | --- | --- | |`Timestamp`|**DateTime : required** - hier sollte der Zeitpunkt der Rechnungsstellung im *[ISO8601-Format](http://de.wikipedia.org/wiki/ISO_8601)* inkl. Zeitzone angegeben werden. Beispiel: 2015-04-30T08:45:15+02:00 für 08:45:15 Uhr am 30. April 2015 in Berlin (MESZ – Sommerzeit)| |`HasDiscounts`|**bool : required** - wird hier true angegeben, findet für diesen basket keine Reward-Berechnung im qnips-System statt| |`CurrencyISO`|**string : optional** - Geben Sie die Währung, in der abgerechnet wird, als 3-stellige Abkürzung nach *[ISO 4217](http://de.wikipedia.org/wiki/ISO_4217)*| |`WaiterName`|**string : optional** - hier sollte der Name des Bedieners/Kassierers stehen, der den Kunden maßgeblich bedient/beraten hat| |`BillPdf`|**Byte-Array : optional** - Um das Features des Digitalen Bons auch den Konsumenten ohne Smartphone anbieten zu können, kann Ihre Software die Kopie der Rechnung als eine PDF-Datei an uns schicken, damit wir diese einerseits im qnips Consumer Portal anzeigen, aber auch direkt per Email an den Konsumenten verschicken können. Schicken Sie hier dazu den Inhalt der PDF-Datei als base64-kodierter Byte-Array| |`ContextIdentifiers`|**String-Array : optional** - Wurden zum Warenkorb Coupon-Identifiers oder ein Profil-Identifier beigefügt (siehe *[Context-Identifiers](https://qnipsapi1draft.docs.apiary.io/#reference/contextidentifiers)*), dann sollten sie hier angegeben werden | |`RegularGrossPrice`|**double : optional** - hier sollte der normale Verkaufspreis für den Artikel angegeben werden| |`CurrentGrossPrice`|**double : optional** - hier sollte der Preis stehen, unter welchem dieser Artikel in dieser Rechnung verkauft wird, z.B. wenn irgendwelche Discounts (Aktionen, Mitarbeiterrabatte etc.) auf diesen Artikel angewendet wurden.| |`PositionId`|**int** - geben Sie hier eine eindeutige Nummer, welche die Position referenzierbar macht. Dies wird benötigt, um einen von unserem System evtl. errechneten Rabatt auf die inh auslösende Position zu referenzieren. | |`ProductId`|**string : optional** - geben Sie hier die PLU des Produkts, unter welcher das Produkt an unser System eingereicht wurde, damit wir dieses korrekt für die Berechnung der produktbasierten Rewards erkennen können. Haben Sie in Ihrer Software keine Unterstützung für produktbasierte Rewards implementiert, können Sie dieses Feld auslassen| |`OrderTime`|**DateTime : optional** - in der Gastronomie ist häufig anzutreffen, dass eine Rechnung längere Zeit offen steht und Positionen nach und nach gebucht werden. Ist dies bei Ihnen auch der Fall, geben Sie hier den genauen Buchungszeitpunkt der jeweiligen Position im *[ISO8601-Format](http://de.wikipedia.org/wiki/ISO_8601)* (inkl. Zeitzone) an. Dies wird uns erlauben, sog. “Happy Hour”-Rewards auf eine Position genau berechnen zu können. Beispiel: 2015-04-30T08:45:15+02:00 für 08:45:15 Uhr am 30. April 2015 in Berlin (MESZ – Sommerzeit)| |`Unit`|**int** - Das ist die Einheit der Mengenangabe in Amount. 0 für Stückzahl, 1 für Kilogramm. | |`Amount`|**decimal** - Mengenangabe des Artikels. Kann als Stückzahl oder Kilogramm angegeben werden (siehe Unit). | |`ProductProps`|**dictionary[string, string] : optional** - Haben Ihre Produkte besondere Eigenschaften, die auf dem Digitalen Kassenbon strukturiert angezeigt werden sollen, so ist diese Liste der Key-Value-Pairs genau der richtige Ort für diese Eigenschaften| #### Response Der Response-Body enthält eine Liste der auf den eingereichten basket anwendbaren Preisreduzierungen (Rewards) sowie Treuepunkte. Die Rewards-Liste wird leer sein, wenn kein Reward anwendbar ist. Anderenfalls ist es eine Liste mit Objekten, die jeweils einen Reward pro einzelnen Artikel darstellen oder sich auf den Gesamt-Rechnungsbetrag beziehen. Wurde ein Reward durch eine Artikelkombination getriggert, so wird dieser Reward aus Steuerrechtlichen Gründen auf alle ihn auslösenden Artikel proportional aufgespalten. Dabei besitzt jeder Reward-Eintrag eine Referenz auf die Position des baskets, welcher er zuzuordnen ist. Es ist nun die Aufgabe der Kasse, diese Preisreduzierungen buchhalterisch korrekt in die Rechnung einzuarbeiten. Die errechnete Reward-Höhe basiert dabei stets auf dem im basket angegebenen Bruttopreis der jeweiligen Position. Jedes Objekt hat dabei folgende Properties: | Name | Beschreibung | | --- | --- | |`RewardType`|**int**: 1 = Reward auf Produkt. 2 = Reward auf Gesamt-Rechnungsbetrag. Bei 1 wird die `ProductId` mit der PLU des Produktes belegt, für welchen dieser Reward ermittelt wurde. Bei 2 bleibt das Feld `ProductId` leer.| |`ProductId`|**string**: die PLU des im basket-Datensatz übermittelten Artikels, auf welchen ein Reward ermittelt werden konnte.| |`GrossRewardValue`|**decimal**: das ist der errechnete Wert des Rewards, der auf der Grundlage des Bruttopreises der jeweiligen Position ermittelt wurde. | |`RewardTriggeringPositionId`|**int**: PositionId der den aktuellen Reward auslösenden Position des übermittelten Baskets.| |`CouponId`|**int**: Id des Coupons im qnips-System für Dokumentationszwecke.| |`Name`|**string**: Name des Coupons im qnips-System für Dokumentationszwecke.| Und hier die Erklärung der Properties eines LoyaltyInfo-Objekts: | Name | Beschreibung | | --- | --- | |`LoyaltyId`|**int**: Id des Treuepunkte-Schemas im qnips-System für Dokumentationszwecke.| |`Name`|**string**: Name des Treuepunkte-Schemas im qnips-System für Dokumentationszwecke.| |`PointsInScheme`|**int**: Anzahl der Punkte, die gesammelt werden müssen, damit ein Reward ausgegeben wird.| |`PreviousPoints`|**int**: Anzahl der Punkte vor dem aktuellen Kauf| |`NewPoints`|**int**: Anzahl der neuen, mit diesem Kauf erzeugten Punkte| |`GrossRewardValue`|**decimal**: das ist der errechnete Wert des Rewards, der auf der Grundlage des Bruttopreises der jeweiligen Position ermittelt wurde. Er wird nur gesetzt, wenn PreviousPoints+NewPoints >= PointsInScheme ist.| |`RewardTriggeringPositionId`|**int**: PositionId der den aktuellen Reward auslösenden Position des übermittelten Baskets.| + Parameters + basketId (string, `'ab12345'`) ... Ein frei von Ihrem Client erzeugbarer String, welcher den basket eindeutig identifiziert. Sie können hier z.B. die Rechnungsnummer als `basketId` verwenden. + token1 (optional, string, `'member123'`) ... Hat sich der Konsument bereits am POS z.B. durch Vorzeigen einer Kundenkarte identifiziert, in welcher der mit seinem Konsumenten-Profil verlinkte `token` gespeichert ist, sollte dieser `token` hier angegeben werden. + token2 (optional, string, `'giftCardABC'`) ... Hatte der Konsument beim Kauf eine Geschenkkarte vorgezeigt und wurde diese von Ihrem System erfasst, sollte hier die in der Karte gespeicherte ID angegeben werden + Request + Headers Content-Type: application/json DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Body { "BillNumber":"1234", "Timestamp":"2015-04-21T08:15:45.000+02:00", "TotalValue":159.90, "HasDiscounts":false, "TableId":"123", "WaiterName":"Max Mustermann", "CurrencyISO":"EUR", "Positions":[ { "PositionId":1, "ProductId":"20019", "ProductName":"Kaffee Large", "Amount":1, "Unit":0, "HasDiscounts":false, "CurrentGrossPrice":2.39, "RegularGrossPrice":2.39, "VatInPercent":19.0, "OrderTime":"2015-04-21T08:15:45.000+02:00", "ImageUrl":"http://abc.de/xyz", "ProductProps":{ "Bohne":"100% Arabica", "Milch":"soja" } }, { "PositionId":2, "ProductId":"151", "ProductName":"Vanille Donut", "Amount":1, "Unit":0, "HasDiscounts":false, "CurrentGrossPrice":1.79, "RegularGrossPrice":1.79, "VatInPercent":19.0, "OrderTime":"2015-04-21T08:15:45.000+02:00" } ], "BillPdf":"98761238467129803470987809123489798172435980728347578091873645786..." } + Response 200 + Headers Content-Type: application/json + Body { "Rewards":[ { "ProductId": "20019", "Name": "30% auf Großes Kaffeegetränk und einen Donut", "GrossRewardValue": 0.72, "CouponId": 683, "RewardTriggeringPositionId": 1, "RewardType": 1 }, { "ProductId": "151", "Name": "30% auf Großes Kaffeegetränk und einen Donut", "GrossRewardValue": 0.53, "CouponId": 683, "RewardTriggeringPositionId": 2, "RewardType": 1 } ], "LoyaltyInfo": [ { "Name": "Ein Punkt für jedes Heißgetränk", "LoyaltyId": 171, "Details": [ { "PreviousPoints": 7, "NewPoints": 2, "PointsInScheme": 10, "GrossRewardValue": 0, "RewardTriggeringPositionId": 1 } ] } ] } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden // 11004: consumerIdentToken nicht gefunden. Wiederholen Sie ggf. ohne diesen Token // 11005: consumerIdentToken gesperrt. Wiederholen Sie ggf. ohne diesen Token // 11006: consumerIdentToken abgelaufen. Wiederholen Sie ggf. ohne diesen Token // 11007: giftCardId nicht gefunden. Wiederholen Sie ggf. ohne diesen Token // 11008: giftCardId gesperrt. Wiederholen Sie ggf. ohne diesen Token // 11009: giftCardId abgelaufen. Wiederholen Sie ggf. ohne diesen Token // 11010: dieser basket ist `granted` und somit nicht mehr veränderbar ## Rewards als `granted` markieren [/baskets/{basketId}/grant?redeemedInPos={redeemedInPos}] ### POST baskets/{basketId}/grant [POST] Erst durch das Markieren eines baskets als `granted` betrachtet das qnips-System die ermittelten Rewards als legitim und schreibt diese dem Konsumenten gut. Es werden dabei genau die Rewards als `granted` markiert, die bei dem letzten *[POST baskets/{basketId}](#reference/baskets/basket-hochladen/post-baskets%2F%7Bbasketid%7D%3Fconsumeridenttoken%3D%7Btoken1%7D%26giftcardid%3D%7Btoken2%7D)*-Call für den basket mit der angegebenen basketId ermittelt und zurückgeliefert wurden. Geben Sie für den `redeemedInPos`-Parameter den Wert `true` wenn Ihr System bei dem aktuellen Händler die qnips-Rabatte direkt verrechnet. + Parameters + basketId (string, `1234567890`) ... Geben Sie hier die Id des baskets, den Sie als abgeschlossen markieren wollen + redeemedInPos (bool, `true` ) ... Verwenden Sie **true** wenn Ihr System bei dem aktuellen Händler die qnips-Rabatte direkt verrechnet. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 400 { "ErrorId":12000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 11001: basketId unbekannt #Group contextIdentifiers Es ist sinnvoll an der Kasse nach einem Scan oder einer Tastatureingabe direkt erkennen zu können, ob es sich um einen qnips-relevanten Identifier handelt, um diesen ggf. dem Kontext eines baskets beizufügen. Dies kann über Reguläre Ausdrucke erreicht werden, welche die Kasse über einen speziellen API-Call abholen kann. Diese Regulären Ausdrucke sind immer Standortgebunden und können tagtäglich erweitert oder angepasst werden, sodass eine nächtliche Aktualisierung dieser Information je Standort an der Kasse angebracht ist. Es existieren folgende Typunterscheidungen für qnips-relevante Identifier: | Typ | Beschreibung | | --- | --- | |Qnips-Profil-Identifier|Dieser ist in der Regel als `consumerIdentToken` beim POST basket zu verwenden| |Rabatt-Codes|Das sind einfache Coupon-Identifier, die dem basket direkt und ohne einen `consumerIdentToken` beigefügt werden können| Werden an der Kasse solche Rabatt-Codes erfasst, ohne dass ein Qnips-Profil-Identifier folgt, handelt es sich um sogenannte generischen Coupons, die nicht personengebunden sind und daher keinen Profil-Identifier zur Berechnung erfolgen. Solche Coupon-Identifier sind dem basket direkt im Request-Body in der Property `ContextIdentifiers` beizufügen. ## Context-Identifiers holen [/merchants/{qnipsUsername}/contextIdentifiers?outletId={outletId}] ### GET context-identifiers [GET] Diese Resource liefert im Wesentlichen eine Liste der Regulären Ausdrucke, mit denen unterschieden werden kann, ob eine Eingabe oder ein Scan eines Barcodes (bzw. 2D-Codes) einen qnips-relevanten Identifier enthält und um was für eine Art des Identifiers es sich ggf. handelt. Folgende Arten können unterschieden werden: + Request + Headers Accept: application/json DevKey: {Ihr Key} Authorization: {qnipsUserName}:{qnipsPwd} Lassen Sie den Merchant den Username und Passwort seines Qnips-Accounts eintragen und fügen Sie diese hier mit Doppelpunkt getrennt ein, z.B. `hans.müller@firma.de:geheim` + Response 200 [ { "type": 1, "regex": "oT[a-zA-Z0-9]{10}P", "name": "Profiltoken App" }, { "type": 1, "regex": "C[a-zA-Z0-9]{16}", "name": "Profiltoken Kundenkarte" }, { "type": 2, "regex": "VA04[0-9]{9}", "name": "Vattenfall-Coupon Lokalzeitung" }, { "type": 2, "regex": "VA05[0-9]{9}", "name": "Vattenfall-Coupon PDF-Download" } ] // type 1: ConsumerIdentToken // type 2: CouponIdent + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 401 // HTTP-Unauthorized: DevKey nicht angegeben oder nicht vorhanden + Response 404 // HTTP-NotFound: qnipsUserName oder outletId unbekannt #Group sortiments ## sortimentId besorgen [/sortiments?thirdPartySortimentIdentifier={string1}&name={string2}] ### GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2} [GET] Bevor ein Artikel im qnips-System hochgeladen bzw. upgedated werden kann, muss die trackingUnit sich an einem Sortiment anmelden. Dies geschieht mit diesem Call. Im Response erhalten Sie eine sogenannte `qnipsSortimentId`, die künftig bei jeder Operation auf dem Sortiment anzugeben ist. Existiert noch kein Sortiment mit den angegebenen Parametern, wird eins angelegt und dessen ID zurückgeliefert. Speichern Sie die Rückgabe.

    {
        "qnipsSortimentId":"sortiment123"
    }

Wird die Kasse in Ihrem System mit einem anderen Sortiment verknüpft, rufen Sie diese Ressource erneut auf und speichern Sie die Rückgabe erneut ab. + Parameters + string1 (string, `1234567890`) ... hier ist der unique Identifier des Sortiments anzugeben, unter welchem er in Ihrem eigenen System geführt wird + string2 (optional, string, `'Sortiment Lounge-Bar' -> 'Sortiment%20Lounge-Bar'` ) ... ein für den merchant verständlicher Name, unter welchem er das Sortiment im qnips-Dashboard sehen und ggf. bearbeiten können wird. Der Wert sollte URL-konform escaped werden. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Response 200 { "qnipsSortimentId":"sortiment123" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } ## Warengruppen bearbeiten [/sortiments/{qnipsSortimentId}/group/{Id}] ### POST sortiments/{qnipsSortimentId}/group} [POST] + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Body { "Id":”123”, "UpGroupId":"234", "Name":[ { "lang":"de-DE", "val":"Getränke" }, { "lang":"de-CH", "val":"Getränke" }, { "lang":"it-CH", "val":"Bevande" }, { "lang":"fr-UK", "val":"Boissons" } ] } + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 12001: qnipsSortimentId unbekannt // 12002: Id-Feld nicht gesetzt // 12003: Keine Warengruppe unter der angegebenen UpGroupId gefunden // 12004: Es gibt bereits eine Warengruppe unter der angegebenen Id // 12005: Name wurde nicht angegeben, wird aber in mind. einer Sprache erwartet ### PUT sortiments/{qnipsSortimentId}/group} [PUT] + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Body { "Id":”123”, "UpGroupId":"234", "Name":[ { "lang":"de-DE", "val":"Getränke" }, { "lang":"de-CH", "val":"Getränke" }, { "lang":"it-CH", "val":"Bevande" }, { "lang":"fr-UK", "val":"Boissons" } ] } + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden // 12001: qnipsSortimentId unbekannt // 12002: Id-Feld nicht gesetzt // 12003: Keine Warengruppe unter der angegebenen UpGroupId gefunden // 12005: Name wurde nicht angegeben, wird aber in mind. einer Sprache erwartet // 12006: Kein Item unter angegebener Id gefunden // 12007: UpGroupId darf nicht geändert werden. Löschen Sie den item statt dessen und legen Sie ihn in der anderen Warengruppe neu an ### DELETE sortiments/{qnipsSortimentId}/group/{Id}} [DELETE] Warengruppen sind wie folgt aufgebaut:

    {
       "Id":”123”,
       "UpGroupId":"234",
       "Name":[
            { "lang":"de-DE", "val":"Getränke" }, 
            { "lang":"de-CH", "val":"Getränke" }, 
            { "lang":"it-CH", "val":"Bevande" },
            { "lang":"fr-UK", "val":"Boissons" }
        ]
    }

| Property | Beschreibung | | --- | --- | |`Id`|**string**: Das ist der eindeutige Identifier für diese Warengruppe. Er kann als `UpGroupId` von einer anderen Warengruppe referenziert werden, wodurch eine beliebig tief geschachtelte Warengruppen-Hierarchie abgebildet werden kann. | |`UpGroupId`|**string**: Ist diese Warengruppe eine Gruppe auf oberster Ebene, ist diese Angabe nicht erforderlich. Sonst geben Sie hier den Identifier der Warengruppe, welcher diese Einheit untergeordnet werden soll.| |`Name`|**translatable**: Hier kann der Name der Warengruppe in beliebig vielen Übersetzungen im Format, das weiter unten beschrieben wird, angegeben werden. Bitte geben Sie den Namen stets mindestens in einer Sprache an. | #### Übersetzbare Properties (`translatable`) Manche String-Properties von einigen Entitäten, wie z.B. der Name eines Artikels im Sortiment kann unser System in mehreren Übersetzungen entgegennehmen. In dieser Dokumentation sind solche Properties als `translatable` markiert. Die Übergabe der Werte für solche Properties ist immer als eine Liste von Translatable-Objekten zu erfolgen. Im `lang`-Property des Translatable-Objekts ist der Language-Tag nach [RFC 5646]() und die Übersetzung des Wertes in der entsprechenden Sprache im `val`-Feld anzugeben. JSON-Beispiel:

    "Name":[
        { "lang":"de-DE", "val":"Getränke" }, 
        { "lang":"de-CH", "val":"Getränke" }, 
        { "lang":"it-CH", "val":"Bevande" },
        { "lang":"fr-UK", "val":"Boissons" }
    ]

+ Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + Id (string, `'A12'` ) ... geben Sie hier die PLU des zu löschenden Artikels oder die Id der zu löschenden Warengruppe an. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":12000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 12001: qnipsSortimentId unbekannt // 12006: Kein Item unter angegebener Id gefunden ## Artikel bearbeiten [/sortiments/{qnipsSortimentId}/article/{PLU}?groupId={groupId}] ### POST sortiments/{qnipsSortimentId}/article} [POST] + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Body { "PLU":"123", "Name":[{ "lang":"de-DE", "val":"Bier 0.5L" }], "GroupId":"234", "Rateable":true, "ExternalName":[{ "lang":"de-DE", "val":"Bier vom Fass 0.5L" }], "Description":[{ "lang":"de-DE", "val":"Frisch gezapft" }], "Ingredients":[{ "lang":"de-DE", "val":"Malz, Hopfen, Wasser" }], "SoldOut":false, "PictureUrl":"http://...", "Prices":[ { "Value":9.95, "CurrencyIso":"EUR", "Tag":{ "Name":[ { "lang":"de-DE", "val":"Mitarbeiter" } ] } }, { "Value":8.95, "CurrencyIso":"EUR", "Tag":{ "Name":[ { "lang":"de-DE", "val":"Studenten" } ] } } ], "Allergens":[1, 5, 11], "Traces":[2, 4], "Additives":[1, 8], "Tags":[ "Vegan", "Frisch gezapft" ], "WeightInGramms":125.0, "NutritionFacts":{ "Fats":20, "Carbs":20, "Protein":20, "KJoule":200, "KCal":200 } } + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden // 12001: qnipsSortimentId unbekannt // 12003: Keine Warengruppe unter der angegebenen UpGroupId gefunden // 12005: Name wurde nicht angegeben, wird aber in mind. einer Sprache erwartet // 12008: PLU-Feld nicht gesetzt // 12009: In der angegebenen Warengruppe existiert bereits ein Artikel mit der angegebenen PLU ### PUT sortiments/{qnipsSortimentId}/article} [PUT] + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Body { "PLU":"123", "Name":[{ "lang":"de-DE", "val":"Bier 0.5L" }], "GroupId":"234", "Rateable":true, "ExternalName":[{ "lang":"de-DE", "val":"Bier vom Fass 0.5L" }], "Description":[{ "lang":"de-DE", "val":"Frisch gezapft" }], "Ingredients":[{ "lang":"de-DE", "val":"Malz, Hopfen, Wasser" }], "SoldOut":false, "PictureUrl":"http://...", "Prices":[ { "Value":9.95, "CurrencyIso":"EUR", "Tag":{ "Name":[ { "lang":"de-DE", "val":"Mitarbeiter" } ] } }, { "Value":8.95, "CurrencyIso":"EUR", "Tag":{ "Name":[ { "lang":"de-DE", "val":"Studenten" } ] } } ], "Allergens":[1, 5, 11], "Traces":[2, 4], "Additives":[1, 8], "Tags":[ "Vegan", "Frisch gezapft" ], "WeightInGramms":125.0, "NutritionFacts":{ "Fats":20, "Carbs":20, "Protein":20, "KJoule":200, "KCal":200 } } + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden // 12001: qnipsSortimentId unbekannt // 12005: Name wurde nicht angegeben, wird aber in mind. einer Sprache erwartet // 12006: Kein Item unter angegebener Id/PLU gefunden. Versuchen Sie einen POST // 12007: (Up)GroupId darf nicht geändert werden. Löschen Sie den item statt dessen und legen Sie ihn in der anderen Warengruppe neu an // 12008: PLU-Feld nicht gesetzt ### DELETE sortiments/{qnipsSortimentId}/article/{PLU}?groupId={groupId} [DELETE] + Artikel können in Warengruppen organisiert sein. Dazu muss das Feld GroupId des Artikels mit der Id der Gruppe gesetzt werden und die Warengruppe zuvor im qnips-System angelegt worden sein. + Die zwei mindestens notwendige Angaben zu einem Artikel sind PLU und Name + alle anderen Felder sind optional. Hier also eine Minimal-Variante:

    {
       "PLU":”123”,
       "Name":[{ "lang":"de-DE", "val":"Bier 0.5L" }]
    }

Und hier die Maximalvariante. Jeder Properties-Subset dazwischen ist natürlich auch möglich:

    {  
       "PLU":"123",
       "Name":[{ "lang":"de-DE", "val":"Bier 0.5L" }],
       "GroupId":"234",
       "Rateable":true,
       "ExternalName":[{ "lang":"de-DE", "val":"Bier vom Fass 0.5L" }],
       "Description":[{ "lang":"de-DE", "val":"Frisch gezapft" }],
       "Ingredients":[{ "lang":"de-DE", "val":"Malz, Hopfen, Wasser" }],
       "SoldOut":false,
       "PictureUrl":"http://...",
       "Prices":[  
          {  
             "Value":9.95,
             "CurrencyIso":"EUR",
             "Tag":{  
                "Name":[  
                   {  
                      "lang":"de-DE",
                      "val":"Mitarbeiter"
                   }
                ]
             }
          },
          {  
             "Value":8.95,
             "CurrencyIso":"EUR",
             "Tag":{  
                "Name":[  
                   {  
                      "lang":"de-DE",
                      "val":"Studenten"
                   }
                ]
             }
          }
       ],
       "Allergens":[1, 5, 11],
       "Traces":[2, 4],
       "Additives":[1, 8],
       "Tags":[  
          "Vegan",
          "Frisch gezapft"
       ],
       "WeightInGramms":125.0,
       "NutritionFacts":{  
          "Fats":20,
          "Carbs":20,
          "Protein":20,
          "KJoule":200,
          "KCal":200
       }
    }

| Property | Beschreibung | | --- | --- | |`PLU`|**string**: Was die `Id` für die Warengruppe ist, ist die PLU für den Artikel. | |`Name`|**translatable**: Der Name sowie einige andere Properties eines Artikels können in beliebig vielen Übersetzungen im Format, das weiter unten beschrieben wird, angegeben werden. Bitte geben Sie mindestens eine Sprache an. | |`GroupId`|**string**: Das ist die Id der Warengruppe, unter welcher der Artikel eingeordnet werden soll. Die Warengruppe muss bereits angelegt worden sein, sonst wird der Call abgewiesen. Wird diese Angabe unterlassen, wird der Artikel auf oberster Ebene, ungruppiert, im Sortiment eingeordnet.| |`Rateable`|**bool**: Kann der merchant die Bewertbarkeit eines Artikels in Ihrer Software angeben, so können Sie diese Einstellung hier an qnips übermitteln, um bestimmte Artikel aus den Bewertungsbögen fernzuhalten. Bietet Ihr System keine Möglichkeit an, die Bewertbarkeit der Artikel zu erfassen, so belegen Sie dieses Feld mit `null` bzw. lassen Sie es komplett aus.| |`Description`|**translatable**: Existiert zum Artikel eine Beschreibung, kann sie mehrsprachig hier übermittelt werden. Diese kann in bestimmten Fällen den Konsumenten in der App angezeigt werden| |`Ingredients`|**translatable**: Existiert diese Angabe, kann sie mehrsprachig hier übermittelt werden und ist ggf. für die Konsumenten in der App sichtbar.| |`SoldOut`|**string**: Kann ein Artikel in Ihrer Software als ausverkauft markiert werden, sollten Sie diese Zustandsänderung über dieses Feld an qnips übermitteln. Dadurch können wir den Artikel z.B. in öffentlichen elektronisch angezeigten Speiseplänen dynamisch und zeitnah in der Speisekarte ausblenden.| |`PictureUrl`|**string**: Existiert ein über eine URL öffentlich zugängliches Bild zum Artikel, kann dessen URL hier angegeben werden.| |`Prices`|**List of Price**: Hier können beliebig viele Preisebenen angegeben werden, wobei eine Preisebene immer aus einem `Tag` als öffentlich sichtbarem Bezeichner und dem eigentlichen Preis als decimal-Wert besteht.| |`Allergens`|**List of ints**: Hier kann eine Liste der Allergen-IDs angegeben werden, die im Artikel enthalten sind. Siehe hierzu die Allergenauflistung unten. | |`Traces`|**List of ints**: Hier kann eine Liste der Allergen-Spuren angegeben werden, die im Artikel enthalten sind. Lister Sie hier die AllergenIDs auf, deren Spuren im Artikel enthalten sein können. Siehe hierzu die Allergenauflistung unten. | |`Additives`|**List of ints**: Hier kann eine Liste der nach LMIV-Verordnung mindestens ausweispflichtiger Zusatzstoffe angegeben werden, die im Artikel enthalten sind. Siehe hierzu die Zusatzstoff-Auflistung unten. | |`Tags`|**List of strings**: Sind für einen merchant eigene individuelle Artikel-Auszeichnungen implementiert worden, die sich nicht über bisherige Properties übermitteln ließen, und dennoch von Bedeutung für eine der qnips-Funktionen (z.B. individuelle Artikelauszeichnungen in öffentlichen Speiseplänen), können sie über diese Listen-Property angegeben werden. | |`WeightInGramms`|**decimal**: Existiert die Angabe in Ihrer Software, geben sie hier an uns weiter. | |`Fats`|**decimal**: Angabe des Fettanteils in Gramm je 100 Gramm Artikelgewicht. | |`Carbs`|**decimal**: Angabe des Kohlenhydrate-Anteils in Gramm je 100 Gramm Artikelgewicht. | |`Protein`|**decimal**: Angabe des Proteinanteils in Gramm je 100 Gramm Artikelgewicht. | |`KJoule`|**decimal**: KJoule je 100 Gramm Artikelgewicht. | |`KCal`|**decimal**: KCal je 100 Gramm Artikelgewicht. | #### Allergen-Liste Id|Allergen ---|--- 0|Glutenhaltiges Getreide 1|Krebstiere und Krebstiererzeugnisse 2| Eier und Eiererzeugnisse 3| Fisch und Fischerzeugnisse 4| Erdnüsse und Erdnusserzeugnisse 5| Soja und Sojaerzeugnisse 6| Milch und Milcherzeugnisse 7| Schalenfrüchte und Schalenfruchterzeugnisse 8| Sellerie und Sellerieerzeugnisse 9| Senf und Senferzeugnisse 10| Sesam und Sesamerzeugnisse 11| Schwefeldioxid und Sulfite 12| Lupinen und Lupinenerzeugnisse 13| Weichtiere und Weichtiererzeugnisse #### Zusatzstoff-Liste Id|Zusatzstoff-Bezeichnung ---|--- 0| mit Farbstoff 1| mit Konservierungsstoff 2| mit Antioxidationsmittel 3| mit Geschmacksverstärker 4| geschwefelt 5| geschwärzt 6| gewachst 7| mit Phosphat 8| mit Süßungsmittel(n) 9| enthält eine Phenylalaninquelle 10| mit Nitrat 11| mit Nitritpökelsalz 12| koffeinhaltig + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + PLU (string, `'A12'` ) ... geben Sie hier die PLU des zu löschenden Artikels an. + groupId (string, `'123'` ) ... geben Sie hier die PLU der Warengruppe in welcher der zu löschende Artikel steckt oder eine '0' wenn der Artikel keiner Warengruppe zugeordnet ist + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 12001: qnipsSortimentId unbekannt // 12006: Kein Item unter angegebener Id/PLU gefunden. Versuchen Sie einen POST ## BulkUpload [/sortiments/{qnipsSortimentId}/bulkUpload?changedArticlesOnly={changedArticlesOnly}] ### POST sortiments/{qnipsSortimentId}/bulkUpload?changedArticlesOnly={changedArticlesOnly}} [POST] + Im BulkUpload können Sie mehrere Artikel und Warengruppen in einem Rutsch erstellen und updaten + Über das boolean-Parameter `changedArticlesOnly` können Sie angeben, ob der Body einen kompletten Stamm beinhaltet oder lediglich die seit dem letzten Upload in Ihrem System aktualisierten Artikel. + Bei `changedArticlesOnly=true` wird unser System eigenständig die im Vergleich zum vorhergehenden Upload neu hinzugefügten, geänderten oder gelöschten Artikel und Warengruppen ermitteln und lediglich diese Änderungen historisiert abspeichern. + Bei `changedArticlesOnly=false` interpretiert unser System die Angaben im Body als neue, bzw. zu aktualisierende Artikel und Warengruppen. Löschen muss in diesem Fall über Einzellöschungen realisiert werden, wie oben schon beschrieben wurde. Der Body ist wie folgt für diesen Request anzugeben:

        {
            "Groups":[
                {
                   "Id":"1",
                   "UpGroupId":"",
                   "Name":[{ "lang":"de-DE", "val":"Getränke" }]
                },
                {
                   "Id":"1_2",
                   "UpGroupId":"1",
                   "Name":[{ "lang":"de-DE", "val":"Biere" }]
                }
            ],
            "Products":[
                {
                   "PLU":"123",
                   "GroupId":"1_2"
                   "Name":[{ "lang":"de-DE", "val":"Bier 0.5L" }]
                }
            ]
        }
    
| Property | Beschreibung | | --- | --- | |`Groups`| Liste der Warengruppen, wie unter [Warengruppen bearbeiten](#reference/sortiments/warengruppen-bearbeiten) spezifiziert. | |`Products`| Liste der Artikel, wie unter [Artikel bearbeiten](#reference/sortiments/artikel-bearbeiten) spezifiziert. | + Parameters + qnipsSortimentId (string, `'sortiment123'`) ... das ist die `qnipsSortimentId`, die beim Call auf `GET sortiments?thirdPartySortimentIdentifier={string1}&name={string2}` zurückgeliefert wurde. + changedArticlesOnly (boolean, `true'` ) ... gibt an, ob es sich beim Upload um das Gesamtsortiment, welches mit qnips geteilt werden soll, handelt oder dieser Request lediglich die Änderungen zum vorherigen erfolgreichen Upload beinhaltet. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} Content-Encoding: gzip (optional) // fügen Sie den Body dieses Requests in gezippter Form an, wenn dieser Header gesetzt ist. + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey oder trackingUnitId nicht angegeben oder nicht vorhanden + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 1000: Body enthält syntaktische Fehler und kann nicht deserialisiert werden // 12001: qnipsSortimentId unbekannt # Group tokens Tokens sind beliebig geartete, jedoch stets voneinander unterschiedliche, nicht wiederholbare Strings, welche sowohl zur Identifikation eines Konsumenten ( `consumer` ) als auch zur Identifikation eines Guthabens ( `balance` ) verwendet werden können. In der Regel werden diese Tokens auf einem beliebigen Trägermedium gespeichert und an einen Konsumenten in Form einer `Kundenkarte` oder `Geschenkkarte` ausgehändigt. Der Konsument wird bei seinen nächsten Käufen dieses Trägermedium am POS vorzeigen müssen, um auf das darin gespeicherte Guthaben zugreifen zu können bzw. die qnips-Rewards angerechnet zu bekommen. ## Token registrieren [/tokens/{id}] ### POST tokens/{id} [POST] Dieser Call legt ein neues Konsumenten-Profil für den übergebenen Token an. *Bitte bilden Sie zur* **{id}** *eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über {id}} Token: optional {der von einem Trägermedium ausgelesene Token als Kunden-Identifier} + Body { "UserGroup": { "Key": "DrittanbieterId", "Name": "Benutzergruppe" } } + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":13000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13026: Feature ist nicht ünterstützt von der Handelsmarke. // 13025: Profil existiert schon. ## Token Info holen [/tokens/{id}/info] ### POST tokens/{id}/info [GET] Hiermit kann abgefraget werden, ob am Token z.B. schon Guthaben aufgeladen ist, welche Aufladelimits für den Token ggf. existieren, ob eine PIN-Abfrage erforderlich ist, wieviele Treuepunkte der User dieses Tokens ggf. schon hat und vieles mehr. Diese Info kann für die visuelle Anzeige für den Kassierer relevant sein. *Bitte bilden Sie zur* **{id}** *eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. Ist der Token länger als 50 bytes, sollte dieser anstatt über die URL, über den Token-Requestheader übermittelt werden. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über {id}} Token: optional {der von einem Trägermedium ausgelesene Token als Kunden-Identifier} + Response 200 { "TokenType": "consumerIdentifier", "Balance": 25.99, "MaxBalance": 99999.0, "RemainingBalanceToAdd": 99974.01, "IsWithdrawBalanceAllowed": false, "IsAddBalanceAllowed": true, "RelatedGiftCards": [], "IdentAliases": [ { "IdentString": "6088514929546754", "Source": 4 } ], "IdentString": "6088514929546754", "ExternalIdentString": "12398730984", "ExternalIdentStringSource": "Company", "ExternalCardNumber": "12356473", "RequiresPin": false, "InfoUrl": "http://pos.dev.qnips.com/TokenInfo?token=4BZC2R1iAqmappIzAMbOkQ0LZCm0b%2fmc0v7nhSITEV7snTxI%2f%2bELsNd6n1Z2fyXtMp%2fuwlg%2fgU%2be9r7bIQwSiA%3d%3d", "ConsumerIdentityInfo": { "LoyaltySchemeProgressInfos": [ { CurrentPoints = 2, MaxPoints = 10, LoyaltySchemeId = 123, LoyaltySchemeName = "Jedes 10. Kaffeegetränk gratis" } ], "QrCodeContent": "", "Allowances": [ { "Id": 180, "Name": "Allowances Daily", "ThirdPartyId": "AD123", "RemainingValue": 2.0, "Type": 1 } ] }, "TagInfos": [ { "ThirdPartyId": "DrittanbieterId", "Name": "Benutzergruppe" } ] } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":13000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt ## Token sperren [/tokens/{id}/lock] ### POST tokens/{id}/lock [POST] Hiermit sperren Sie einen Token, sodass mit diesem Token kein weiterer `basket` eingereicht und das Guthaben weder aufgewertet noch abgehoben werden kann. Mögliches verbliebenes Guthaben hinter diesem Token verfällt ersatzlos. *Bitte bilden Sie zur* **{id}** *eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an, der zu sperren ist. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über {id}} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":13000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt ## Token freigeben [/tokens/{id}/dispose] ### POST tokens/{id}/dispose [POST] Gibt den Token zur Wiederverwendung durch einen neuen Konsumenten frei. Mögliches Guthaben hinter diesem Token verfällt ersatzlos. *Bitte bilden Sie zur* **{id}** *eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an, den Sie zur Wiederverwendung freigeben wollen. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über {id}} + Response 200 + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":13000, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt ## Guthaben abfragen [/tokens/{id}/balance] ### GET tokens/{id}/balance [GET] Hiermit können Sie das auf dem Token ggf. vorhandene Guthaben abfragen. *Bitte bilden Sie zur* **{id}** *eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an, dessen Guthaben Sie abfragen wollen. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über {id}} + Response 200 { Balance = 50.0, CurrencyIso = "EUR" } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt // 13004: token ist temporär gesperrt, keine weitere Aktion möglich // 13008: token ist endgültig gesperrt, keine weitere Aktion möglich ## Guthaben aufladen [/tokens/{id}/addBalance?balanceToAdd={balanceToAdd}] ### POST tokens/{id}/addBalance?balanceToAdd={balanceToAdd} [POST] Hiermit können Sie das auf dem Token ggf. vorhandene Guthaben um einen beliebigen Betrag erhöhen. *Bitte bilden Sie zum* **'{id}_{balanceToAdd}'** *(z.B. 'card123_1.99') eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + balanceToAdd (decimal, `1.99`) .. Geben Sie hier den Betrag an, der auf das Guthaben dieses Tokens aufgeladen werden soll. Negative Beträge sind nicht erlaubt + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über '{id}_{balanceToWithdraw}'} (in diesem Beispiel über 'card123_1.99') + Body { "ReceiptPdfUrl":"https://..." } + Response 200 { "oldBalance":1.5 "newBalance":3.49 } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt // 13004: token ist gesperrt, keine weitere Aktion möglich // 13005: Es sind nur positive Werte zum Aufladen bzw. Abheben erlaubt // 13006: Limit zum Abheben/Aufladen überschritten ## Guthaben abheben [/tokens/{id}/withdrawBalance?balanceToWithdraw={balanceToWithdraw}&pin={pin}] ### POST tokens/{id}/withdrawBalance?balanceToWithdraw={balanceToWithdraw}&pin={pin} [POST] Hiermit können Sie einen Betrag vom Guthaben des Tokens abheben. Ist der abzuhebende Betrag größer als das Guthaben, wird der Call abgewiesen. *Bitte bilden Sie zum* **'{id}_{balanceToWithdraw}'** *(z.B. 'card123_1.99') eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + balanceToWithdraw (decimal, `1.99`) .. Geben Sie hier den abzuhebenden Betrag an. Negative Beträge sind nicht erlaubt + pin (optional, string, `'PQ123'`) ... Die optionale PIN, im Fall dass die Ressource GET tokens/{id}/info das Feld `RequiresPin` auf `true` gesetzt hat. Die PIN ist üblicherweise auf der Karte gedruckt. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über '{id}_{balanceToWithdraw}'} (in diesem Beispiel über 'card123_1.99') + Body { "ReceiptPdfUrl":"https://..." } + Response 200 { "oldBalance":3.49 "newBalance":1.5 } + Response 500 // HTTP-InternalServerError: Ein unbekannten Problem bei der Verarbeitung aufgetreten { "ErrorMessage":"some explanation" "StackTrace":"text" } + Response 403 // HTTP-Forbidden: trackingUnitId ist noch nicht vom Merchant aktiviert + Response 401 // HTTP-Unauthorized: DevKey, trackingUnitId oder SecurityToken nicht angegeben/falsch + Response 400 { "ErrorId":123, "ErrorText":"Details zum Fehler" } // hier alle möglichen ErrorCodes inkl. Texte für diese Ressource // 13001: token unbekannt // 13004: token ist gesperrt, keine weitere Aktion möglich // 13005: Es sind nur positive Werte zum Aufladen bzw. Abheben erlaubt // 13006: Limit zum Abheben/Aufladen überschritten // 13010: Guthabenzahlung im Store noch nicht eingerichtet // 13012: Guthaben nicht ausreichend // 13015: PIN ist falsch [nur wenn PIN als Query-Parameter mit angegeben] ## Draw allowances [/tokens/{id}/Allowances?pin={pin}] ### POST tokens/{id}/Allowances?pin={pin} [POST] Hiermit können sie einen Betrag der Zuschüsse eines Tokens abheben. *Bitte bilden Sie zum* **'{id}_{balanceToAdd}'** *(z.B. 'card123_1.99') eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + pin (optional, string, `'PQ123'`) ... Die optionale PIN, im Fall dass die Ressource GET tokens/{id}/info das Feld `RequiresPin` auf `true` gesetzt hat. Die PIN ist üblicherweise auf der Karte gedruckt. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über '{id}_{balanceToWithdraw}'} (in diesem Beispiel über 'card123_1.99') Token: optional {der von einem Trägermedium ausgelesene Token als Kunden-Identifier} + Body { "UserGroup": { "Key": "123", "Name": "Interner Kunde" }, "Allowances": [ { "Id": 1, "ThirdPartyId": "9999", "Amount": 5.99 } ] } + response 200 [ { "Id": 1, "ThirdPartyId": "9999", "Amount": 5.99, "Success": true } ] ## Post basket [/tokens/{id}/ExternalBasket?pin={pin}] ### POST tokens/{id}/ExternalBasket?pin={pin} [POST] Hiermit können sie einen Basket einreichen ohne dabei die internen Prozesse auszulösen. *Bitte bilden Sie zum* **'{id}_{balanceToAdd}'** *(z.B. 'card123_1.99') eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + pin (optional, string, `'PQ123'`) ... Die optionale PIN, im Fall dass die Ressource GET tokens/{id}/info das Feld `RequiresPin` auf `true` gesetzt hat. Die PIN ist üblicherweise auf der Karte gedruckt. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über '{id}_{balanceToWithdraw}'} (in diesem Beispiel über 'card123_1.99') Token: optional {der von einem Trägermedium ausgelesene Token als Kunden-Identifier} + Body { "BillNumber": "727e085b-91e9-4383-92b6-b53e04ea1615", "TimestampUtc": "2024-02-16T08:51:01.216Z", "Positions": [ { "PositionId": 0, "ProductId": "QNP-1699877891354", "Amount": 1, "Name": "Hamburger", "OrderTimeUtc": "2024-02-16T08:51:01.216Z", "ProductProps": {} } ] } + response 200 ## Post UserGroups [/tokens/{id}/UserGroups?pin={pin}] ### POST tokens/{id}/UserGroups?pin={pin} [POST] Hiermit können sie die Benutzergruppen eines Tokens verwalten. Wenn eine Benutzergruppe(Key) in den Listen `UserGroupsToAdd` und `UserGroupKeysToRemove` auftaucht, dann hat das Löschen Vorrang und es wird nicht hinzugefügt. *Bitte bilden Sie zum* **'{id}_{balanceToAdd}'** *(z.B. 'card123_1.99') eine Checksumme (siehe [Security-Token](#introduction/grundlegendes/secutiry-token)) und geben Sie diese im `SecurityToken`-Header an.* + Parameters + id (string, `'cardId123'`) ... Geben Sie hier den von einem Trägermedium ausgelesenen Token an. + pin (optional, string, `'PQ123'`) ... Die optionale PIN, im Fall dass die Ressource GET tokens/{id}/info das Feld `RequiresPin` auf `true` gesetzt hat. Die PIN ist üblicherweise auf der Karte gedruckt. + Request + Headers DevKey: {Ihr Key} TrackingUnitId: {trackingUnitId} SecurityToken: {checksumme über '{id}_{balanceToWithdraw}'} (in diesem Beispiel über 'card123_1.99') Token: optional {der von einem Trägermedium ausgelesene Token als Kunden-Identifier} + Body { "UserGroupsToAdd": [ { "Key": "DrittanbieterId", "Name": "Benutzergruppenname" }, { "Key": "DrittanbieterId2", "Name": "Benutzergruppenname2" } ], "UserGroupKeysToRemove": [ "DrittanbieterId2", "DrittanbieterId3" ] } + response 200 + response 400 { "ErrorId":123, "ErrorText": "Details of the error" } // here all possible error codes incl. texts for this resource // 13027: Mindestens eine `UserGroupsToAdd` oder `UserGroupKeysToRemove` muss vorhanden sein