Denne siden er arkivert, og kan inneholde utdatert, gammel eller feil informasjon.
Billig PayEx
Tidlig 2010 begynte Samfundet en prosess for å bytte fra Paynet (som la ned APIet vi brukte) til PayEx, et prosjekt som fikk kodenavnet «okkupasjon» (etter «UKEnavnet» i 1941 og 1943 iflg. wiki.samfundet.no). Den største konseptuelle endringen er i den nye løsningen tar vi imot kortnummer selv (dvs., vi bruker et «direct»-API i stedet for Paynets «redirect»-API). Dette har en del ekstra organisatoriske implikasjoner; for mer informasjon, se Billig sikkerhetspolicy og PCI DSS sjekkliste. Denne siden vil primært handle om teknisk implementasjon.
Oversikt
Som med det gamle APIet (se Billig Paynet) er det et uttalt ønske å holde grensesnittet mot frontends (www.samfundet.no o.a.) så enkelt som mulig. Siden frontenden ikke har lov til å ta imot kortnummer, gjøres et kjøpt oversiktsvis slik:
- Brukeren konstruerer en handlekurv e.l. på frontenden til han/hun er fornøyd.
- 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.
- /pay gjør all nødvendig initialisering mot PayEx (via web service-APIet deres), og sender folk videre til /pay2, normalt sett via en Verified By Visa-side. /pay2 venter til kjøpet er i en stabil tilstand i PayEx (dvs., kjører “complete”) og videresender folk til frontendens OK-side (med billettnumrene som parameter).
- timeout-payex.pl fullfører reservasjonen (kjører “capture” 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 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. Informasjon om PayEx' web service-API finnes på http://www.payexpim.com/
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:
- 1. Ordren finnes i Billig.
- 2. Har initialisert i PayEx, venter på 3D-Secure/bankklarering.
- 3. Penger er reservert men ikke trukket ennå (complete er utført, men ikke capture).
- 4. Penger er trukket (complete er utført, capture er utført; eller ordren var énfase så capture var aldri behøvd).
- 5. Epost med billetter i er sendt, billetten er aktivert.
- -1. Kjøpet ble avbrutt, ingen penger er trukket (eller tilbakeført om nødvendig).
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 PayEx; 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 eller 4 og kansellert samtidig; duplikatlogikken vil, dersom den ser en annen ordre i tilstand 3 eller 4, 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 PayEx 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 eller 4 med cancel-flagget 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:
- Aldri endre på tilstanden til en ordre (herunder kanselleringsflagget) uten først å ha tatt en lås.
- Hold låser så kort tid som mulig; husk at du kan blokkere andre lesere.
- Aldri anta at tilstanden ikke har endret seg med mindre du holder låsen.
- Følge av de to forrige punktene: Aldri hold en lås under soving. Spesifikt betyr dette at om du skal gjøre et web service-kall, må du slippe opp låsen først, gjøre kallet, ta låsen igjen, og sjekke hva tilstanden nå er før du gjør noe mer.
Timeout
Timeoutscriptet går jevnlig fra cron på okkupasjon, og tar ansvar for ordre som enten er gått ut eller markert som kansellert. Logikken er relativt enkel:
- Tilstand 1: Flytt til -1.
- Tilstand 2: Gjør Complete-kall mot PayEx for å finne ut om ordren egentlig er i 3, 4 eller -1.
- Tilstand 3, cancel-flagget satt: Kanseller reservasjonen, flytt til -1. (Merk: Denne kombinasjonen kan bare genereres av timeout.pl selv.)
- Tilstand 3, cancel-flagget ikke satt: Flytt til 4.
- Tilstand 4, cancel-flagget satt: Refunder pengene, flytt til -1. (Merk: Denne kombinasjonen kan bare genereres av timeout.pl selv.)
- Tilstand 4, cancel-flagget ikke satt: Send epost med billetter i, flytt til 5.
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 paynet, billig stripe, billig swedbank pay, billig verp, til nye itkere
Epost: itk@samfundet.no | Telefon: 992 15 925 | Sist endret: 2020-09-04 13:49 | Revisjon: 15 (historie, blame) | Totalt: 1905 kB | Rediger