Billig + Swedbank-pay

Vår/sommer 2020 ble Billig skrevet opp til å bruke Swedbank Pay (rebranding av PayEx etter det ble kjøpt opp av Swedbank) sitt nye API, ettersom det gamle APIet skulle bli lagt ned. Flyten er stort sett lik som til Billig + PayEx; den største forskjellen er at det nye APIet bruker JSON istedenfor SOAP XML.

Oversikt

Kjøpsflyten er omtrent slik:

  1. Brukeren konstruerer en handlekurv e.l. på frontenden til han/hun er fornøyd.
  2. I en kjøpsform på frontenden fylles epostadresse eller kortnummer inn, sammen med kortinformasjon. Denne POSTes til betalingsbackenden (som ligger på en egen maskin med svært begrenset tilgang), med mål /pay.
  3. /pay gjør all nødvendig initialisering mot Swedbank Pay-APIet, med et «create payment»-kall etterfulgt av et authorize-kall (som inneholder kortdataen til kunden). Dette lager en reservasjon i kundens bankkonto, men pengene blir ikke faktisk trukket før i neste steg (såkalt to-fase-kjøp). Kunden blir deretter videresendt til frontendens OK-side som viser billettene (eller error-side med feilmelding), eventuelt via en 3D-secure-roundtrip.
  4. process-purchases.pl fullfører reservasjonen (gjør “capture” med et nytt API-kall for å gjøre den om til et ekte trekk), sender epost med billetter og merker billettene som aktive i databasen.

Dersom det på noe tidspunkt oppstår en feil i prosessen (alt fra ugyldig epostadresse til manglende penger på konto) blir brukeren sendt til frontendens feilside, med et session-parameter. Session-parameteret peker til en spesiell tabell i Billig-databasen hvor feilmelding ligger, samt nok informasjon til å rekonstruere handlekurven (slik at brukeren ikke trenger å gjøre det selv, men heller kan korrigere kun det som er feil).

Det kan når som helst skje at denne prosessen stopper, enten fordi brukeren går lei, fordi strømmen eller nettet går, eller andre årsaker man ikke har tenkt på. Det går derfor jevnlig et timeout-script (process-purchases) som tar ansvar for å rydde opp gamle ordre, og kansellere dem. Mer om dette under.

Detaljert informasjon om APIet fra frontendens side finnes i Billig frontend-API. Dokumentasjonen på Swedbank Pay-APIet finnes her: https://developer.swedbankpay.com/payments/card/direct

Intern implementasjon

Tilstandsmaskin

Kjernen i betalingsimplementasjonen er at alle ordre ikke lenger bare har et «paid»-tidspunkt (som også fungerer til å se om ordren er betalt eller ikke), men to ekstra felter: Tilstandsnummer, og kanselleringsflagg. Det eksisterer constraints i databasen for å sikre at kun gyldige kombinasjoner av disse blir brukt.

Kanselleringsflagget blir satt dersom brukeren begynner på en ny ordre før den gamle er ferdig; dette eksisterer for å unngå dobbeltklikk-problematikk (brukeren klikket to ganger på «kjøp» raskt etter hverandre), og for å raskere få uferdige ordre gjennom systemet. Kanselleringsflagget betyr i praksis at ingen prosesser skal gå lenger i å fullføre ordren, men avbryte den så fort som mulig. For hver bruker kan kun én ikke-kansellert ordre eksistere samtidig (det er igjen constraints for dette).

Tilstandene en ordre kan gå gjennom er disse:

Ordre starter alltid i tilstand 1, og beveger seg så gjennom tilstandene én for én til de enten havner i tilstand 5 (hvor også «paid» er satt), eller blir kansellert og flyttet til tilstand -1 (hvor også kanselleringsflagget fjernes). Det gir ikke mening at en ordre i tilstand -1 eller 5 har kanselleringsflagget satt.

Merk at en ordre som er i tilstand 2 egentlig er i ukjent tilstand hos Swedbank Pay; den i prinsippet være i enten 2, 3, 4 eller -1 avhengig av hva brukeren har gjort i ettertid. For sikkerhets skyld kan også ordre i sjeldne tilfeller endre seg i PayEx etter at vi har flyttet den til -1 (som regel at det er blitt trukket penger etter at vi tror at en ordre har timet ut). Den eneste måten vi kan finne ut av det på, er ved at PayEx kaller en spesiell callback-URL hos oss; dersom vi for en kansellert ordre (tilstand -1) har fått callback etter siste complete, flyttes den tilbake til tilstand 2, med «cancel»-flagget satt. Tidspunktene for callback og complete måles med en SQL-sekvens, for å unngå hjørnetilfeller når systemklokken beveger seg bakover o.a.. Dessuten er det tatt spesielt hensyn til om vi skulle få callback mens vi kjører complete på samme ordre: Tidspunktet som er lagret hentes ut før complete-kallet (men committes først etter at kallet har gått gjennom og vi har gjort alle endringer vi skal gjøre basert på det). Slik sørger vi for å avgjøre races i den konservative retningen (et ekstra complete-kall er ufarlig).

Det er ytterst sjelden at en ordre er i tilstand 3 og kansellert samtidig; duplikatlogikken vil, dersom den ser en annen ordre i tilstand 3, kansellere den nye ordren i stedet for den gamle. Det eneste tilfellet hvor denne kombinasjonen kan eksistere i praksis, er i et hjørnetilfelle i timeout-scriptet (se under), eller når Swedbank Pay informerer oss lenge after-the-fact (via en callback) om at en ordre vi trodde var kansellert, i virkeligheten er betalt (hvor den da blir i tilstand 2 med cancel-flagget satt, som kan flytte den til tilstand 3 med cancel-flagget satt). En ordre er aldri i tilstand 4 samtidig som kanselleringsflagget er satt.

Korrekthet

Flere prosesser (f.eks. webgrensesnittet og en eller flere instanser av timeout-scriptet) kan ønske å endre på tilstanden eller kanselleringsflagget. Det er derfor innført et låsesystem (ved hjelp av SELECT FOR UPDATE) likt det man ofte finner i «vanlig» låsbasert (ofte objektorientert) programmering. Vanlige regler burde være kjent for de som skal endre på dette systemet, men en grei oppsummering er:

Timeout

Timeoutscriptet (process-purchases.pl) går jevnlig fra cron på okkupasjon, og tar ansvar for ordre som enten er gått ut eller markert som kansellert, i tillegg til at det gjør capture på ordre og sender ut eposter med billetter. Logikken er relativt enkel:

Ved flytting til -1 (uansett fra-tilstand): Om cancel-flagget ikke er satt, send epost til brukeren for å informere om at ordren ble avbrutt. På den måten slipper brukeren å lure på om ordren gikk gjennom eller ikke, og får dessuten en link (lignende error-linkene i webgrensesnittet) hvor han/hun kan ta opp igjen kjøpet om ønskelig. (Eposten er tenkt informativ og ikke reklamerende, men vil nok likevel ha en viss salgseffekt.)

Lenker: Start, billig, billig frontend-api, billig generelt, billig stripe, historie

Epost: itk@samfundet.no | Telefon: 992 15 925 | Sist endret: 2020-09-04 13:54 | Revisjon: 8 (historie, blame) | Totalt: 1886 kB | Rediger