Et gemt wallet-pass er en kopi
Kuvert-gavekort kan gemmes i Apple Wallet og Google Wallet. At få passene signeret var den nemme halvdel. Den svære halvdel var at opdage, at et gemt pass aldrig opdaterer sig selv, så et brugt kort blev ved med at vise sin gamle saldo.
Et gavekort der lever i en email går tabt i en indbakke. Et gavekort i Apple Wallet eller Google Wallet er på låseskærmen når gæsten går ind i restauranten. Så Kuvert lader modtageren gemme hvert kort og hver billet som en wallet pass, og i et par uger i august så den funktion ud til at være færdig længe før den var.
Signering uden en binær#
Et Apple pass er et zip-arkiv: et pass.json, billederne, en manifest af hashes, og en løsrevet signatur over manifestet lavet med et certifikat Apple udsteder til pass-typen. Enhver tutorial laver signaturen ved at brække ud til openssl smime.
Den Bun buildpack API'en kører på sender ikke en openssl binær. I stedet for at bøje buildet omkring en, skrives signaturen in-process: CMS-strukturen samles i TypeScript og signeres med certificatet og nøglen fra miljøet, med ingen afhængighed. Det er omkring hunderede linjer, og det betyder API'en behøver intet fra maskinen den kører på.
Google's side er lettere. Klasserne er registreret gennem Wallet API'en, og knappen gemme er et link der bærer en JWT signeret med udsteder's service-account nøgle.
"Noget gik galt"#
Google Wallet udstedere starter i demo-tilstand. Kun konti der holder en rolle på udstederen, eller der er blevet tilføjet som test-konti, kan gemme et pass. Alle andre får Googles uigennemsigtige "Noget gik galt", samme besked enhver anden nægtelse producerer, hvilket er hvorfor det tog en dag at identificere.
At forlade demo-tilstand betyder at bede Google om publishing access. Knappen der beder gør intet på det hele når uBlock Origin kører: det gengiver, du klikker det, og intet sker, med intet i konsollen en normal bruger ville se. At slukke ad blokkeren var retningen.
Den del værd at huske er at skiftet behøvede ingen deploy. Demo-tilstand hører til udsteder kontoen, ikke til koden, så de samme signerede links der allerede var i production begyndte at arbejde for alle det øjeblik Google godkendte det. Intet i lageret kan detect hvilken tilstand udstederen er i, fordi ingen API rapporterer det. Så tilstanden skrives ned i hånden i opsætningsnoterne, med datoen.
Apple havde sin egen version. Udvikler-kontoen skiftede fra en individuel til en organisation i samme uge. Team ID'et ændrede sig ikke, og det var tjekket mod et live pass i stedet for antaget: holdet navngivet i pass.json og det i certificatet inden i dets signatur stemmer overens. Da et mismatch der gør telefonen afviser et pass serveren tror den betjente fint, sammenlignes API'en nu de to ved boot og siger det så højt, sammen med hvor længe certificatet har tilbage.
Kopieringsproblemet#
Så den rigtige bug, som intet rapporterede.
En gæst gemmer et gavekort til deres telefon. De bruger det meste af det ved disken. Passet på deres telefon viser stadig det fulde beløb.
Et gemt pass er en kopi. Google læser objektet inde i et gem link første gang et pass bliver gemt og ignorerer det hver senere gang. Et Apple pass er en fil på en telefon. Gem linket blev genopbygget på enhver anmodning og var korrekt hver eneste gang, som er præcis hvad der gjorde buggen usynlig: enhver check vi kunne køre mod serveren bestod, og det eneste forkerte var i nogendes lomme.
Begge wallets har en måde at opdatere et pass der allerede er gemt, og de arbejder forskelligt:
Google: PATCH pass objektet gennem Wallet API'en, og den gemte kopi følger.
Apple: passet bærer en web-service URL og en token. Telefonen registrerer sig selv når passet gemmes, og når noget ændrer sig sender serveren en tom push gennem APNs. Telefonen spørger derefter web-servicen for det nye pass.
APNs pushen er godkendt med det samme certifikat der signerer passet, så dette tilføjede ingen nye Apple hemmelighed. Hvad det gjorde tilføj er en token der godkender hvert pass mod vores web-service, og den må aldrig roteres casually, fordi hvert pas allerede gemt bærer det.
At holde det fast#
Opdatering af passet er en funktion. Risikoen er næste kodestykke der flytter en saldo uden at kalde det.
En saldo bevæger sig flere steder end du ville gætte: en indløsning ved disken, en korrektion i dashboard'et, en cash-out, en refund eller en dispute der ankommer fra Stripe. Hver af dem kalder nu en hook efter dets skrivning, fire-og-glem, så en langsom wallet API kan aldrig fejle et salg.
Og der er en test der læser kilden. Den finder enhver service der skriver en saldo og fejler hvis en af dem ikke fortæller wallet'erne. Det er et stumpet instrument, og det er det eneste der stopper denne bug fra at komme tilbage stille næste gang nogen tilføjer en måde at bruge et kort.
Hvad jeg tog fra det#
Passet der viste den forkerte saldo skulle aldrig blive fanget af en test af serveren, fordi serveren var ret. Når en funktion overgiver en kopi af dine data til en anden persons system, er spørgsmålet ikke om kopien er korrekt når den forlader. Det er hvem der holder den korrekt bagefter, og svaret er normalt dig.
Var dette nyttigt?
Flere noter
Hvorfor Kuvert aldrig holder pengene
Kuvert sælger gavekort til danske restauranter og spillesteder, og den første beslutning var, hvor pengene ligger mellem salget og middagen. Svaret er: slet ikke hos os. Det ene valg formede refunderinger, chargebacks og gebyret.
Da en gavekortplatform skulle lære at sælge en klubaften
Kuvert startede som gavekort til restauranter. Så ville en natklub i Vejle have billetter, releases og bokse. En klub sælger ikke et bord kl. 19.30, den sælger natten, og døren viste sig at være den sværeste skærm i produktet.