Matko Milić logo
Natrag na blog
react-nativerevolutpaymentscase-studyios

Kako sam integrirao Revolut plaćanja — Apple Pay, Google Pay i Revolut Pay — u React Native mobilnu aplikaciju

Matko Milić·22. 08. 2026.·6 min čitanja
Kako sam integrirao Revolut plaćanja — Apple Pay, Google Pay i Revolut Pay — u React Native mobilnu aplikaciju

Lanac autopraonica nas je kontaktirao da im od nule izgradimo mobilnu aplikaciju — Odlučivši se za tehnologije Expo, React Native, Firebase backend i ostale, krenuli smo u izradu no brzo smo spoznali sljedeće: nije bila riječ samo o autopraonicama. Ista aplikacija je trebala prodavati unaprijed plaćene kredite za usisavače, dozatore krpa za čišćenje, pa čak i samoposlužne toalete — sustav je izgrađen da se skalira na 24 partnerske tvrtke, 59 lokacija i 571 pojedinačni samoposlužni uređaj diljem Hrvatske. Jedan kreditni saldo, iskoristiv na svemu tome, nadopunjiv s telefona bilo kojom metodom plaćanja koju je korisnik već imao — Apple Pay, Google Pay, spremljena kartica ili sam Revolut. Bez preusmjeravanja na web checkout, bez rezervnog "plati na terminalu" rješenja. Moralo je djelovati nativno.

Na kraju smo integrirali Revolut Merchant API za sva četiri načina plaćanja, uz Firebase Cloud Functions i webhook-driven pipeline narudžbi. Evo kako je to zapravo izgledalo — uključujući dijelove koji nisu bili zabavni.

Zašto Revolut, a ne Stripe

Ovo je bila klijentova odluka, a brojke je potvrđuju. Od siječnja 2026., Revolut Business naplaćuje 1% + €0.20 na plaćanja EEA potrošačkim karticama putem svog merchant API-ja. Stripeova standardna EEA domaća stopa je 1.5% + €0.25. Za posao s čestim malim nadopunama — po nekoliko eura odjednom, mnogo puta dnevno, na desecima lokacija — ta razlika se brzo nakuplja. Sam Revolut Pay (plaćanje s Revolut računa) je opet jeftiniji od usmjerenog plaćanja karticom, tako da mobilna aplikacija koja ozbiljno shvaća plaćanja će dosta benefirati od korištenja Revoluta.

Postojao je i praktičan aspekt: klijent je već bankao preko Revolut Businessa, pa je namirenje bilo isti račun, ista valuta, bez dodatnog posrednika.

Četiri načina plaćanja

Aplikacija je trebala podržati:

  • Apple Pay (iOS) — putem @rnw-community/react-native-payments, W3C PaymentRequest API-ja
  • Google Pay (Android) — ista biblioteka, isti API, drugačiji identifikator metode plaćanja
  • Revolut Pay (nativni gumb) — @revolut/revolut-pay-lite, Revolutov vlastiti SDK
  • Unos kartice@revolut/revolut-merchant-card-form

Apple Pay i Google Pay oba prolaze kroz isti PaymentRequest tok na klijentu — jedina razlika je koji supportedMethods deklarirate:

const request = new PaymentRequest([{
  supportedMethods: PaymentMethodNameEnum.ApplePay, // or AndroidPay
  data: { merchantIdentifier, countryCode: "HR", supportedNetworks: [...] },
}], {
  total: { label: "Self-service credits", amount: { currency: "EUR", value: amount.toFixed(2) } },
});

const response = await request.show();
const paymentData = response.details.applePayToken?.paymentData;
// forward paymentData to our backend, which hands it to Revolut

Token koji stigne s Appleovog PassKit sučelja — ili Google Payjevog ekvivalenta — nikad ne dodiruje Revolut izravno s klijenta. Prvo ide u Cloud Function, koja ga pridružuje Revolut narudžbi:

const paymentPayload = { payment_method: { type: "apple_pay", token: paymentToken } };
await fetch(\`${REVOLUT_API_BASE}/api/orders/${orderId}/payments\`, {
  method: "POST",
  headers: { Authorization: \`Bearer ${REVOLUT_SECRET_KEY}\`, "Revolut-Api-Version": "2024-05-01" },
  body: JSON.stringify(paymentPayload),
});

Revolut Pay je jednostavniji na klijentskoj strani — nativni SDK sam upravlja svojim payment sheetom čim dobije token narudžbe — no sve i dalje prolazi kroz istu logiku kreiranja narudžbe i webhooka na backendu.

Kreiranje narudžbi, webhookovi i idempotentnost

Svako plaćanje — bez obzira na metodu — počinje isto: klijent poziva funkciju createRevolutOrder s iznosom, ID-em korisnika i metapodacima narudžbe (koja partnerska lokacija, koliko kredita, koja metoda plaćanja je odabrana). Ta funkcija kreira Revolut narudžbu i vraća token i checkout referencu.

Stvarna potvrda ne dolazi od klijenta — dolazi od Revolutovog webhooka, na ORDER_COMPLETED:

  1. Revolut poziva naš webhook s eventom
  2. Ponovno dohvaćamo narudžbu izravno s Revolutovog API-ja (nikad ne vjerujemo iznosu/stanju iz webhook payloada bez provjere)
  3. Provjeravamo kolekciju processed_orders u Firestoreu prije nego išta napravimo — ako je ovaj ID narudžbe već obrađen, izlazimo. Webhookovi mogu i znaju stići više puta.
  4. Tek nakon toga odobravamo korisnikov saldo i sinkroniziramo ga s klijentovim vlastitim backendom za upravljanje objektima — zasebnim naslijeđenim sustavom koji je svaka partnerska lokacija već koristila.

Taj zadnji korak je ispao svoj mali projekt — API dobavljača objekata prethodi većini ovog stacka, očekuje specifične oblike polja, i trebao je translacijski sloj između "što nam Revolutov webhook govori" i "što strojevi na terenu zapravo razumiju."

Dio koji je zapravo trajao najduže: da iOS uopće proradi

Ništa od gore navedene logike plaćanja nije bio težak dio. Težak dio je bio natjerati Expo aplikaciju s use_frameworks! :linkage => :static (koje zahtijeva Firebase), New Architectureom i tri zasebna Revolut SDK-a da se kompajlira na Xcodeu 16.4 bez da CocoaPods padne.

Uzorak kako je ta borba izgledala:

  • Firebase podovi vs. statičko linkanje: use_frameworks! :linkage => :static prisiljava DEFINES_MODULE = YES na svaki pod. Firebaseovi ObjC podovi (RNFBApp, RNFBAuth, itd.) zatim okidaju Clangovu provjeru "module consistency" jer se RCTBridgeModule uvozi dvaput — jednom kroz Reactovu module mapu, jednom kroz nemodularan import unutar Firebaseovih vlastitih headera. Rješenje: prisiliti DEFINES_MODULE = NO na čistim ObjC Firebase podovima putem prilagođenog Expo config plugina — osim RNFBFirestorea, koji miješa Swift i ObjC i treba da mu module mapa preživi, samo uz uklonjeni sporni React header iz njegovog umbrella headera nakon pod install.
  • Xcode 16.4 / iOS 18 SDK vs. iOS 26-only API-ji: nekoliko Expo paketa (expo-image-picker, expo-image) poziva iOS 26 API-je unutar @available(iOS 26, *) blokova. Swift i dalje provjerava te grane naspram SDK-a s kojim stvarno kompajlirate — pa čak i kod koji nikad ne može izvršiti na iOS 18 ne uspije se kompajlirati na iOS 18. Trebalo je patchati te datoteke da se u potpunosti uklone iOS 26 grane.
  • Revolut SDK binarka kompajlirana za pogrešan Xcode: u jednom trenutku, rutinski npm install je podigao @revolut/revolut-pay-lite na verziju čija je temeljna nativna RevolutPayments binarka bila kompajlirana s Xcodeom 26 — i referencirala je Swift 6.1 runtime simbole koji jednostavno ne postoje na Xcodeu 16.4. Zaključao sam Revolut pakete na zadnju verziju kompajliranu protiv starijeg toolchaina.

Ništa od ovoga nisu bila jednolinijska rješenja — ugrađena su u prilagođene Expo config pluginove i skup idempotentnih post-install patch skripti koje se pokreću na svakom buildu, lokalno i na CI-u, tako da se nitko ne mora sjećati da ih ponovno napravi ručno.

Direktan rad s Revolutovim timom

Nekoliko trikijih problema — posebno oko ponašanja nativnog SDK-a na React Nativeovoj New Architectureu — nisu bile stvari koje sam mogao riješiti samo iz dokumentacije. Završio sam u nizu uživo debugging sesija s Revolutovim vlastitim developer support timom, prolazeći kroz integraciju zajedno u realnom vremenu. U jednom trenutku, nešto što smo zajedno riješili bilo je dovoljno specifično i korisno da je završilo u ažuriranju Revolutove vlastite React Native integration dokumentacije. Rijetka je stvar imati direktan pristup timu iza SDK-a koji integrirate, umjesto samo reda za podršku — to je napravilo stvarnu razliku u tome koliko brzo je ovo isporučeno.

Što je isporučeno

Četiri metode plaćanja, jedan pipeline narudžbi, verificiran webhookom i idempotentan, sinkroniziran uživo u postojeći on-premise sustav upravljanja svake partnerske lokacije, pokrenut na New Architecture React Nativeu u produkciji. Build pipeline je preživio nekoliko rundi nadogradnji Expo SDK-a i Revolut SDK-a otad, jer rješenja žive u kodu, a ne u nečijem sjećanju što treba kliknuti u Xcodeu. Značajka i aplikacija su isporučene pod Nordia Digitalom, gdje sam bio CEO i jedini developer na projektu.

Ako integriraš Revolut u React Native aplikaciju i naiđeš na neku od gore navedenih grešaka pri buildu, Revolut Merchant API dokumentacija i stranica Revolut Business naknada su dvije reference vrijedne otvaranja.