Anleitungen

Shopify

Noch nicht lauffähigProjektbaum für die Shopify-CLI (shopify app deploy)

Der Programmteil steht, aber es fehlt ein Schritt, den wir nicht allein gehen können. Was genau fehlt, steht in der Anleitung — bitte lesen Sie das, bevor Sie Zeit investieren.

Passwort: edelverify — Shopify lässt bei Entwicklungs-Shops keine offene Vorführung zu.

Als Datei: EINBAU.md · PRUEFPLAN.md

Einbau

EdelVerify für Shopify — Einbau

Altersprüfung mit der staatlichen Schweizer E-ID. Diese Anleitung führt Sie durch die Einrichtung, auch wenn Sie mit Shopify noch nie etwas zu tun hatten. Sie brauchen dafür keine Programmierkenntnisse, aber Zugang zum Shopify-Admin Ihres Ladens.

Rechnen Sie mit 20 Minuten. Der einzige Schritt, den man vergessen kann und der dann alles Sichtbare abschaltet, ist Schritt 5.


0. Zuerst: Was Sie bekommen — und was nicht

Shopify betreibt die Kasse selbst. Fremder Code darf dort nur, was Shopify vorsieht. Daraus folgen genau zwei Ausbaustufen:

Ohne Shopify PlusMit Shopify Plus
Hinweis und Prüffenster im Ladenjaja
Kassenknöpfe im Warenkorb gesperrtja (umgehbar)ja (umgehbar)
Sperre vor dem Bestellabschlussneinja, von Shopify erzwungen
Wirkt bei Apple Pay / Shop Pay / Google Payneinja
Jede betroffene Bestellung wird geprüftjaja
Ohne Nachweis: Versand angehalten, Bestellung markiertjaja

Und eine dritte Einschränkung: eine Grenze für den ganzen Laden

Die Altersprüfung kennt zwei Schwellen, 16 und 18. In WooCommerce, Shopware und Magento lässt sich die Grenze je Warengruppe setzen — Bier ab 16, Spirituosen ab 18, im selben Laden.

In Shopify geht das nicht. Dort ist die Sammlung die Warengruppe, und dieser App-Block kennt genau eine. Es gilt deshalb eine Grenze für alles, was geprüft wird, und sie kommt aus der Vorgabe Ihres EdelVerify-Kontos (unter *Shops*, ab Werk 18). Im Theme lässt sie sich nicht einstellen, und das ist Absicht: Eine Grenze im Theme könnte von der Vorgabe abweichen, gegen die der Server den Nachweis wieder einlöst — Ihre Kundschaft sähe ein grünes Prüffenster und danach eine Absage.

Wer beides braucht, hat zwei Wege:

  1. Auf 18 stellen und Bier mitprüfen. Zu streng, aber nie zu lasch — und für die meisten Läden die richtige Antwort.
  2. Zwei Anbindungen: zwei Schlüsselpaare, zwei EdelVerify-Konten, zwei Läden. Aufwändig; sagen Sie uns, wenn Sie es brauchen, dann bauen wir es.

Die Grenze, die wirklich gilt, steht im Prüffenster: Es holt sie beim Start vom Dienst und schreibt sie hin («Über 16 bestätigt»). Sie muss dort nicht geraten werden.


Ohne Shopify Plus gibt es bei Shopify keine Altersprüfung vor dem Bestellabschluss — nicht von uns und nicht von einem Mitbewerber. Die Zusage lautet dann: *Jede betroffene Bestellung wird binnen Sekunden geprüft und ohne Nachweis vom Versand angehalten.* Die Bestellung entsteht, das Geld wird eingezogen, nur die Ware geht nicht raus.

Mit Shopify Plus kommt eine echte Sperre dazu, die auch bei Apple Pay greift. Sie prüft aber nur, ob ein Nachweis-Kennzeichen im Warenkorb liegt, nicht ob es echt ist. Wer weiss, was er tut, setzt es sich selbst — und wird dann nach der Bestellung gefasst, genau wie ohne Plus.

Alles, was mit Plus zu tun hat, steht in dieser Anleitung unter «Nur mit Plus». Ohne Plus überspringen Sie diese Abschnitte ersatzlos.


1. Was Sie bereithalten müssen

  • Zugang zum Shopify-Admin Ihres Ladens (Rolle: Inhaber oder ein Mitarbeiter mit den Rechten «Apps und Vertriebskanäle» und «Themes»)
  • Das Paket: https://verify.edelbyte.ch/v1/shopify/edelverify-shopify.zip — der Projektbaum für die Shopify-CLI, samt dieser Anleitung und dem Prüfplan
  • Ihre beiden EdelVerify-Schlüssel — die bekommen Sie von uns, siehe unten:
  • öffentlicher Schlüssel, beginnt mit pk_live_ (zum Testen: pk_test_)
  • geheimer Schlüssel, beginnt mit sk_live_ (zum Testen: sk_test_)
  • Ihre myshopify.com-Adresse — EdelByte legt den Shop damit von Hand an (siehe Schritt 3); einen Installationslink gibt es heute noch nicht
  • ein Handy mit der swiyu-App und einer E-ID, um am Ende selbst zu probieren

Der geheime Schlüssel gehört nie ins Theme. Er wird ausschliesslich im EdelVerify-Bereich der App eingetragen (Schritt 4) und liegt dort auf unserem Server. Was im Theme steht, kann jeder Besucher lesen.

Woher Sie die Schlüssel bekommen

Einen Selbstbedienungszugang gibt es heute nicht: kein Anmeldeformular, kein Konto, das Sie sich selbst anlegen. Wir legen Ihren Shop von Hand an und schicken Ihnen das Paar — zum Bauen die Testschlüssel pk_test_… und sk_test_…. Im selben Zug hinterlegen wir die erlaubte Herkunft.

Melden Sie uns dabei gleich Ihre myshopify.com-Adresse, Ihre eigene Domain und die Altersgrenze, die Ihre Ware verlangt.

Was es kostet

Heute nichts — weil heute nichts verkauft wird. Es gibt keinen Preis, kein Abonnement und keine Rechnung; wir nennen bewusst auch keinen Betrag «ab», solange die produktive E-ID nicht steht. Was es gibt, ist ein Pilotgespräch, Testschlüssel zum Bauen und ein Platz auf der Warteliste. Über Konditionen sprechen wir, bevor Sie scharf schalten — nicht danach. Für Shopify kommt hinzu, was in Abschnitt 0 steht: Der Riegel vor dem Bestellabschluss verlangt Shopify Plus, und das ist ein Preis, den Shopify stellt, nicht wir.

Die vollständige technische Anleitung — Schnittstelle, Fehlerschlüssel, Webhook, Testausgänge — steht unter https://verify.edelbyte.ch/entwickler


2. Die Artikel bestimmen, die geprüft werden sollen

Shopify braucht eine Liste, welche Artikel altersbeschränkt sind. Dafür dient eine Collection (deutsch im Admin: «Kategorie» bzw. «Sammlung»).

  1. Shopify-Admin → ProdukteSammlungenSammlung erstellen
  2. Titel z. B. Altersgeprüft 18+
  3. Sammlungstyp: manuell oder automatisch, wie es Ihnen lieber ist
  4. Speichern und die betroffenen Artikel hinzufügen
  5. Wichtig: Rechts unter Vertriebskanäle muss Online Store angehakt sein.
  6. Notieren Sie den Handle. Er steht ganz unten auf der Seite der Sammlung unter «Vorschau der Suchmaschinenliste» → Bearbeiten → in der URL, z. B. altersgeprueft-18.

Warum der Vertriebskanal wichtig ist: Eine Sammlung, die im Online Store nicht verfügbar ist, sieht Ihr Theme nicht — der Hinweis im Laden bliebe dann aus. Geprüft würde die Bestellung trotzdem, aber der Kunde erführe erst hinterher davon. Das ist der ungünstigste aller Fälle.

Lassen Sie das Feld später leer, wenn *alles* in Ihrem Laden altersbeschränkt ist. Leer heisst ausdrücklich der ganze Shop, nicht «nichts».


3. Die App installieren

Diese App lässt sich heute noch nicht selbst installieren. Es gibt keinen Installationslink, den Sie öffnen könnten: Der OAuth-Weg von Shopify ist bei uns nicht gebaut — die Rückrufadressen, die Shopify nach der Zustimmung anspringen würde, antworten mit 404. Auch einen EdelVerify-Bereich im Shopify-Admin gibt es nicht: Unsere Seiten verbieten die Einbettung ausdrücklich (X-Frame-Options: DENY, frame-ancestors 'none'), ein eingebetteter Bereich kann dort also gar nicht erst erscheinen.

Stattdessen legt EdelByte Ihren Shop von Hand an. Sagen Sie uns Ihre myshopify.com-Adresse; wir richten die Anbindung ein und melden uns mit den Angaben, die Sie für Schritt 5 brauchen. Die Berechtigungstabelle unten beschreibt, was die App dabei erhält — lesen Sie sie, auch wenn Sie nichts anzuklicken haben.

Shopify zeigt in der Zustimmung, welche Berechtigungen die App verlangt. Jede hat genau einen Grund:

BerechtigungWofür
Bestellungen lesendie neue Bestellung überhaupt sehen
Bestellungen bearbeitenKennzeichen, Notiz und Prüfvermerk an die Bestellung schreiben
Produkte lesenprüfen, ob ein Artikel aus Ihrer Sammlung bestellt wurde
Fulfillment (eigenes Lager und Dienstleister)den Versand anhalten, wenn kein Nachweis vorliegt
App-Proxydie Rückmeldung des Prüffensters unter Ihrer eigenen Domain entgegennehmen

Jeder Shopify-Shop braucht eine eigene Anbindung. Nur wenn Ihre Shops in derselben Plus-Organisation liegen, genügt eine.


4. Schlüssel eintragen

Auch dieser Schritt läuft heute über uns. Es gibt keinen EdelVerify-Bereich im Shopify-Admin, in dem Sie etwas eintragen könnten — aus demselben Grund wie in Schritt 3. Wir hinterlegen die folgenden Angaben für Sie; melden Sie sie uns, dann ist der Schritt in wenigen Minuten erledigt:

  1. Geheimer Schlüssel (sk_live_…). Er bleibt auf unserem Server und wird nie an den Browser ausgeliefert. Schicken Sie ihn uns nicht per E-Mail — wir erzeugen ihn ohnehin selbst und teilen ihn Ihnen mit.
  2. Sammlung → den Handle aus Schritt 2, z. B. altersgeprueft-18.
  3. Nicht geprüfte Bestellungen automatisch stornieren bleibt aus. Eine Stornierung ist bei Shopify unumkehrbar; ein angehaltener Versand ist in zehn Sekunden wieder freigegeben. Sagen Sie uns ausdrücklich Bescheid, wenn Sie das anders wollen.
  4. Verbindung prüfen. Wir prüfen für Sie nach, ob die Prüfstelle erreichbar ist, ob der geheime Schlüssel angenommen wird und ob Ihr Laden als Herkunft zugelassen ist, und melden Ihnen das Ergebnis zurück.

Den öffentlichen Schlüssel (pk_live_…) tragen Sie im nächsten Schritt im Theme ein — dorthin gehört er, und nur dorthin. Das ist der erste Schritt, den Sie wirklich selbst machen.


5. Den App-Block im Theme einschalten — der Schritt, den man vergisst

Shopify schaltet Blöcke einer App nicht von selbst ein. Solange Sie das nicht tun, ist im Laden nichts von der Altersprüfung zu sehen.

  1. Shopify-Admin → OnlineshopThemes
  2. Beim aktiven Theme auf Anpassen
  3. Links in der Seitenleiste ganz unten auf App-Einbettungen (englisch: *App embeds*)
  4. Altersprüfung (E-ID) finden und den Schalter auf ein stellen
  5. Auf den Namen klicken — jetzt erscheinen die Einstellungen:
  6. Öffentlicher Schlüssel: pk_live_… einsetzen
  7. Collection der altersbeschränkten Artikel: der Handle aus Schritt 2, oder leer für den ganzen Shop
  8. Kassenknöpfe ohne Nachweis sperren: eingeschaltet lassen
  9. Die technischen Einstellungen darunter bleiben, wie sie sind
  10. Oben rechts Speichern

Was passiert, wenn Sie diesen Schritt vergessen

Im Laden erscheint kein Prüffenster und kein Hinweis. Kunden bestellen altersbeschränkte Ware, ohne je nach ihrem Alter gefragt worden zu sein.

Die Bestellungen werden trotzdem geprüft und ohne Nachweis vom Versand angehalten — Sie sitzen dann also auf lauter angehaltenen Bestellungen von Kunden, die gar nicht wussten, dass sie etwas hätten bestätigen sollen. Jede einzelne müssen Sie von Hand nachfassen.

Mit Plus kommt hinzu: Die Kasse blockt, aber im Laden gibt es keinen Weg, die Prüfung zu bestehen. Ihre Kunden stehen vor einer verschlossenen Tür ohne Klinke.

Prüfen Sie es deshalb sofort nach: Schritt 7.

Wechseln Sie das Theme später (oder installieren ein neues), müssen Sie diesen Schritt im neuen Theme wiederholen.


6. Der Webhook — nichts zu tun, aber gut zu wissen

Das Abonnement auf orders/create bringt die App mit; Sie müssen im Admin nichts einrichten. Es ist die verbindliche Prüfung: Innerhalb von Sekunden nach jeder Bestellung entscheidet unser Server, ob ein Nachweis vorliegt.

Was Sie danach an der Bestellung sehen:

FallKennzeichenWeitere Wirkung
geprüftedelverify:geprueftPrüfvermerk auf der Bestellseite
nicht geprüftedelverify:ungeprueftVersand angehalten, Notiz, Risiko «hoch»
nichts Altersbeschränktes dabeiedelverify:nicht-noetig
Testbetriebzusätzlich edelverify:test

Nach den Kennzeichen können Sie die Bestellliste filtern: tag:edelverify-ungeprueft. Eine eigene Spalte in der Bestellliste erlaubt Shopify einer App nicht.

Reicht ein Kunde den Nachweis nach, geben Sie den Versand im EdelVerify-Bereich wieder frei — oder in Shopify direkt beim jeweiligen Versandauftrag über Halten aufheben.


7. Probekauf — bitte wirklich machen

  1. Legen Sie einen altersbeschränkten Artikel in den Warenkorb.
  2. Unten erscheint der Streifen «Ihr Warenkorb enthält altersbeschränkte Artikel …». Erscheint er nicht, ist Schritt 5 nicht erledigt — oder die Sammlung ist nicht im Online Store verfügbar (Schritt 2, Punkt 5).
  3. Öffnen Sie die Warenkorbseite. Es legt sich eine Ebene darüber.
  4. Alter bestätigen → QR-Code erscheint → mit der swiyu-App scannen → bestätigen.
  5. Das Fenster wird grün, danach lädt die Seite neu, Streifen und Ebene sind weg.
  6. Bestellen Sie zu Ende und öffnen Sie die Bestellung im Admin: Sie trägt edelverify:geprueft.

Die Reihenfolge in Punkt 5 ist wichtig. Lädt die Seite neu, bevor das Fenster grün wird, ist etwas falsch eingerichtet — melden Sie es uns, statt es zu wiederholen: Ein Nachweis wird genau einmal ausgeliefert.


8. Nur mit Plus: die erzwungene Sperre einschalten

Ohne Shopify Plus überspringen Sie diesen Abschnitt. Die Function lässt sich dann nicht aktivieren — das ist Shopifys Tarifgrenze, kein Fehler.

  1. Die App muss mit der Berechtigung Validierungen installiert sein. Ist sie erst nachträglich dazugekommen, fragt Shopify Sie erneut um Zustimmung.
  2. Im EdelVerify-Bereich: Sperre im Checkout aktivieren.
  3. Danach steht sie im Shopify-Admin unter **Einstellungen → Checkout → Validierungen**. Dort können Sie sie sehen, aber lassen Sie sie eingeschaltet.
  4. Probekauf wiederholen, diesmal ohne Bestätigung: Der Checkout muss die Bestellung mit der Meldung «Bitte bestätigen Sie zuerst Ihr Alter …» verweigern — auch über Apple Pay oder Shop Pay.

Höchstens 25 solcher Validierungen sind je Shop möglich; unsere ist eine davon.


9. Wenn etwas nicht tut

BeobachtungHäufigste Ursache
Kein Streifen im LadenApp-Block nicht eingeschaltet (Schritt 5) oder im neuen Theme nicht wiederholt
Streifen erscheint bei jedem ArtikelSammlungsfeld ist leer — das heisst «ganzer Shop»
Streifen erscheint nie, obwohl der Artikel in der Sammlung istSammlung nicht im Vertriebskanal «Online Store»
Fenster öffnet nicht, Meldung «gerade nicht erreichbar»falscher oder fehlender öffentlicher Schlüssel, oder Ihre Domain ist im EdelVerify-Admin nicht als Herkunft zugelassen
Alles bestätigt, Bestellung trotzdem ungeprueftgeheimer Schlüssel falsch (Schritt 4, «Verbindung prüfen»)
Sperre wirkt nicht bei Apple Payohne Plus so vorgesehen; mit Plus: Schritt 8 nicht ausgeführt

10. Was Sie Ihren Kunden gegenüber sagen können

  • Übermittelt wird eine einzige Angabe: Altersgrenze erfüllt oder nicht. Kein Name, kein Geburtsdatum, keine Ausweiskopie.
  • An der Bestellung steht nur, dass, wann und gegen welche Grenze geprüft wurde — der Nachweis selbst wird nirgends gespeichert.
  • Die Prüfung läuft über die staatliche Schweizer E-ID und die swiyu-App.

Prüfplan

Damit nehmen Sie den Einbau selbst ab, ohne uns zu fragen. Geht ein Punkt nicht durch, schicken Sie ihn uns mit der Nummer — dann wissen wir sofort, wo wir suchen.

Prüfplan — Shopify-Seite

Was hier steht, liess sich ohne einen echten Shopify-Shop nicht klären. Alles, was sich an der Dokumentation klären liess, ist aus dieser Liste entfernt und steht am Ende unter «Was bereits belegt ist» — damit niemand dieselbe Arbeit zweimal macht.

Sortiert nach Risiko: Wer oben abbricht, braucht unten nicht weiterzulesen.

Es geht durchgehend um den Shop-seitigen Teil: shopify.app.toml, Theme App Extension, Validation Function. Der Server (App Proxy, Webhook, Admin-API-Aufrufe) hat seinen eigenen Plan.

Zur Altersgrenze steht hier nichts zu prüfen — und das ist der Befund. Die Schnittstelle kann gegen 16 oder gegen 18 prüfen, und die anderen drei Pakete stellen das je Warengruppe ein. Dieses Paket nicht: Shopifys Warengruppe ist die Sammlung, und der App-Block kennt genau eine. Es gilt deshalb eine Grenze für alles, und sie kommt aus der Vorgabe des EdelVerify-Kontos — dieselbe, gegen die der App-Proxy den Nachweis wieder einlöst.

Der Block schickt bewusst kein min_age mit. Täte er es, könnte die Grenze im Theme von der Vorgabe abweichen: Der Laden startete eine 16er-Prüfung, der Server verlangte 18, und die Kundschaft sähe ein grünes Prüffenster und danach eine abgewiesene Rückmeldung. Nachgeprüft wird das statisch: python3 scripts/pakete-pruefen.py, Abschnitt K — «setzt die Grenze nirgends selbst».

Die Einschränkung steht ausführlich in EINBAU.md, Abschnitt 0. Sie gehört ins Verkaufsgespräch, nicht in eine Fussnote nach der Installation.

Es gibt keinen Installationsweg — und auch das ist ein Befund, kein Prüfpunkt. Der OAuth-Weg ist nicht gebaut: Die Rückrufadressen, die Shopify nach der Zustimmung anspringen würde, antworten auf verify.edelbyte.ch mit 404 (gemessen für /auth/callback und /auth/shopify/callback). Ein eingebetteter App-Bereich im Shopify-Admin ist ebenfalls ausgeschlossen: Die Seiten liefern X-Frame-Options: DENY und frame-ancestors 'none', ein Rahmen kann dort also nie rendern.

Solange das so ist, legt EdelByte jeden Shop von Hand an, und alle Einstellungen aus EINBAU.md, Schritt 4, nehmen wir vor. Für diesen Prüfplan heisst das: Die Prüfungen unten setzen einen bereits von Hand angelegten Entwicklungsshop voraus — sie prüfen den Shop-seitigen Teil, nicht die Installation. Wer den OAuth-Weg baut, braucht dafür einen eigenen Plan; die Prüfungen hier decken ihn nicht ab.


0. Umgebung

npm install -g @shopify/cli@latest
shopify version

# Im Ordner der App:
shopify app config link          # verbindet mit der App im Dev Dashboard
shopify app dev --store=<dev-store>.myshopify.com

Ausserdem gebraucht:

  • ein Entwicklungsshop mit einem Online-Store-2.0-Theme (Dawn genügt) und einer Sammlung, die im Vertriebskanal «Online Store» verfügbar ist
  • ein EdelVerify-Testkonto mit pk_test_… / sk_test_…; die Domain des Entwicklungsshops muss dort als erlaubte Herkunft eingetragen sein, sonst scheitert Prüfung 6 und man sucht den Fehler im falschen System
  • ein Handy mit der swiyu-App und einer Beta-E-ID
  • für Weg B: ein Shop mit Shopify Plus. Ein gewöhnlicher Entwicklungsshop genügt nicht — Functions in Custom-Apps sind Plus vorbehalten. Ob ein Partner-Development-Store die Plus-Merkmale mitbringt, ist selbst zu prüfen (Prüfung 9).

Hoch

1. Überlebt ein Warenkorb-Attribut mit führendem Unterstrich die Bestellung?

Die ganze Bindung des Nachweises an die Bestellung hängt daran. Belegt ist nur, dass doppelter Unterstrich das Attribut überall verschwinden lässt; über den einfachen sagt die Dokumentation nichts.

Erste Bauhandlung, vor allem anderen. Im Laden, in der Browserkonsole:

await fetch(window.Shopify.routes.root + 'cart/update.js', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    attributes: { _edelverify: 'mit-strich', edelverify: 'ohne-strich' },
  }),
}).then((r) => r.json())

Dann eine Testbestellung abschliessen und nachsehen:

# Nutzdaten des Webhooks (Server-Log) — Feld note_attributes
# und/oder über die Admin API:
shopify app function replay   # nein, hier nicht; stattdessen:
query {
  orders(first: 1, reverse: true) {
    nodes { name customAttributes { key value } }
  }
}

Erwartet: beide Schlüssel kommen an. Wenn nur `edelverify` ankommt: an drei Stellen umstellen und nirgends sonst — blocks/edelverify.liquid (ev_attribut), src/cart_validations_generate_run.graphql (attribute(key:)) und die Webhook-Auswertung im Server.

2. Sieht die Validation Function dasselbe Attribut? *(nur mit Plus)*

Auch wenn das Attribut in der Bestellung ankommt, ist damit nicht gesagt, dass cart.attribute(key: "_edelverify") in der Function-Eingabe etwas liefert.

shopify app function run          # mit einer eigenen Eingabe-JSON
shopify app function replay       # mit einer echten Ausführung aus dem Shop

Erwartet: In der Eingabe der abgespielten Ausführung steht "attribute": { "value": "…" }. Steht dort null, obwohl das Attribut im Warenkorb liegt, ist die Function auf Plus wirkungslos — dann bleibt nur der Webhook, und das gehört dem Kunden gesagt.

3. Übersetzt die Function überhaupt?

Der Rust-Teil ist ein Gerüst. Die erzeugten Typpfade (schema::cart_validations_generate_run::input::cart::lines::Merchandise) und die Rückgabearten der Zugriffsmethoden (&String gegen &str, &bool gegen bool) stammen aus der Dokumentation, nicht aus einem Übersetzungslauf.

cd extensions/edelverify-gate
shopify app function schema          # holt schema.graphql
rustup target add wasm32-unknown-unknown
cargo build --target=wasm32-unknown-unknown --release
cargo doc --open                     # zeigt die tatsächlich erzeugten Typen
ls -l target/wasm32-unknown-unknown/release/edelverify_gate.wasm

Erwartet: Übersetzung ohne Fehler, Datei unter 256 kB. Weicht ein Typpfad ab, steht die richtige Schreibweise in cargo doc; die Logik bleibt davon unberührt.

Ebenfalls hier zu klären: ob die erzeugte Wasm-Datei bei einem Paketnamen mit Unterstrich tatsächlich edelverify_gate.wasm heisst — der Pfad in shopify.extension.toml muss stimmen, sonst schlägt der Deploy fehl.

4. Der Cart-Token in der Rückmeldeadresse

Unser Skript hängt den Token aus GET /cart.js als ?ct= an die Proxy-Adresse. Zwei Fragen auf einmal:

  1. Reicht der App Proxy eigene Abfrageparameter durch und nimmt sie in die Signatur auf? (Das Beispiel in der Dokumentation zeigt extra=1&extra=2 aber unser Wert enthält selbst ein ?key=…, also kodierte Sonderzeichen.)
  2. Ist der Wert derselbe wie cart_token in den Nutzdaten von orders/create, oder nur der Teil vor ?key=?

Handgriff: Im Laden eine Prüfung durchführen, im Server-Log die ankommende URL und den berechneten Signaturvergleich mitschreiben; danach die Bestellung abschliessen und cart_token aus dem Webhook danebenlegen.

Erwartet: Signatur stimmt, und die beiden Token lassen sich aufeinander abbilden. Wenn nicht, wird serverseitig auf den Teil vor ?key= normalisiert — an einer Stelle, nicht an zweien.

5. Wird `gate.js` von einer Content-Security-Policy geblockt?

Das Skript lädt https://verify.edelbyte.ch/v1/gate.js dynamisch nach. Setzt das Theme (oder eine andere App) eine CSP mit script-src, ohne unsere Domain zu nennen, passiert nichts — und zwar lautlos ausser einer Meldung in der Konsole.

Handgriff: Warenkorbseite öffnen, Konsole beobachten, dann auf «Alter bestätigen» klicken.

Erwartet: Das Skript lädt, window.EdelVerify existiert, das Fenster geht auf. Bei einer CSP-Meldung: mit dem Theme-Anbieter klären, ob verify.edelbyte.ch in script-src und connect-src aufgenommen werden kann.


Mittel

6. Die Prüfung von Anfang bis Ende, mit echter E-ID

  • [ ] Streifen erscheint, sobald ein Artikel der Sammlung im Warenkorb liegt
  • [ ] Auf der Warenkorbseite legt sich die Ebene darüber; Escape schliesst sie
  • [ ] «Alter bestätigen» öffnet das Fenster von gate.js, QR erscheint
  • [ ] Mit swiyu bestätigen → Fenster wird grün
  • [ ] Im Netzwerk-Tab: POST /apps/edelverify/confirm?ct=… antwortet 200 {"ok":true} vor dem Neuladen
  • [ ] Danach POST /cart/update.js mit dem Attribut, dann Neuladen (~0,9 s)
  • [ ] Nach dem Neuladen: kein Streifen mehr, Kassenknöpfe frei

Die Reihenfolge der letzten drei Punkte ist der Test. Lädt die Seite neu, bevor /confirm geantwortet hat, wird die Übermittlung abgebrochen und der Nachweis ist verbrannt — er wird genau einmal ausgeliefert. Deshalb hängt das Neuladen an edelverify:stored, nicht an edelverify:verified.

Danach ins Server-Log sehen: weder `sk_…` noch `av_…` darf dort auftauchen.

7. Überschreibt `cart/update.js` fremde Attribute?

Manche Themes und Apps schreiben selbst Warenkorb-Attribute. Ob unser Aufruf nur den eigenen Schlüssel setzt oder die übrigen mitnimmt, ist zu messen — sonst löschen wir einem anderen etwas weg (oder umgekehrt, und unser Nachweis verschwindet beim nächsten Klick des Themes).

await fetch(window.Shopify.routes.root + 'cart/update.js', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ attributes: { fremd: 'bleibt?' } }),
}).then(r => r.json())
// dann unser Attribut setzen und erneut GET /cart.js lesen

Erwartet: fremd steht danach noch da.

8. Kassenknöpfe in echten Themes

Die Auswahl [name="checkout"], .shopify-payment-button__button, form[action*="/checkout"] [type="submit"] deckt Dawn ab. Ein gekauftes Theme kann andere Knöpfe haben, vor allem in der Warenkorb-Schublade.

  • [ ] Dawn: Warenkorbseite und Schublade
  • [ ] Das Theme des Kunden, beide Orte
  • [ ] Nach dem Öffnen/Schliessen der Schublade greift die Sperre weiterhin (Beobachter)
  • [ ] Express-Checkouts (Shop Pay, Apple Pay, Google Pay) gehen daran vorbei — das ist erwartet und ohne Plus nicht zu ändern. Trotzdem einmal ansehen, damit es niemanden überrascht.

9. Läuft die Function auf dem Zielsystem überhaupt? *(nur mit Plus)*

Zu klären, bevor irgendetwas zugesagt wird:

  • Tarif je Shop des Kunden (nur Plus kann Custom-Apps mit Functions)
  • ob die Shops in einer Plus-Organisation liegen (entscheidet, ob eine Installation genügt)
  • ob ein Partner-Development-Store die Function annimmt (zum Entwickeln)
shopify app deploy
# danach im Admin: Einstellungen → Checkout → Validierungen

Erwartet: Die Validierung erscheint und lässt sich einschalten. Kommt eine Tarifmeldung, ist Weg B für diesen Shop erledigt — dann gilt Weg A, und das gehört ins Kundengespräch, nicht in ein Ticket.

10. Instruktionsbudget

Erst messen, dann die Härtung aus Bauvorgabe 6.5 (signiertes Attribut mit HMAC-SHA256) zusagen.

shopify app function replay

Erwartet: Die einfache Fassung liegt weit unter den Referenzbeispielen (17 177 bzw. 19 272 Instruktionen für vergleichbare Prüfungen). Erst wenn HMAC-SHA256 dazukommt, wird es eng — dann hier nachmessen, nicht schätzen.

11. Der Umfang der Function

Die Collection-IDs stehen als Literal in der Eingabeabfrage und werden beim Deploy eingesetzt; das Metafeld edelverify/config entscheidet nur zwischen «ganzer Shop» und «nur diese Collection».

  • [ ] Metafeld gesetzt (metafieldsSet auf die Validation) → Function sieht den Wert in der Eingabe
  • [ ] Metafeld nicht gesetzt → jeder Warenkorb gilt als altersbeschränkt (gewollt: fail-closed), und der Händler merkt es sofort
  • [ ] Platzhalter COLLECTION_ID_EINSETZEN nicht ersetzt → dieselbe Wirkung wie ohne Metafeld, nie eine stille Freigabe

Niedrig

12. Theme-Editor: Block, Einstellungen, Übersetzungen

  • [ ] Altersprüfung (E-ID) erscheint unter Theme-Einstellungen → App-Einbettungen
  • [ ] Alle sechs Einstellungen sind da, mit deutschen Beschriftungen — steht dort roher Text wie t:settings.public_key.label, findet Shopify die Schema-Sprachdateien nicht
  • [ ] Der Direktlink zum Einschalten funktioniert: https://<shop>.myshopify.com/admin/themes/current/editor?context=apps&template=index&activateAppId=<client_id>/edelverify (der letzte Teil ist der Dateiname des Blocks, nicht der Name der Extension)
  • [ ] Im Editor erscheint der Streifen, ohne dass etwas gesperrt wird (Shopify.designMode)

13. Sprachwahl im Laden

  • [ ] Ein deutschsprachiger Laden zeigt die Texte aus de.json
  • [ ] fr / it ebenso
  • [ ] Eine nicht abgedeckte Sprache fällt auf en.default.json zurück und zeigt Englisch — keine leeren Streifen, keine Schlüsselnamen

Offen ist dabei, ob eine Theme App Extension zwingend en.default.json als Vorgabedatei verlangt oder auch de.default.json annähme. Deshalb liegt Englisch als Vorgabe bei; das ist die risikoärmere Wahl, nicht die schönste.

14. Auswahlfeld statt Handle

Die Sammlung wird heute als Text (Handle) eingetragen. Schöner wäre ein Auswahlfeld ("type": "collection"). Belegt ist nur, dass Resource-Settings in App blocks existieren; für App embed blocks sagt die Dokumentation nichts, und sie können ausdrücklich nicht auf dynamische Quellen zeigen.

Handgriff: In einer Wegwerf-Fassung des Blocks "type": "collection" setzen, shopify app deploy, im Editor nachsehen. Wird das Feld angeboten und liefert block.settings.collection.handle einen Wert, wird umgestellt — das spart die häufigste Fehleingabe (Titel statt Handle).

15. Warenkorb-Schublade und `request.page_type`

Die sperrende Ebene erscheint nur, wenn request.page_type == 'cart'. Themes mit Schublade führen den Kunden nie auf diese Seite.

  • [ ] In einem Schubladen-Theme: Klick auf «Zur Kasse» in der Schublade wird abgefangen und öffnet die Prüfung direkt
  • [ ] Nach dem Abbrechen bleibt der Kunde in der Schublade, nichts hängt

16. Grössengrenzen bei Deploy

shopify app deploy

Shopify prüft die Extension beim Deploy: alle Dateien zusammen ≤ 10 MB, höchstens 30 Blöcke, Liquid über alle Dateien ≤ 100 kB, je Sprachdatei ≤ 15 kB. Für JavaScript und CSS gelten 10 kB bzw. 100 kB als Empfehlung, nicht als harte Grenze. Unsere Dateien liegen deutlich darunter — nachsehen lohnt erst, wenn jemand etwas hinzufügt.


Was bereits belegt ist

Diese Punkte standen zur Frage und sind an shopify.dev geklärt (Stand

  1. August 2026). Sie gehören nicht mehr geprüft.
FrageAntwortQuelle
Aufbau von shopify.app.tomlname, client_id, application_url, embedded, handle, [access_scopes] scopes, [auth] redirect_urls, [webhooks] api_version + [[webhooks.subscriptions]] mit topics / compliance_topics / uri, [app_proxy] url/prefix/subpath, [build]app-configuration
Erlaubte Werte für prefixa, apps, community, tools; subpath ≤ 30 Zeichen, nicht admin, services, password, loginebenda
Inhalt von shopify.extension.toml (Theme)nur name, type = "theme", optional handletheme-app-extensions/configuration
Ordner der Theme App Extensionassets, blocks, snippets, locales, package.json, shopify.extension.tomlebenda
App embed block"target" ist head, compliance_head oder body; wird vor </head> bzw. </body> eingefügt; ab Werk deaktiviert, der Händler muss ihn im Theme-Editor einschaltenebenda
Weitere Schema-Attributename (< 25 Zeichen, ohne App-Namen), stylesheet, javascript, enabled_on/disabled_on, class, tag, settings, default, available_ifebenda
Blöcke im Checkout«App blocks and app embed blocks can't be rendered on checkout pages» — deshalb liegt die sperrende Ebene über dem Warenkorbebenda
Grenzen10 MB gesamt, 30 Blöcke, 100 Sprachdateien à 15 kB, Liquid ≤ 100 kB (erzwungen); CSS 100 kB / JS 10 kB (empfohlen)ebenda
Direktlink zum Einschalten…/admin/themes/current/editor?context=apps&template={template}&activateAppId={api_key}/{handle}, {handle} = Dateiname des Blocksebenda
Privates Warenkorb-Attributdoppelter Unterstrich; solche Attribute sind «not available in the Liquid cart.attributes object or the Ajax API» — für uns unbrauchbarAjax Cart API
Felder aus GET /cart.jstoken, item_count, attributes, items[].key, .product_id, .variant_id, .quantity; Token in der Form Z2Nw…?key=…ebenda
Sprachbewusste Ajax-Adressenwindow.Shopify.routes.root, endet immer auf /Ajax API
product.collections in Liquidvorhanden — aber «Collections that aren't available on the Online Store sales channel aren't included». Daher der Pflichtschritt im EINBAU.product
request.page_typeWerte u. a. cart, product, collection, indexrequest
App-Proxy-SignaturParameter shop, path_prefix, timestamp, logged_in_customer_id, signature; unkodiert, sortiert, verkettet, HMAC-SHA256 mit dem Client Secret, hexauthenticate-app-proxies
Cookies über den Proxykeine — Shopify entfernt Cookie und Set-Cookieebenda
Händler ändert die Proxy-URLEinstellungen → Apps und Vertriebskanäle → {App} → App-Proxy → URL anpassen; Änderungen an subpath/prefix in der App wirken nur auf neue Installationenapp-proxies
Function: Netzzugrifffür gewöhnliche Plus-Händler nicht verfügbarnetwork-access
Function: inAnyCollection(ids:)vorhanden, ids ist Pflicht und ein Literal — Eingabeabfragen kennen keine LaufzeitvariablenCart and Checkout Validation
Function: cart.attribute(key:)vorhanden, liefert key und valueebenda
Function: validation.metafield(namespace:key:)vorhanden, value ist immer eine Zeichenketteebenda
Function: Ziel der Fehlermeldung«You can also use $.cart to target the current checkout page»ebenda
Function in Rust#[typegen("schema.graphql")] mit #[query("src/….graphql")], Einstieg #[shopify_function] fn cart_validations_generate_run(input: schema::cart_validations_generate_run::Input); Union über schema::…::input::cart::lines::Merchandise::ProductVariant(v); main() bricht abRust for Functions, Beispiele in der Validation-Referenz
Function: Bauzielab shopify_function 2.0 wasm32-unknown-unknown, Wasm-Datei < 256 kBRust for Functions
Functions und Tarif«Only stores on a Shopify Plus plan can use custom apps that contain Shopify Function APIs.»functions
Kann der Laden die Altersgrenze selbst wählen?nein, und zwar absichtlichDie Sammlung ist die Warengruppe; es gibt genau eine. Die Grenze kommt aus der Vorgabe des Händlerkontos, gegen die auch /api/shopify/proxy/confirm einlöst. Belegt durch scripts/pakete-pruefen.py, Abschnitt K.
Zeigt das Prüffenster die richtige Grenze?jagate.js liest min_age aus der Antwort des Dienstes und schreibt sie in den Text («Über 16 bestätigt»). Es rät sie nicht. Nachgemessen: scripts/api-vertragstest.py, Abschnitt 10.
Woran erkennt der Erfolg sich — over_18 oder age_ok?age_okover_18 ist nur wahr, wenn gegen 18 geprüft wurde; bei einer bestandenen 16er-Prüfung steht dort false. Sowohl gate.js als auch /api/shopify/proxy/confirm lesen deshalb age_ok. In einem Laden mit Grenze 16 wäre die andere Wahl eine Kundschaft, die alt genug ist und trotzdem abgewiesen wird. Belegt: scripts/pakete-pruefen.py, Abschnitt J.
Braucht die Validation Function die Grenze?neinSie prüft nur, ob ein Nachweis-Kennzeichen im Warenkorb liegt. Bei einer einzigen Grenze je Laden gibt es nichts zu unterscheiden.