Ionic aplikacije razvijam od 2015. godine i sve do prije nešto više od godinu dana temeljio sam ih primarno na Cordovi.
Usput sam pratio razvoj Capacitora koji je sa verzijama 3 i 4 počeo “dobivati oblik” s kojim sam bio zadovoljan. Tada sam zaključio da može poslužiti kao temelj novih, ali i postojećih projekata.
Cordova pripada prošlosti
Ako se pogleda samo činjenica da je Cordova nastala 2009., a Capacitor 2018. više je nego jasno da među njima vrlo vjerovatno postoje dosta velike razlike. Bile one konceptualne ili praktične ipak su razlike.
Također, kroz godine se povećavao naglasak na sigurnosti mobilnih aplikacija pa tako Google i Apple više neće pustiti bilo što u svoje trgovine aplikacijama. Pri tome održavanje postojećih, a pogotovo razvoj novih aplikacija temeljenih na Cordovi gubi svoj smisao.
Capacitor nije zamjena za Cordovu
Ovo je i najveća zabluda developera koji su toliko navikli na Cordovu da Capacitoru ne žele dati niti šansu jer misle da sada moraju sve mijenjati i raditi ispočetka.
Capacitor je kompatibilan sa Cordovom tako da niti migracija nije veliki problem i moguće ju je raditi postupno. Ako i dođete u slučaj da ipak trebate neki stariji Cordova plugin moći ćete ga dodati u postojeći Ionic Capacitor projekt.
Iako Capacitor i Cordova mogu raditi zajedno osobno mi je cilj u svim projektima postupno skroz napustiti Cordovu.
Prednosti Capacitora
Najveća prednost je skroz druga filozofija razvoja aplikacija, koja je više prilagođena današnjem tehnološkim potrebama. Ali da ne bi sve ostalo na filozofiranju evo i konkretnih prednosti koje mi se osobno sviđaju.
Brži i jednostavniji build/deploy aplikacija
Postavljanje na Google Play Store i Apple App Store nešto je čime završi svaki novi ciklus razvoja nove ili održavanja postojeće aplikacije. To je proces koji se ponavlja i kao takav je važno da bude što jednostavniji.
Capacitor omogućava brže i jednostavnije kreiranje produkcijskog builda. Kod Android aplikacija sve ide putem Android Studia. Kod iOS-a mi je isto kao i do sada, s tom razlikom da više nema problema s nekompatibilnosti verzija nekih pluginova.
Manja ovisnost o pluginovima
Capacitor donosi nativne funkcionalnosti, kao npr. Deep Link, za koje više nisu potrebni pluginovi što je bio slučaj sa Cordovom. Samim time manje toga je potrebno posebno ažurirati i usklađivati po pitanju verzija.
Svaki plugin manje donosi aplikaciju koju je lakše održavati.
Jednostavna migracija
Općenito kada se prelazi sa tehnologije na tehnologiju u dosta slučajeva su u pitanju toliko velike promjene da je jednostavnije sve raditi ispočetka. To nije slučaj kod Capacitora.
Iako donosi značajne promjene, Capacitor u prvoj fazi migracije može poslužiti samo kao temelj. Čak i kada se ostave postojeći Cordova pluginovi. Kada se savlada taj korak, onda lagano migrirati ili uklanjati plugin po plugin. Kod pluginova koji nisu kompatibilni sa Capacitorom potrebno je odmah planirati adekvatnu zamjenu.
Izrada nove aplikacije
Kada se kreira nova aplikacija u startu je sasvim lijepo vidjeti da više ne postoji ovisnost o config.xml-u. Platformske postavke su sada ondje gdje i trebaju biti – u datotekama pojedine platforme. Za Android je to AndroidManifest.xml, a za iOS Info.plist.
To je super. Jer koliko god se prije pisalo “programiraš jednom za sve platforme” u praksi to često nije bio slučaju kada su u pitanju razlike između iOS-a i Androida.
Zaključak
Iz svega navedenog meni je Capacitor jedini logičan izbor i jedva čekam vidjeti što će novog donijeti.
Iako u testnom okruženju, plaćanje kreirano na ovaj način biti će uspješno provedeno što će se moći vidjeti unutar PayPal testnog sučelja i prikazano u nastavku ovog blog posta.
Ipak, kako je vidljivo u službenoj dokumentaciji potrebno je napraviti i serverski tj. backend dio koji će služiti kao potvrda uspješnog plaćanja. Serverski dio u ovom blog postu neće biti detaljnije obrađen.
Smart Payment Buttons funkcionira na sljedeći način:
Gumb za plaćanje prikazan je na web/mobilnoj aplikaciji
Kupac klikne na gumb
Poziva se PayPal Orders API koji priprema transakciju
Prikazuje se PayPal Checkout forma za prijavu/plaćanje
Kupac odobrava plaćanje
Poziva se PayPal Orders API koji izvršava transakciju
Kupcu se prikazuje poruka o uspješnoj transakciji
PayPal Sandbox & App Name
Kako bi mogao testirati PayPal naplatu potrebni su mi: testni PayPal korisnički račun na koji će sredstva biti uplaćena, testni PayPal korisnički račun koji će izvršavati plaćanje kao i profil aplikacije putem koje će se plaćanje izvršavati.
Moguće je kreirati testni račun fizičke ili pravne osobe tj. tvrtke. U ovom ću primjeru kreirati poslovni PayPal račun.
Valuta tog računa biti će u američkim dolarima (USD), a plaćanje ću kasnije izvršavati u dolarima i eurima (EUR). PayPal automatski radi konverziju valuta.
Klikom na „Create Account“ kreiram Sandbox PayPal račun fortuno@example.com.
Kao što se može vidjeti, ovdje mi je potreban ranije kreiran Client ID. Ako drugačije ne navedem zadana valuta biti će USD. U jednom od primjera kao valutu ću dodati EUR, parametar
currency.
<p>Thank you for your payment. Your transaction has been completed, and a receipt for your purchase has been emailed to you. Log into your PayPal account to view transaction details.</p>
</ion-card>
</ion-col>
</ion-row>
</ion-content>
Koristim ngIf direktivu kako bi nakon uspješnog plaćanja umjesto gumba za plaćanje prikazao odgovarajuću poruku.
S obzirom da je u pitanju demo aplikacija na ekranu se neće nalaziti ništa drugo osim gumba za plaćanje na vrhu ekrana. U suprotnom bi se više moralo razmisliti o optimalnom mjestu za postavljanje gumba. Više o tome na poveznici https://developer.paypal.com/docs/checkout/best-practices/feature-paypal/#
Inicijalizacija transakcije vrši se klikom na gumb “PayPal” tj. funkcijom
createOrder unutar koje ću proslijediti samo iznos koji želim na/platiti.
JavaScript
1
2
3
4
5
6
7
8
9
createOrder:function(data,actions){
returnactions.order.create({
purchase_units:[{
amount:{
value:_this.paymentAmount
}
}]
});
},
Kada kupac odobri transakciju poziva se funkcija
onApprove.
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
onApprove:function(data,actions){
returnactions.order.capture()
.then(function(details){
console.log(details);
if(details.status=="COMPLETED"){
// Show a success message to the buyer
//alert('Transaction completed by ' + details.payer.name.given_name + '!');
_this.placanjeUspjesno=true;
}else{
alert("Neuspješno plaćanje!");
}
})
.catch(err=>{
console.log(err);
})
}
Kompletna funkcionalnost izgleda ovako:
home.page.ts
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import{Component}from'@angular/core';
declarevar window:any;
@Component({
selector:'app-home',
templateUrl:'home.page.html',
styleUrls:['home.page.scss'],
})
exportclassHomePage{
paymentAmount:string="10";
placanjeUspjesno:boolean=false;
constructor(){
this.paymentOptions();
}
paymentOptions(){
let _this=this;
setTimeout(()=>{
// Render the PayPal button into #paypal-button-container
window.paypal.Buttons({
// Set up the transaction
createOrder:function(data,actions){
returnactions.order.create({
purchase_units:[{
amount:{
value:_this.paymentAmount
}
}]
});
},
// Finalize the transaction
onApprove:function(data,actions){
returnactions.order.capture()
.then(function(details){
console.log(details);
if(details.status=="COMPLETED"){
// Show a success message to the buyer
//alert('Transaction completed by ' + details.payer.name.given_name + '!');
_this.placanjeUspjesno=true;
}else{
alert("Neuspješno plaćanje!");
}
})
.catch(err=>{
console.log(err);
})
}
}).render('#paypal-button-container');
},500)
}
}
Demo prikaz
Sada ću proći kroz čitav proces naplate i prikazati ga slikama.
Klikom na žuti gumb „PayPal“ pokrećem postupak plaćanja. Prijavljujem se pomoću PayPal Sandbox računa.
Biram na koji način želim platiti navedeni iznos. To mogu učiniti sa svojeg ukupnog balansa, kreditnom ili debitnom karticom koje su povezane s PayPal računom.
Klikom na “Pay Now” potvrđujem plaćanje. Plaćanje je uspješno izvršeno ako dobijem (Order) ID. U ovom slučaju to je
"id":"99D86747XH516732E".
PayPal će mi vratiti sljedeći odgovor, u kojemu imam sve potrebno kako bi kasnije mogao raditi provjeri plaćanja, nakon što je plaćanje uspješno prošlo:
Osim toga, potvrdu o uspješnom plaćanju mogu vidjeti ako se prijavim unutar PayPal Sandbox računa na adresi https://www.sandbox.paypal.com/ sa korisničkim imenom tomislavstankovic1-buyer@gmail.com i pripadajućom lozinkom.
Ovdje mogu vidjeti koliki mi je trenutni tj. ukupni dostupan iznos na PayPal Sandbox računu kao i popis svih transakcija.
Klikom na „Informatika Fortuno’s Test Store“ mogu vidjeti detalje navedene transakcije.
Zaključak
Što se klijentskog dijela aplikacije tiče to bi bilo sve.
Ostaje još napraviti serverski, backend, dio gdje će se vršiti provjera plaćanja. Na taj API šaljem ranije spomenuti ID"id":"99D86747XH516732E".
U ovom koraku trebam postaviti temelj tj. na
HomePage komponenti gdje ću kreirati dva gumba kako bi mogao prikazati dva načina za prosljeđivanje parametara.
Sada napokon mogu pokazati kako proslijediti podatke tj. parametre sa
HomePage na
DetailsPage na dva načina.
Prosljeđenim ću parametrima pristupiti kroz ActivatedRoute.
Query Params
Ovaj način je najčešći s kojim sam se susretao i ok je ako se radi mobilna aplikacija kojoj će se pristupati kroz npr. Google Play Store jer se u tom slučaju neće vidjeti URL svake od stranica.
Kako bi ovo funkcioniralo potrebno je koristiti Angular 7.2 ili noviju verziju.
State passed to any navigation. This value will be accessible through the extras object returned from router.getCurrentNavigation() while a navigation is executing. Once a navigation completes, this value will be written to history.state when the location.go or location.replaceState method is called before activating of this route. Note that history.state will not pass an object equality test because the navigationId will be added to the state before being written.
While history.state can accept any type of value, because the router adds the navigationId on each navigation, the state must always be an object. – NavigationExtras
Ovo rješenje je slično onome iznad s tom razlikom što izgleda ljepše kada je vidljiv URL jer se parametri ne vide. To je posebno korisno kada se radi Ionic Progressive Web App.
Primjer iz tog blog posta uredno radi u razvojnoj verziji mobilne aplikacije tj. prilikom testiranja, ali kada se aplikacija jednom stavi u produkciju tj. na Google Play Store može se dogoditi da login/prijava više ne radi bez nekog očitog razloga i jedina greška koju je moguće vidjeti je:
Invalid key hash. The key hash **************************** does not match any stored key hashes. Configure your app key hashes at https://developers.facebook.com/apps/appid
facebook for developers
Rješenje je u biti vrlo jednostavno što se zaključiti prema zadnjoj rečenici poruke o grešci. Potrebno je otići na https://developers.facebook.com/ tj. na adresu profila aplikacije https://developers.facebook.com/apps/appid/ te pod Settings – Basic – Android – Key Hashes kopirati hash key iz poruke o grešci.
Nakon ovoga će login/prijava ponovno raditi bez ikakvih problema.
Do sada sam u blog postovima pisao o Ionic aplikacijama koje svoju budućnost temelje na objavi na nekoj od trgovina aplikacijama u .apk ili .ipa obliku.
Ovaj će blog post ići u sasvim jednom drugom smjeru jer u potpunosti mogu zaobići trgovine aplikacijama i svoju PWA aplikaciju objaviti jednako kao što bi objavio bilo koju web stranicu.
Što je, Ionic, PWA?
Ionic 4 PWA je zapravo obična Ionic 4 aplikacija prilagođena prikazu na svim platformama bez potrebe za objavom putem Google Play Storea, Apple App Storea i sl.
Kreiranje projekta
Projekt kreiram već poznatom naredbom:
Shell
1
2
$ionic start Ionic4PWA blank
cdIonic4PWA
U ovom koraku ovo je tek obična Ionic 4 aplikacija. Za sada neću instalirati Ionic Native pluginove jer mi za ovaj primjer neće biti potrebni.
– kreirana je konfiguracijska datotekaService Workerangsw-config.json. Trenutno se ovdje nalazi zadani naziv aplikacije
"name":"app" koji mogu promijeniti u
"name":"ionic4pwa".
– ažurirana je app.module.ts datoteka koja služi za registraciju Service Workera. Ovdje se spominje ngsw-worker.js koja trenutno ne postoji unutar Ionic projekta, ali će se kreirati pokretanjem
$ionic build--prod naredbe.
– ažurirana je index.html datoteka tj.
<head> tag unutar kojega se spominje manifest.json datoteka. Ovdje također mogu promijeniti
"name":"app","short_name":"app", u
"name":"Ionic4PWA","short_name":"Ionic4PWA",.
manifest.json
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name":"app",
"short_name":"app",
"theme_color":"#1976d2",
"background_color":"#fafafa",
"display":"standalone",
"scope":"/",
"start_url":"/",
"icons":[
{
"src":"assets/icons/icon-72x72.png",
"sizes":"72x72",
"type":"image/png"
},
...
– kreirane su ikone za različite platforme (zadane Angular ikone koje je za produkcijski projekt potrebno promijeniti)
Produkcijska PWA aplikacija
Produkcijska PWA aplikacija kreira se naredbom:
Shell
1
$ionic build--prod
Pokretanjem ove naredbe kreirat će se www mapa sa produkcijskom verzijom aplikacije spremnom za postavljanje na server.
Ovu PWA aplikaciju mogu pokrenuti i testirati naredbom: