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:

  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 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).
  4. 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:

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:

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:

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 paynet, billig verp, til nye itkere

Mail: itk@samfundet.no | Telefon: 992 15 925 | Sist endret: 2015-02-17 01:25 | Revisjon: 11 (historie, blame) | Totalt: 1419 kB | Rediger