Mobilni razvoj
FlutterRazvoj mobilnih aplikacijaKupnje unutar aplikacijePretplateRevenueCatiOSAndroid

Flutter kupnje unutar aplikacije i pretplate: Apple i Google postavljanje, testiranje i RevenueCat

AO
Adrijan Omićević
·15 min čitanja

# Što ćete izgraditi i zašto je to važno#

Ovaj vodič prolazi kroz implementaciju Flutter kupnji unutar aplikacije za jednokratne kupnje i pretplate, kroz dva pristupa:

  • Direktna integracija sa storeovima pomoću in_app_purchase plugina
  • Produkcijski “friendly” pristup uz RevenueCat za validaciju računa, entitlements, paywallove i sinkronizaciju stanja pretplatnika

Ako isporučujete pretplate, pogreške su skupe. Apple i Google mogu ukinuti pristup zbog povrata, chargebackova, grace perioda, ponovnih pokušaja naplate te nadogradnji ili degradacija plana, a pristup koji se oslanja samo na klijenta će prije ili kasnije dodijeliti pristup pogrešnim korisnicima.

Pokrit ćemo postavke u storeovima, implementaciju, koncept validacije računa, sandbox testiranje te posebnu sekciju za debugging čestih produkcijskih kvarova.

# Preduvjeti#

ZahtjevVerzijaNapomena
Flutter3.19+Radi i na novijim verzijama
Dart3+Usklađeno s Flutter stable
iOSXcode 15+Potrebno za moderne iOS buildove
AndroidAGP 8+Koristite aktualni Android Gradle Plugin
App Store Connect računAktivanS dovršenim ugovorima i bankovnim podacima
Google Play Console računAktivanS postavljenim payments profilom
Testiranje na stvarnom uređajuPreporučenoEmulatori mogu biti ograničeni za billing flowove

Ako vam je release proces još uvijek ručan, riješite to što ranije. Problemi s IAP-om često su specifični za okruženje i često ćete rebuildati. Pogledajte naš CI vodič: Flutter CI/CD s GitHub Actions, Codemagic i Fastlane.

# Odlučite arhitekturu: direktno na storeove vs RevenueCat#

IAP u Flutteru najčešće se gradi na dva načina.

Opcija A: Native store integracija s in_app_purchase#

Prednosti:

  • Nema dodatne ovisnosti o vendoru
  • Niži kontinuirani trošak
  • Puna kontrola

Nedostaci:

  • Vi ste odgovorni za validaciju računa, obnove, povrate i rubne slučajeve
  • Cross-platform stanje pretplatnika je teže uskladiti
  • Restore purchases i entitlement logika moraju biti besprijekorni

Opcija B: RevenueCat iznad storeova#

Prednosti:

  • Server-side validacija računa i upravljanje stanjem
  • Entitlements i offerings ujednačuju iOS i Android
  • Ugrađeni paywallovi i podrška za A B testiranje
  • Bolja vidljivost (observability) promjena statusa pretplatnika

Nedostaci:

  • Trošak vendora pri većem obujmu
  • I dalje trebate ispravnu konfiguraciju storeova
  • Još jedan SDK i dashboard za održavanje
MogućnostSamo in_app_purchaseRevenueCat
Osnovni purchase flowDaDa
Validacija računaVi implementirateUključeno
Obnove i otkazivanja pretplataVi pratiteUključeno
Abstrakcija entitlementsVi implementirateUključeno
Paywall offeringsVi implementirateUključeno
Restore purchasesVi implementiratePojednostavljeni helperi
Cross-platform stanje pretplateTežeLakše
Alati za debuggingMinimalnoDashboard događaji i logovi

🎯 Ključna poruka: Ako su pretplate ključan izvor prihoda, koristite RevenueCat osim ako već nemate zreo backend i iskustvo s billingom.

# Postavljanje proizvoda na Appleu: App Store Connect#

Appleove postavke određuju možete li uopće dohvatiti proizvode u sandboxu. Većina “product not found” bugova počinje ovdje.

1) Kreirajte aplikaciju i omogućite IAP capability#

  • Kreirajte aplikaciju u App Store Connect s ispravnim bundle ID-jem
  • U Xcodeu omogućite capability In-App Purchase za target
  • Osigurajte da se bundle ID potpuno podudara između Xcodea, App Store Connecta i svih okruženja

2) Kreirajte In-App Purchase proizvode#

Apple ima tri najčešća IAP tipa za većinu Flutter aplikacija:

  • Consumable: coinovi, krediti, jednokratni itemi koji se mogu ponovno kupiti
  • Non-consumable: trajno otključavanje
  • Auto-renewable subscription: mjesečni ili godišnji planovi

Za pretplate dodatno konfigurirate:

  • Subscription Group: korisnik može imati samo jednu aktivnu pretplatu po grupi
  • Subscription Levels: za upgrade i downgrade

Minimalni checklist po proizvodu:

  • Reference name
  • Product ID, npr. com.samioda.app.pro.monthly
  • Pricing
  • Localization display name i description
  • Review screenshot u nekim slučajevima

3) Ugovori, porezi i bankovni podaci#

Ako ugovori nisu prihvaćeni ili bankovni podaci nisu dovršeni, kupnje mogu padati ili se proizvodi neće pojavljivati kako očekujete tijekom testiranja.

4) Sandbox testeri i testna distribucija#

Appleova očekivanja oko sandbox testiranja često “sapletu” timove:

  • Za iOS instalirajte preko TestFlighta za realističnije testiranje. Lokalni debug install može dohvatiti proizvode, ali ćete rjeđe uhvatiti “stvarne” scenarije.
  • Koristite sandbox Apple ID kreiran u App Store Connect pod users and access.
  • Na uređaju se odjavite sa standardnog Apple ID-ja unutar App Store purchase flowa kad vas pita, pa se prijavite sa sandbox testerom.

⚠️ Upozorenje: Apple sandbox obnove pretplate su ubrzane i mogu se obnoviti više puta u kratkom roku. Aplikacija mora podnijeti više renewal događaja bez ponovnog dodjeljivanja consumables ili dupliranja zapisa o pristupu.

# Postavljanje proizvoda na Googleu: Play Console#

Google Play Billing je strog po pitanju testing trackova i računa.

1) Kreirajte aplikaciju i konfigurirajte billing#

  • Kreirajte aplikaciju u Play Console
  • Dovršite payments profil i svaku potrebnu verifikaciju
  • Osigurajte da je aplikacija ispravno potpisana i barem jednom uploadana na testing track

2) Kreirajte proizvode i pretplate#

U Play Console:

  • Sekcija Monetize za in-app products i subscriptions
  • Kreirajte product ID-eve poput pro_monthly ili pro_yearly
  • Dodajte base plans i offers za pretplate ako ih koristite

U 2026. Google pretplate se najčešće oslanjaju na:

  • Base plans: definiraju period naplate i obnovu
  • Offers: trial, intro cijene, regionalni popusti

3) License testeri i internal testing#

Očekivanja za testiranje:

  • Dodajte svoj Gmail račun kao license tester
  • Uploadajte App Bundle u Internal testing
  • Instalirajte preko Play Store internal testing linka

Ako instalirate direktno preko Android Studija, Billing se može ponašati drugačije i često ne uspije dohvatiti “live” proizvode.

# Validacija računa i zašto klijent-only nije dovoljno#

Možete krenuti s client-side purchase flowovima, ali kontrola pristupa mora se oslanjati na pouzdan izvor.

Što može poći po zlu bez validacije#

  • Jailbroken uređaj može lažirati purchase state
  • Pretplata može biti refundirana ili završiti kao chargeback
  • Naplata može ući u grace period ili account hold
  • Korisnik može otkazati obnovu, ali i dalje biti aktivan do kraja perioda
  • Google može pauzirati ili odgoditi naplatu i obnoviti kasnije

Odluka o pristupu treba se temeljiti na verificiranom stanju pretplate, ne na tome “korisnik nam je rekao da je platio”.

Dva praktična pristupa#

PristupKako radiNajbolje za
Server-side validacijaVaš backend validira Apple i Google račune i sprema trenutno stanje entitlementaTimove s backendom + billing iskustvom
RevenueCatSDK šalje purchase info, RC validira i prati entitlement state, aplikacija provjerava entitlementsVećinu pretplatničkih aplikacija

Ako se aplikacija oslanja i na korisničke račune, kombinirajte stanje pretplatnika s vašim auth modelom. Ako implementirate auth i notifikacije, držite okruženja čista. Pogledajte Flutter push notifikacije s FCM i APNs u produkciji.

# Implementacija Flutter kupnji unutar aplikacije s in_app_purchase#

Ovo je najniža razina u Flutteru. Kasnije možete migrirati na RevenueCat.

1) Dodajte dependencyje#

YAML
# pubspec.yaml
dependencies:
  in_app_purchase: ^3.2.0
  in_app_purchase_storekit: ^0.3.20
  in_app_purchase_android: ^0.4.0

Držite verzije usklađene s vašim Flutter stable kanalom.

2) Dohvat proizvoda (query)#

Trebate točne store product ID-eve.

Dart
import 'package:in_app_purchase/in_app_purchase.dart';
 
final iap = InAppPurchase.instance;
 
Future<List<ProductDetails>> fetchProducts() async {
  final ids = <String>{
    'com.samioda.app.pro.monthly',
    'com.samioda.app.pro.yearly',
  };
 
  final available = await iap.isAvailable();
  if (!available) return [];
 
  final response = await iap.queryProductDetails(ids);
  if (response.error != null) {
    throw Exception('IAP query error: ${response.error}');
  }
  return response.productDetails;
}

Česti uzroci praznih rezultata:

  • Pogrešni product ID-evi
  • Aplikacija instalirana izvan TestFlighta ili Play internal testinga
  • Proizvod nije u “Ready to submit” stanju, nedostaju metapodaci ili nije odobren za testiranje

3) Pokrenite kupnju#

Za non-consumables i pretplate koristite buyNonConsumable. Za consumables koristite buyConsumable i odlučite kako dodjeljujete i bilježite kredite.

Dart
Future<void> buy(ProductDetails product) async {
  final param = PurchaseParam(productDetails: product);
  await iap.buyNonConsumable(purchaseParam: param);
}

4) Slušajte purchase updateove i završite transakcije#

Purchase updateovi dolaze kao stream. Morate:

  • verificirati kupnju
  • dodijeliti pristup
  • završiti transakciju
Dart
late final StreamSubscription<List<PurchaseDetails>> sub;
 
void startListening() {
  sub = iap.purchaseStream.listen((purchases) async {
    for (final p in purchases) {
      if (p.status == PurchaseStatus.purchased ||
          p.status == PurchaseStatus.restored) {
        final ok = await verifyOnServer(p);
        if (ok) {
          await grantEntitlement(p.productID);
        }
      }
      if (p.pendingCompletePurchase) {
        await iap.completePurchase(p);
      }
    }
  });
}

verifyOnServer je teški dio. Za pretplate verifikacija nije opcionalna ako želite točnu kontrolu pristupa.

5) Restore purchases#

Korisnici očekuju da restore radi na iOS-u. Važno je i nakon reinstalacije.

Dart
Future<void> restore() async {
  await iap.restorePurchases();
}

Restore nije potpuno rješenje za pretplate osim ako dodatno verificirate trenutno stanje i obrađujete isteke, povrate i obnove.

💡 Savjet: “restore” tretirajte kao okidač za osvježavanje verificiranog stanja pretplatnika, ne kao dokaz aktivnog pristupa.

# Implementacija pretplata s RevenueCat#

RevenueCat uklanja većinu složenosti oko računa i daje čistu provjeru entitlementa.

1) Kreirajte RevenueCat projekt i povežite storeove#

U RevenueCat-u:

  • Kreirajte projekt
  • Dodajte iOS i Android aplikacije
  • Povežite App Store Connect i Google Play
  • Importajte proizvode

Ključni koncepti: Offerings i Entitlements.

2) Definirajte entitlements i mapirajte proizvode#

Primjer:

  • Entitlement: pro
  • Proizvodi: mjesečna i godišnja pretplata
  • Ako je pretplata aktivna, entitlement pro je aktivan

Ovo sprječava da logika aplikacije bude vezana uz konkretne product ID-eve. Kasnije možete mijenjati cijene i proizvode bez updatea aplikacije.

3) Dodajte dependency i inicijalizirajte#

YAML
# pubspec.yaml
dependencies:
  purchases_flutter: ^8.3.0

Inicijalizirajte rano, najčešće nakon pokretanja aplikacije i nakon što imate stabilan app user id ako koristite račune.

Dart
import 'package:purchases_flutter/purchases_flutter.dart';
 
Future<void> initRevenueCat() async {
  await Purchases.setLogLevel(LogLevel.info);
 
  await Purchases.configure(
    PurchasesConfiguration('public_sdk_key_here'),
  );
}

Ako imate autentikaciju, identificirajte korisnike kako bi kupnje “pratile” korisnika kroz uređaje.

Dart
Future<void> loginToRevenueCat(String appUserId) async {
  await Purchases.logIn(appUserId);
}

4) Dohvatite offerings i prikažite paywall#

Dart
Future<Offering?> fetchPaywall() async {
  final offerings = await Purchases.getOfferings();
  return offerings.current;
}

Vaš paywall UI treba prikazati:

  • naziv plana
  • cijenu i period
  • trial ako postoji
  • restore gumb
  • jasne uvjete i link za upravljanje pretplatom

Kad korisnik odabere package:

Dart
Future<void> purchasePackage(Package pkg) async {
  final result = await Purchases.purchasePackage(pkg);
  final proActive = result.customerInfo.entitlements.active.containsKey('pro');
  if (!proActive) throw Exception('Purchase completed but pro not active');
}

5) Provjerite entitlement bilo gdje#

Aplikacija treba “gateati” featuree na temelju entitlementa, ne na temelju lokalnog booleana.

Dart
Future<bool> hasPro() async {
  final info = await Purchases.getCustomerInfo();
  return info.entitlements.active.containsKey('pro');
}

Ovo pouzdanije pokriva obnove, otkazivanja, povrate i restore kroz uređaje nego ručne provjere purchase historyja.

# Paywallovi koji konvertiraju bez da dobijete odbijenicu#

Paywall je i proizvod i compliance. Apple i Google odbijaju nejasne cijene ili nedostajući restore i uvjete.

Praktični paywall checklist#

StavkaZašto je bitnoPrimjer
Jasna cijena i periodSprječava obmanjujući UX“€4.99 mjesečno”
Disclosure trialaObavezno ako nudite trial“7 dana besplatno, zatim €4.99 mjesečno”
Restore purchases gumbObavezno na iOS-u“Vrati kupnje”
Link za upravljanje pretplatomSmanjuje broj support upitaLink na sistemsko upravljanje pretplatama
Linkovi na uvjete i privatnostReview compliance“Uvjeti” i “Privatnost”

Držite paywall logiku jednostavnom:

  • 1 primarni CTA za preporučeni plan
  • mjesečni i godišnji izbor
  • istaknite godišnju uštedu s realnom matematikom, npr. “€49.99 godišnje je €4.17 mjesečno”

Ako trebate procijeniti budžet i timeline za dodavanje pretplata i paywallova, koristite ovaj kontekst cijena: Cijena Flutter aplikacije u 2026..

# Sandbox i postavljanje testnog okruženja#

Većina billing bugova je zapravo “ne vrti se u sandboxu”.

Apple sandbox testiranje — workflow#

  1. 1
    Kreirajte sandbox testera u App Store Connect
  2. 2
    Instalirajte aplikaciju iz TestFlighta
  3. 3
    Pokrenite purchase flow
  4. 4
    Prijavite se sa sandbox Apple ID-jem kad vas prompt traži

Očekivano ponašanje:

  • Obnove pretplata se događaju brzo u sandboxu
  • Možete vidjeti više obnova i isteka u nekoliko minuta
  • Otkazivanje i togglanje obnove radi se u iOS settings za sandbox račun

Google testiranje — workflow#

  1. 1
    Uploadajte AAB u Internal testing
  2. 2
    Dodajte sebe kao testera i license testera
  3. 3
    Instalirajte iz Play Store testing linka
  4. 4
    Pokrenite kupnje u aplikaciji

Očekivano ponašanje:

  • Purchase dialog prikazuje “test” indikacije za license testere
  • Lifecycle događaji pretplate su brži nego u produkciji

Validirajte signale da ste u testnom okruženju#

PlatformaSignal da ste u sandboxuBrza provjera
iOSSandbox login prompt i sandbox subscription managementSettings app prikazuje sandbox kontekst računa
AndroidTest purchase dialog i test card ponašanjePlay Store račun je tester i aplikacija je instalirana iz Playja

# Česti produkcijski problemi i kako ih debugirati#

Ova sekcija je razlika između “radilo je jednom” i stabilnog prihoda.

Problem 1: Proizvodi vraćaju praznu listu#

Najvjerojatniji uzroci:

  • Nepodudaranje product ID-eva
  • Aplikacija instalirana putem sideloada ili debug-a, a ne TestFlighta ili Play testinga
  • Proizvodi nisu odobreni, nedostaje lokalizacija ili nisu “Ready”
  • Država store računa se ne podudara s dostupnošću proizvoda

Koraci za debug:

  1. 1
    Logirajte queryane ID-eve i environment build flavor
  2. 2
    Potvrdite izvor instalacije i track
  3. 3
    Potvrdite stanje proizvoda i potpunost metapodataka
  4. 4
    Testirajte s novim sandbox tester ili license tester računom

Problem 2: Kupnja završi, ali entitlement nije aktivan#

Tipični uzroci:

  • Zatvorili ste kupnju (complete) prije nego je verifikacija završila
  • Pogrešno mapiranje entitlementa u RevenueCat-u
  • Provjeravate lokalni cache umjesto osvježenog customer info
  • Mrežni problemi tijekom post-purchase synca

Koraci za debug:

  1. 1
    Nakon kupnje pozovite getCustomerInfo i provjerite active entitlements
  2. 2
    U RevenueCat dashboardu pregledajte customer timeline
  3. 3
    Na iOS-u potvrdite da je korišten ispravan Apple ID
  4. 4
    Ponovite fetch na app resume i nakon kratkog delayja

Problem 3: Duple dodjele ili “nestali” consumables#

Consumables zahtijevaju idempotentnost. Ako aplikacija dvaput dodijeli kredite, curit će prihod.

Pattern za popravak:

  • Spremite transaction ID i dodijelite samo jednom
  • Ako je moguće, dodjelu napravite atomarnom na backendu

Problem 4: iOS “Ask to Buy” i iznenađenja s family sharingom#

Ako podržavate family sharing ili naletite na flowove roditeljskog odobrenja, kupnje mogu ostati pending. UI mora obraditi PurchaseStatus.pending i dati jasno stanje.

Problem 5: Google pretplate su aktivne u Playu, ali ne i u aplikaciji#

Često uzrokovano s:

  • Pogrešnim Google računom na uređaju
  • Cache problemima Play servicesa
  • Aplikacija nije potpisana istim ključem kao build uploadan na track

Koraci za debug:

  1. 1
    Provjerite signing key i package name
  2. 2
    Potvrdite da je Play račun na uređaju tester račun
  3. 3
    Clearanje Play Store cachea koristite samo kao zadnju opciju
  4. 4
    Koristite RevenueCat dashboard ili backend logove za potvrdu validation događaja

Problem 6: Webhookovi i backend se ne ažuriraju nakon obnova#

Ako koristite RevenueCat webhooks ili store server notifikacije, propušten webhook može uzrokovati zastarjeli pristup kod vas.

Ublažavanja:

  • Uvijek dopustite aplikaciji da osvježi entitlement state na launch i resume
  • Webhook processing učinite idempotentnim i otpornim na retry
  • Čuvajte timestamp “last verified at” i periodično re-provjerite

ℹ️ Napomena: Stanje pretplatnika je event-driven, ali aplikacija mora biti otporna na propuštene evente. Periodično osvježavanje entitlementa je jeftino i sprječava dugotrajno pogrešan pristup.

# Operativni checklist prije launch-a#

Koristite ovo kao last-mile listu za produkcijsku spremnost.

PodručjeProvjeraZašto
ProizvodiID-evi, cijene, lokalizacijaSprječava prazne liste proizvoda i odbijenice
PaywallRestore, uvjeti, jasne cijeneReview compliance i povjerenje
Kontrola pristupaEntitlements, ne lokalne flagoveSprječava prijevare i zastarjeli pristup
TestiranjeTestFlight i Internal testingPravo ponašanje storeova
ObservabilityLogovi za korake kupnjeBrži debugging
ReleaseAutomatizirani buildovi i verzioniranjeManje ljudskih grešaka

Ako vam je build i release pipeline krhak, billing popravci će trajati dulje i uzrokovati churn. Automatizaciju postavite rano: Flutter CI/CD s GitHub Actions, Codemagic i Fastlane.

# Ključne poruke#

  • Koristite entitlements kao sloj pristupa, ne gole product ID-eve, kako bi billing logika ostala stabilna kroz promjene cijena i proizvoda.
  • Za pretplate izbjegnite client-only logiku i koristite server-side validaciju ili RevenueCat kako biste pokrili obnove, povrate, grace periode i chargebackove.
  • IAP uvijek testirajte preko stvarnih distribucijskih kanala: TestFlight na iOS-u i Play Internal testing na Androidu — inače ćete loviti lažne bugove.
  • Purchase handling gradite idempotentno, posebno za consumables, kako biste spriječili duple dodjele i curenje prihoda.
  • Kod debugiranja “kupnja je uspjela, ali pristup nedostaje”, pregledajte customer timeline, osvježite customer info nakon kupnje i provjerite store račun i signing key-eve.

# Zaključak#

Flutter kupnje unutar aplikacije lako je demonstrirati, a iznenađujuće ih je lako “slomiti” u produkciji ako preskočite validaciju, entitlements i stvarno sandbox testiranje. Ispravno postavite proizvode u App Store Connect i Play Console, implementirajte purchase flowove s jasnim paywallovima i oslanjajte se na verificirano stanje entitlementa umjesto na lokalne flagove.

Ako želite da Samioda implementira pretplate end-to-end, uključujući RevenueCat setup, paywall UX, webhook integraciju i release automatizaciju, kontaktirajte nas i definirat ćemo plan isporuke spreman za produkciju koji odgovara vašem timelineu i budžetu.

FAQ

Share
A
Adrijan OmićevićOsnivač i senior developer

Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.

Trebate pomoć s projektom?

Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.