PayPal Payment Integration using Braintree in Ionic 5 apps
Tags: ionic, paypal, braintree, payment
Published: 2021-08-23 18:12:46

<main role="main"><article class=" u-minHeight100vhOffset65 u-overflowHidden postArticle postArticle--full is-supplementalPostContentLoaded is-withAccentColors" lang="en" ><div class="postArticle-content js-postField js-notesSource editable" id="editor_7" g_editable="true" role="textbox" contenteditable="false" data-default-value="Title
Tell your story…"><section name="296f" class="section section--body section--first"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><p name="a2aa" class="graf graf--p graf-after--figure">In this tutorial, we will go through PayPal payment using Braintree in Ionic 5. Previously Paypal supported the <a href="https://ionicframework.com/docs/native/paypal"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Cordova PayPal Plugin</a>, which is no more maintained by PayPal, hence it has issues while making payment using PayPal Login. PayPal now recommends using Braintree for mobile payments, while web payment still works fine with the generic PayPal JS script. You can read more about the web integration in our blog —&hairsp;<a href="https://enappd.com/blog/paypal-integration-in-ionic-apps-and-pwa/142/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">PayPal integration in Ionic PWA</a>.</p><blockquote name="40ae" class="graf graf--blockquote graf-after--p">For ease of development, it is recommended to have same payment method i.e. Braintree for both mobile and web, instead of PayPal for web and Braintree for mobile.</blockquote><p name="3d8b" class="graf graf--p graf-after--blockquote">This type of integration requires a Backend (server) to generate several tokens and final transaction is also done by the server. For this tutorial, we will be using a Node JS server and will be calling APIs from Ionic app using HTTP client.</p><p name="8703" class="graf graf--p graf-after--p">We will follow below steps to implement PayPal integration using Braintree.</p><h3 name="36b4" class="graf graf--h3 graf-after--p">Steps to Implement PayPal Integration Using Braintree&nbsp;:-</h3><ol class="postList"><li name="5fcc" class="graf graf--li graf-after--h3">Create Braintree sandbox account and get the required keys</li><li name="67e4" class="graf graf--li graf-after--li">Setup Ionic 5 project</li><li name="28c9" class="graf graf--li graf-after--li">Setup Node JS project</li><li name="c3bf" class="graf graf--li graf-after--li">Creating Node JS API’s for Braintree</li><li name="bde3" class="graf graf--li graf-after--li">Braintree/PayPal integration in Ionic app using Node JS API’s</li><li name="dbeb" class="graf graf--li graf-after--li">Testing</li></ol><p name="eec2" class="graf graf--p graf-after--li">Before we proceed, a little bit about Ionic and Braintree</p><h3 name="58e7" class="graf graf--h3 graf-after--p">What is Braintree&nbsp;?</h3><p name="9cab" class="graf graf--p graf-after--h3">Braintree offers a variety of products that make it easy for users to accept payments in their apps or website. Think of Braintree as the credit card terminal you swipe your card through at the grocery store. Braintree supports following major types of payment methods</p><ul class="postList"><li name="c3b1" class="graf graf--li graf-after--p">ACH Direct Debit</li><li name="b53b" class="graf graf--li graf-after--li">Apple Pay</li><li name="5268" class="graf graf--li graf-after--li">Google Pay</li><li name="d80f" class="graf graf--li graf-after--li">PayPal</li><li name="c129" class="graf graf--li graf-after--li">Samsung Pay</li><li name="7134" class="graf graf--li graf-after--li">Credit / Debit cards</li><li name="3c75" class="graf graf--li graf-after--li">Union Pay</li><li name="b9cb" class="graf graf--li graf-after--li">Venmo</li></ul><h3 name="6591" class="graf graf--h3 graf-after--li">What is Ionic&nbsp;?</h3><p name="d89f" class="graf graf--p graf-after--h3">Ionic framework is a mobile app framework, which supports other front-end frameworks like Angular, React, Vue and Vanilla JS, and builds apps for both Android and iOS from same code.</p><p name="43e6" class="graf graf--p graf-after--p">If you create Native apps in Android, you code in Java. If you create Native apps in iOS, you code in Obj-C or Swift. Both of these are powerful but complex languages. <strong class="markup--strong markup--p-strong">With Ionic you can write a single piece of code for your app that can run on both iOS and Android </strong>and web (as PWA), that too with the simplicity of HTML, CSS, and JS.</p><p name="2b1a" class="graf graf--p graf-after--p">It is important to note the contribution of Cordova/Capacitor in this. Ionic is only a UI wrapper made up of HTML, CSS and JS. So, by default, Ionic cannot run as an app in an iOS or Android device. Cordova/Capacitor is the build environment that containerizes (sort of) this Ionic web app and converts it into a device installable app, along with providing this app access to native APIs like Camera, web access etc.</p><h3 name="82e2" class="graf graf--h3 graf-after--p">Ionic and Payment&nbsp;Gateways</h3><p name="b85f" class="graf graf--p graf-after--h3">Ionic can create a wide variety of apps, and hence a wide variety of payment gateways can be implemented in Ionic apps. The popular ones are PayPal, Stripe, Braintree, in-app purchase etc. For more details on payment gateways, you can read our blogs on <a href="https://medium.com/enappd/payment-solutions-in-ionic-8c4bb28ce5cc"  class="markup--anchor markup--p-anchor"    target="_blank">Payment Gateway Solutions in Ionic</a>, <a href="https://enappd.com/blog/ionic-5-stripe-payment-integration-firebase-cloud-functions-vs-node-express-based-server/159"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Stripe integration in Ionic</a>, <a href="https://enappd.com/blog/how-to-integrate-apple-pay-in-ionic-4-apps/21"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Apple pay in Ionic</a> etc.</p><p name="b8c2" class="graf graf--p graf-after--p">Also, there are different types of Ionic Apps you can build for same functionality. Most popular ones are&nbsp;:</p><ol class="postList"><li name="bee1" class="graf graf--li graf-after--p"><strong class="markup--strong markup--li-strong">Front-end: Angular | Build environment&nbsp;: Cordova</strong></li><li name="4677" class="graf graf--li graf-after--li">Front-end: Angular | Build environment&nbsp;: Capacitor <strong class="markup--strong markup--li-strong">✅</strong></li><li name="7c35" class="graf graf--li graf-after--li">Front-end: React | Build environment&nbsp;: Capacitor</li><li name="c4d1" class="graf graf--li graf-after--li">Front-end: Vue | Build environment&nbsp;: Capacitor</li></ol><p name="9fc6" class="graf graf--p graf-after--li">PayPal can be integrated in websites as well as mobile apps. In this blog we’ll learn <strong class="markup--strong markup--p-strong">how to integrate PayPal payment gateway in Angular/Capacitor Ionic 5 apps using Braintree.</strong></p><p name="8c2f" class="graf graf--p graf-after--p">OK, enough of talking, let’s start building our PayPal Braintree Payment System.</p><h3 name="045e" class="graf graf--h3 graf-after--figure"><strong class="markup--strong markup--h3-strong">Step 1&nbsp;:- Create Braintree sandbox account and get the required&nbsp;keys</strong></h3><p name="59a9" class="graf graf--p graf-after--h3">This steps is required to proceed further, we will be needing several keys to integrate PayPal in Ionic 5 app as well as in Node JS script. To get these keys go to <a href="https://sandbox.braintreegateway.com/login"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Braintree Sandbox Account</a>. Once you login, you can go to settings and select API.</p><figure tabindex="0" contenteditable="false" name="a86c" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 175px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 24.9%;"></div><img class="graf-image" data-image-id="1*4Z_gnQyxm4qzSa1fszFVfA.png" data-width="1363" data-height="340" src="https://cdn-images-1.medium.com/max/800/1*4Z_gnQyxm4qzSa1fszFVfA.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Braintree Sandbox settings to get API&nbsp;keys</figcaption><br></figure><p name="e115" class="graf graf--p graf-after--figure">In API section, scroll down and you will get the Required API key. We need three keys to initialize Braintree in Node JS scripts</p><ol class="postList"><li name="dd59" class="graf graf--li graf-after--p">Merchant Id</li><li name="6e93" class="graf graf--li graf-after--li">Public Key and</li><li name="2772" class="graf graf--li graf-after--li">Private Key</li></ol><figure tabindex="0" contenteditable="false" name="3944" class="graf graf--figure graf-after--li"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 135px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 19.3%;"></div><img class="graf-image" data-image-id="1*QSv8L8ofyjBN6l-voJ3WMQ.png" data-width="1282" data-height="248" src="https://cdn-images-1.medium.com/max/800/1*QSv8L8ofyjBN6l-voJ3WMQ.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Public Key and Private Key in Braintree settings</figcaption><br></figure><figure tabindex="0" contenteditable="false" name="7903" class="graf graf--figure graf-after--figure"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 230px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 32.800000000000004%;"></div><img class="graf-image" data-image-id="1*c3hzrZdf94LcBV4CRPSYsA.png" data-width="1353" data-height="444" src="https://cdn-images-1.medium.com/max/800/1*c3hzrZdf94LcBV4CRPSYsA.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Braintree Merchat&nbsp;ID</figcaption><br></figure><p name="15de" class="graf graf--p graf-after--figure">Save these IDs, we will use them in initializing the Braintree on server-side.</p><h3 name="5c31" class="graf graf--h3 graf-after--p">Step 2&hairsp;—&hairsp;<strong class="markup--strong markup--h3-strong">Setup Ionic 5&nbsp;project</strong></h3><p name="42a4" class="graf graf--p graf-after--h3">In this part, we will create and configure Ionic 5 Project and also setup new Node JS Script.</p><h4 name="98b5" class="graf graf--h4 graf-after--p">Creating and configuring the Ionic 5 application.</h4><p name="7c58" class="graf graf--p graf-after--h4">To create new Ionic 5 project, run the below command&nbsp;:-</p><pre name="cd20" class="graf graf--pre graf-after--p">$ ionic start PayPal --blank --type=angular</pre><p name="7543" class="graf graf--p graf-after--pre">This will create blank app in the working directory and if you want to know more about creating the Ionic 5 project, go to <a href="https://enappd.com/blog/how-to-create-an-ionic-app-for-beginners/144/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic 5 Beginners Blog</a>. Now once we have created the Ionic app, we can configure the app for Braintree, to do that flow the below steps.</p><p name="1076" class="graf graf--p graf-after--p"><strong class="markup--strong markup--p-strong">a) Add CDN to </strong><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">index.html</strong></code><strong class="markup--strong markup--p-strong"> file</strong><br>How will Ionic app know we want to use Braintree&nbsp;? Currently there is no Cordova or Capacitor plugin for Braintree. Hence we will add the CDN to parent file i.e. index.html, below is the CDN for Braintree JavaScript SDK.</p><pre name="e725" class="graf graf--pre graf-after--p">&lt;script src="https://js.braintreegateway.com/js/braintree-2.32.1.min.js"&gt;&lt;/script&gt;</pre><p name="b16c" class="graf graf--p graf-after--pre">By adding this SDK CDN, Braintree will be available throughout the application. We can simply use it in any of the pages in our Ionic app.</p><p name="ef1e" class="graf graf--p graf-after--p"><strong class="markup--strong markup--p-strong">b) Using Braintree variable in Ionic 5 application<br></strong>Once we import the SDK CDN, we can use the Braintree variable in any page by using below defined syntax</p><pre name="2c13" class="graf graf--pre graf-after--p">declare const braintree;</pre><p name="51b9" class="graf graf--p graf-after--pre">Now later in Payment-gateway page, we will use this variable to setup the Client side application for successful payment.</p><h3 name="d72e" class="graf graf--h3 graf-after--p">Step 3&hairsp;—&hairsp;Setup Node JS&nbsp;project</h3><p name="ec68" class="graf graf--p graf-after--h3">Create a separate folder for node server. To create Node JS script, we will run<strong class="markup--strong markup--p-strong"> </strong><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">npm init</strong></code> in the working directory&nbsp;.</p><pre name="8885" class="graf graf--pre graf-after--p">$ npm init</pre><p name="6345" class="graf graf--p graf-after--pre">Above command will ask few basic questions and creates the <code class="markup--code markup--p-code">package.json</code> file in working directory. Now you can create the <code class="markup--code markup--p-code">index.js</code> file (All logic will be contained in index file because we’re making a simple server).</p><p name="39a2" class="graf graf--p graf-after--p">We have to install some of the libraries that will help in implementing node script. To install the libraries run the below command&nbsp;:-</p><pre name="a596" class="graf graf--pre graf-after--p">$ npm install <em class="markup--em markup--pre-em">cors express braintree body-parser</em></pre><p name="1c12" class="graf graf--p graf-after--pre">To know more about <a href="https://www.npmjs.com/package/cors"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><em class="markup--em markup--p-em">cors</em></a>, <a href="https://www.npmjs.com/package/express"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><em class="markup--em markup--p-em">express</em></a>, <a href="https://www.npmjs.com/package/body-parser"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><em class="markup--em markup--p-em">body-parser</em></a> and <a href="https://www.npmjs.com/package/braintree"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank"><em class="markup--em markup--p-em">braintree</em></a><em class="markup--em markup--p-em"> </em>you can follow the links. You can also check official <a href="https://ionicframework.com/docs/troubleshooting/cors"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic documentation on CORS</a> which we believe is very good for understanding.</p><figure tabindex="0" contenteditable="false" name="33b1" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 291px; max-height: 143px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 49.1%;"></div><img class="graf-image" data-image-id="1*1-_17l8nH7ybzjl8BD7RxA.jpeg" data-width="291" data-height="143" alt="Project Structure" src="https://cdn-images-1.medium.com/max/800/1*1-_17l8nH7ybzjl8BD7RxA.jpeg"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Project Structure</figcaption><br></figure><p name="bb14" class="graf graf--p graf-after--figure">Now we have all the basic requirements to start our node script.</p><p name="9f84" class="graf graf--p graf-after--p"><strong class="markup--strong markup--p-strong">Note</strong>&hairsp;—&hairsp;Check your project’s <code class="markup--code markup--p-code">package.json</code> file, it should contain the value stated below in scripts section. If it doesn’t, just add manually&nbsp;:-</p><figure tabindex="0" contenteditable="false" name="c5e4" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 624px; max-height: 100px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 16%;"></div><img class="graf-image" data-image-id="1*aueX2NUPIkFc-V1LCwNi7w.png" data-width="624" data-height="100" alt="package.json file" src="https://cdn-images-1.medium.com/max/800/1*aueX2NUPIkFc-V1LCwNi7w.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">package.json file for node&nbsp;server</figcaption><br></figure><h3 name="73fa" class="graf graf--h3 graf-after--figure">Step 4&hairsp;—&hairsp;<strong class="markup--strong markup--h3-strong">Creating Node JS APIs for Braintree</strong></h3><p name="3932" class="graf graf--p graf-after--h3">Creating Braintree APIs will allow us to get the <strong class="markup--strong markup--p-strong">Client ID</strong> requested from client side. We will use Braintree package to implement those APIs. First of all, import the Braintree using below command in <code class="markup--code markup--p-code">index.js</code> file</p><pre name="ad23" class="graf graf--pre graf-after--p"><em class="markup--em markup--pre-em">const</em> braintree = require("braintree");</pre><p name="e039" class="graf graf--p graf-after--pre">Now using Braintree object to initialize the Braintree Gateway. This initialization requires those keys (which we have saved in step 2) and environment value (i.e. for now we are using <strong class="markup--strong markup--p-strong"><em class="markup--em markup--p-em">Sandbox</em></strong>, later you can use production value). Use the below command to initialize the Gateway.</p><pre name="6121" class="graf graf--pre graf-after--p"><em class="markup--em markup--pre-em">const</em> gateway = new braintree.BraintreeGateway({<br>  environment: braintree.Environment.Sandbox,<br>  merchantId: "USE_YOUR_MERCHENT_ID",<br>  publicKey: "USE_YOUR_PUBLIC_KEY",<br>  privateKey: "USE_YOUR_PRIVATE_KEY"<br>});</pre><p name="7094" class="graf graf--p graf-after--pre">This Gateway object will be used to get the <strong class="markup--strong markup--p-strong">Client ID</strong> and used in doing transaction.</p><h4 name="2869" class="graf graf--h4 graf-after--p">Payment Flow with Braintree</h4><p name="6e27" class="graf graf--p graf-after--h4">The following flowchart will get you up to speed with the payment flow. With this in mind, you’ll be able to better follow the code steps given further</p><figure tabindex="0" contenteditable="false" name="052b" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 573px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 81.89999999999999%;"></div><img class="graf-image" data-image-id="1*kAd0KmHKlhYfMVmYrAxHPg.png" data-width="1221" data-height="1000" src="https://cdn-images-1.medium.com/max/800/1*kAd0KmHKlhYfMVmYrAxHPg.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Braintree Payment flow with Ionic app and node&nbsp;server</figcaption><br></figure><p name="8071" class="graf graf--p graf-after--figure">First our Ionic application will call the Node JS API that will return the CLIENT ID generated using API mentioned below&nbsp;:-</p><pre name="1bdb" class="graf graf--pre graf-after--p">app.get("/brainTreeClientToken", (<em class="markup--em markup--pre-em">req</em>, <em class="markup--em markup--pre-em">res</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>  gateway.clientToken.generate({}).then((<em class="markup--em markup--pre-em">response</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>  <em class="markup--em markup--pre-em">console</em>.log('Token', response);<br>  res.send(response);<br> });<br>});</pre><p name="3900" class="graf graf--p graf--startsWithSingleQuote graf-after--pre"><strong class="markup--strong markup--p-strong">‘/brainTreeClientToken’</strong> API will be called from Ionic app using <strong class="markup--strong markup--p-strong">HTTP Client</strong> (later we will see in step 4). Once client side (Ionic app-side) gets the token it will setup the Braintree using setup call (will see in step 4). After setup, client will initiate payment using Pay with PayPal button. Clicking PayPal button will open In App Browser and sandbox will create a random user and provide info containing nonce (token string used in making transaction successful).</p><p name="aefd" class="graf graf--p graf-after--p">For making transaction, we will create Node JS API that will checkout payment using amount and nonce. Below is the transaction API&nbsp;:-</p><pre name="1144" class="graf graf--pre graf-after--p">app.post("/checkoutWithPayment", jsonParser, (<em class="markup--em markup--pre-em">req</em>, <em class="markup--em markup--pre-em">res</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>  <em class="markup--em markup--pre-em">const</em> nonceFromTheClient = req.body.nonceFromTheClient;<br>  <em class="markup--em markup--pre-em">const</em> payment = req.body.paymentAmount;<br>  gateway.transaction.sale({<br>    amount: payment,<br>    paymentMethodNonce: nonceFromTheClient,<br>    options: {<br>      submitForSettlement: true<br>    }<br>  }).then((<em class="markup--em markup--pre-em">result</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>      res.send(result);<br>  });<br>});</pre><p name="e958" class="graf graf--p graf-after--pre">In above API <strong class="markup--strong markup--p-strong">nonceFromTheClient </strong>and <strong class="markup--strong markup--p-strong">paymentAmount </strong>will be passed through Ionic 5 app and using gateway object will make a transaction. This will return a transaction status containing complete info about customer, payment and also contains status of payment. This will complete the flow of payment using PayPal. Below is the complete script code&nbsp;:-</p><figure tabindex="0" contenteditable="false" name="7206" class="graf graf--figure graf--iframe graf-after--p is-defaultValue"><div class="aspectRatioPlaceholder is-locked"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 154.714%;"></div><div class="iframeContainer">{% gist https://gist.github.com/vaibhavgehani/03be0e85a99d510b88f765a798dbba93.js %}</div></div></figure><p name="7da2" class="graf graf--p graf-after--figure"><strong class="markup--strong markup--p-strong">Note</strong></p><ul class="postList"><li name="7342" class="graf graf--li graf-after--p">In the above script, you can replace <code class="markup--code markup--li-code">route</code> in the <code class="markup--code markup--li-code">app.listen</code> with the port <code class="markup--code markup--li-code">3000</code>&nbsp;. So it becomes <code class="markup--code markup--li-code">app.listen(3000,()=&gt;...</code></li><li name="5e8a" class="graf graf--li graf-after--li">You can also create&nbsp;<code class="markup--code markup--li-code">.catch</code> for the&nbsp;<code class="markup--code markup--li-code">.generate</code> and&nbsp;<code class="markup--code markup--li-code">.sale</code> methods in the server. This way, if you get any error from Braintree server, you will be able to see that in node server terminal</li></ul><p name="4392" class="graf graf--p graf-after--li">Run the server using <code class="markup--code markup--p-code">npm start</code> or if you want to deploy code to some cloud server you can do that as well. If you’re running the server in local system, the APIs will have a <code class="markup--code markup--p-code">localhost</code> URL. You can test web payments directly in local system. For phone payments you might have to deploy the node server on a cloud.</p><h3 name="1229" class="graf graf--h3 graf-after--p">Step 5&hairsp;—&hairsp;<strong class="markup--strong markup--h3-strong">Braintree integration in Ionic App using Node JS&nbsp;APIs</strong></h3><p name="5abb" class="graf graf--p graf-after--h3">In this part we will go through client side (Ionic app) integration, using which we can make the PayPal transaction. We have seen how to initialize Braintree in Ionic app (stated in step 2).</p><pre name="0459" class="graf graf--pre graf-after--p">declare const braintree</pre><p name="cd1f" class="graf graf--p graf-after--pre">Following method can be used to retrieve client ID from server. This method will be called during Braintree setup.</p><pre name="c60b" class="graf graf--pre graf-after--p">async getClientTokenForPaypal() {<br> return await<br>this.http.get('http://localhost:3000/brainTreeClientToken').toPromise();<br>}</pre><p name="6718" class="graf graf--p graf-after--pre">Now we can setup Braintree using below code. <strong class="markup--strong markup--p-strong">Notice that the&nbsp;</strong><code class="markup--code markup--p-code"><strong class="markup--strong markup--p-strong">.setup</strong></code><strong class="markup--strong markup--p-strong"> is the function which defines for the first time that we are going to use PayPal as the payment gateway, using Braintree as the provider.</strong></p><blockquote name="c64c" class="graf graf--blockquote graf-after--p">Keep in mind this setup will require the CLIENT_ID that will be generated by server API (mentioned in Step 3). So first we will call the server API and then pass the CLIENT_ID to setup method. You can call this function when page loads ( i.e. in ngOnInit() or ionViewWillEnter() methods).</blockquote><pre name="0dff" class="graf graf--pre graf-after--blockquote">initalizeBrainTree() {<br>  <em class="markup--em markup--pre-em">const</em> that = this;<br>  this.getClientTokenForPaypal().then((<em class="markup--em markup--pre-em">res</em>: <em class="markup--em markup--pre-em">any</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>    <em class="markup--em markup--pre-em">let</em> checkout;<br>    braintree.setup(res.clientToken, 'custom', {<br>      paypal: {<br>        container: 'paypal-container',<br>      },<br>      onReady: <em class="markup--em markup--pre-em">function</em> (<em class="markup--em markup--pre-em">integration</em>) {<br>        checkout = integration;<br>      },<br>      onCancelled: (<em class="markup--em markup--pre-em">obj</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>        <em class="markup--em markup--pre-em">console</em>.log('Cancelled', obj);<br>        checkout.teardown(() <em class="markup--em markup--pre-em">=&gt;</em> { checkout = null });<br>      },<br>      onPaymentMethodReceived: (<em class="markup--em markup--pre-em">obj</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>        checkout.teardown(() <em class="markup--em markup--pre-em">=&gt;</em> {<br>          checkout = null;<br>          that.handleBraintreePayment(obj.nonce);<br>        });<br>      }<br>   });<br>  });<br>}</pre><p name="0247" class="graf graf--p graf-after--pre">In above method, first we call the <code class="markup--code markup--p-code">getClientTokenForPaypal()</code> method that will call the Node JS server <code class="markup--code markup--p-code">brainTreeClientToken</code> API and returns the token. Once we get client token, we can setup Braintree using it. This setup method will initialize the PayPal-container that will be used to render the PayPal button in HTML file.</p><p name="c560" class="graf graf--p graf-after--p">Once the client token is obtained, Paypal button will be automatically rendered in HTML file in this place. Make sure you have this in your HTML file</p><pre name="3f94" class="graf graf--pre graf-after--p">&lt;ion-button class="paypalBtn" id="paypal-container"&gt;&lt;/ion-button&gt;</pre><p name="a038" class="graf graf--p graf-after--pre">This will render the Paypal button in Page view and will look like below screen&nbsp;:-</p><figure tabindex="0" contenteditable="false" name="d116" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 430px; max-height: 54px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 12.6%;"></div><img class="graf-image" data-image-id="1*MvORSvPtD4H1XJWxH7aS-A.jpeg" data-width="430" data-height="54" src="https://cdn-images-1.medium.com/max/800/1*MvORSvPtD4H1XJWxH7aS-A.jpeg"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Paypal button rendered in Ionic HTML view after client ID is&nbsp;obtained</figcaption><br></figure><p name="480a" class="graf graf--p graf-after--figure">Now we have completed the setup and we can make payment using this button. When we click the button it will open the In-app Browser and sandbox will handle the payment and users info. This will return the nonce value in the callback defined in setup call.</p><p name="2e73" class="graf graf--p graf-after--p">One more thing we have to keep in mind that sometimes, PayPal payment screen does not go off when we make a payment (due to some screen refresh angular issues). To remove it manually, we have to use <strong class="markup--strong markup--p-strong">teardown()</strong> method defined in checkout variable (initialized on <strong class="markup--strong markup--p-strong">onReady()</strong> callback).</p><pre name="4d74" class="graf graf--pre graf-after--p">onReady: <em class="markup--em markup--pre-em">function</em> (<em class="markup--em markup--pre-em">integration</em>) {<br>  checkout = integration;<br>},</pre><p name="4cd2" class="graf graf--p graf-after--pre">Below is the callback that will handle the nonce token and pass on to other method that will handle the transaction.</p><pre name="4eda" class="graf graf--pre graf-after--p">onPaymentMethodReceived: (<em class="markup--em markup--pre-em">obj</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>   checkout.teardown(() <em class="markup--em markup--pre-em">=&gt;</em> {<br>     checkout = null;<br>     that.handleBraintreePayment(obj.nonce);<br>   });<br>}</pre><p name="a491" class="graf graf--p graf-after--pre">We will pass the nonce value to <strong class="markup--strong markup--p-strong">handleBraintreePayment()</strong> method that will further call the transaction API defined in Node JS server. Here you can console the final response after payment.</p><pre name="fd87" class="graf graf--pre graf-after--p">async handleBraintreePayment(<em class="markup--em markup--pre-em">nonce</em>) {<br>  this.api.makePaymentRequest(this.payableAmount,    nonce).then((<em class="markup--em markup--pre-em">transaction</em>) <em class="markup--em markup--pre-em">=&gt;</em> {<br>  <em class="markup--em markup--pre-em">console</em>.log('Transaction', transaction);<br>  })<br>}</pre><p name="c21b" class="graf graf--p graf-after--pre">Above <strong class="markup--strong markup--p-strong">makePaymentRequest()</strong> method will call the transaction API defined in Node JS server, that will make the original payment using Braintree. Below is the function call&nbsp;:-</p><pre name="caef" class="graf graf--pre graf-after--p">async makePaymentRequest(<em class="markup--em markup--pre-em">amount</em>, <em class="markup--em markup--pre-em">nonce</em>) {<br>  <em class="markup--em markup--pre-em">const</em> paymentDetails = {<br>    paymentAmount: amount,<br>    nonceFromTheClient: nonce<br>  }<br>  return await   this.http.post('http://localhost:3000/checkoutWithPayment', paymentDetails).toPromise();<br>}</pre><p name="d097" class="graf graf--p graf-after--pre">Above function will contain the paymentAmount and nonce value and will passed them to the server API. That will return the transaction object and we can decide the client end logic according to the response that we get from server. This will complete the PayPal Braintree integration from both client side and server side.</p><h3 name="1faf" class="graf graf--h3 graf-after--p">Step 6&hairsp;—&hairsp;Testing</h3><p name="c3f4" class="graf graf--p graf-after--h3">As mentioned earlier, it is easy to test this setup in a web domain, because the server can run on localhost, and the Ionic app can call the server while running with <code class="markup--code markup--p-code">ionic serve</code>&nbsp;. Here how the payment flow looks</p><figure tabindex="0" contenteditable="false" name="db10" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 485px; max-height: 650px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 134%;"></div><img class="graf-image" data-image-id="1*-YE2LgNaTROcl-RbMYBNhg.gif" data-width="485" data-height="650" src="https://cdn-images-1.medium.com/max/800/1*-YE2LgNaTROcl-RbMYBNhg.gif"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">PayPal payment in Ionic app using Braintree</figcaption><br></figure><p name="451f" class="graf graf--p graf-after--figure">To test it in your device, you will need to deploy the server on a cloud so you can call the APIs from the app. If there is a way you can call localhost APIs from your app (I don’t know any), that should work as well&nbsp;!</p><h3 name="c806" class="graf graf--h3 graf-after--p">Troubleshooting</h3><h4 name="57b4" class="graf graf--h4 graf-after--h3"><strong class="markup--strong markup--h4-strong">&gt;&gt; braintree is not&nbsp;defined</strong></h4><p name="2b1d" class="graf graf--p graf-after--h4">Check if you included the Braintree script in <code class="markup--code markup--p-code">index.html</code></p><h4 name="2d5e" class="graf graf--h4 graf-after--p">&gt;&gt; Authentication error in&nbsp;API</h4><p name="2777" class="graf graf--p graf-after--h4">Check if you have entered correct API keys and Merchant Id in node server. Also check if you are using Production keys in Sandbox, or vice versa</p><h3 name="6071" class="graf graf--h3 graf-after--p">Conclusion&nbsp;:-</h3><p name="cb42" class="graf graf--p graf-after--h3">Congratulations&nbsp;!! 🎉 You just learnt how to integrate the awesome Braintree payment system to make payments using PayPal.</p><p name="bba7" class="graf graf--p graf-after--p">You can make payments using both PayPal account and Debit / Credit Cards. This way of integration can work both in Web view and mobile apps. If you want to know more about any feature integration, you can check out our amazing tutorials at <a href="https://enappd.com/blog/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Enappd Blogs</a>.</p><p name="956c" class="graf graf--p graf-after--p graf--trailing">Happy coding&nbsp;!</p></div></div></section><section name="d6b0" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="0647" class="graf graf--h3 graf--leading">Next Steps</h3><p name="ee74" class="graf graf--p graf-after--h3 graf--trailing">If you liked this blog, you will also find the following blogs interesting and helpful. Feel free to ask any questions in the comment section</p></div></div></section><section name="06ac" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="bdf7" class="graf graf--h3 graf--leading">Ionic Capacitor</h3><ul class="postList"><li name="5f8e" class="graf graf--li graf-after--h3"><strong class="markup--strong markup--li-strong">Basic</strong>&hairsp;—&hairsp;<a href="https://enappd.com/blog/use-geolocation-geocoding-and-reverse-geocoding-in-ionic-capacitor/131"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Geolocation</a> | <a href="https://enappd.com/blog/qr-code-and-barcode-scanning-with-ionic-capacitor/127"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Barcode &amp; QR code</a> | <a href="https://enappd.com/blog/facebook-login-in-capacitor-apps-with-ionic-angular/128"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Facebook Login</a> (Angular) | <a href="https://enappd.com/blog/facebook-login-in-ionic-react-capacitor-apps/118"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Facebook Login (React)</a> | <a href="https://enappd.com/blog/icon-splash-in-ionic-react-capacitor-apps/114"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Icon and Splash</a> | <a href="https://enappd.com/blog/camera-and-gallery-in-ionic-react-app-using-capacitor/110"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Camera &amp; Photo Gallery</a> | <a href="https://enappd.com/blog/debugging-ionic-apps-using-chrome-and-safari-developers-tools/167"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Debugging with browser</a>|<a href="https://enappd.com/blog/make-awesome-theme-switcher-in-ionic-5/170"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Theming in Ionic apps</a></li><li name="0f51" class="graf graf--li graf-after--li"><strong class="markup--strong markup--li-strong">Advanced</strong>&hairsp;—&hairsp;<a href="https://enappd.com/blog/implement-admob-in-ionic-react-capacitor-apps/135"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">AdMob</a> | <a href="https://enappd.com/blog/local-notifications-in-ionic-5-capacitor/132"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Local Notifications</a> | <a href="https://enappd.com/blog/google-login-in-ionic-react-capacitor-apps/122"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Google Login</a> | <a href="https://enappd.com/blog/twitter-login-in-ionic-react-capacitor-app/121"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Twitter Login</a> | <a href="https://enappd.com/blog/html5-games-in-ionic-capacitor-using-phaser/115"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Games using Phaser</a> | <a href="https://enappd.com/blog/play-music-in-ionic-capacitor-apps/112"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Play music</a> | <a href="https://enappd.com/blog/firebase-push-notification-in-ionic-react-capacitor/111"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Push Notifications</a></li></ul><h3 name="da78" class="graf graf--h3 graf-after--li">Ionic Cordova</h3><ul class="postList"><li name="23b7" class="graf graf--li graf-after--h3"><a href="https://enappd.com/blog/ionic-app-with-nodejs-express-mysql-sequelize-taxi-app/160"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Taxi Booking App example with Ionic, Node, Express and MySQL</a></li><li name="e3ed" class="graf graf--li graf-after--li">Ionic Payment Gateways&hairsp;—&hairsp;<a href="https://enappd.com/blog/integrate-stripe-payment-gateway-in-ionic-5-apps-and-pwa-using-firebase/158"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Stripe with Firebase</a> | <a href="https://enappd.com/blog/ionic-5-stripe-payment-integration-firebase-cloud-functions-vs-node-express-based-server/159"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Stripe with NodeJS</a> | <a href="https://enappd.com/blog/paypal-integration-in-ionic-apps-and-pwa/142"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">PayPal</a> |<a href="https://enappd.com/blog/paypal-payment-integration-using-braintree-in-ionic-5-apps/177/"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">PayPal with Braintree</a>| <a href="https://enappd.com/blog/how-to-integrate-apple-pay-in-ionic-4-apps/21"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Apple Pay</a> | <a href="https://enappd.com/blog/how-to-integrate-razorpay-in-ionic-4-apps-and-pwa/20"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">RazorPay</a></li><li name="4ecb" class="graf graf--li graf-after--li">Ionic Charts with&hairsp;—&hairsp;<a href="https://enappd.com/blog/ionic-4-charts-using-google-charts/66"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Google Charts </a>| <a href="https://enappd.com/blog/adding-charts-in-ionic-4-apps-and-pwa-part-3/60"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">HighCharts</a> | <a href="https://enappd.com/blog/adding-charts-in-ionic-4-apps-and-pwa-part-2/54"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">d3.js</a> | <a href="https://enappd.com/blog/charts-in-ionic-4-apps-and-pwa-part-1/52"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Chart.js</a></li><li name="7dc9" class="graf graf--li graf-after--li">Ionic Authentications&hairsp;—&hairsp;<a href="https://enappd.com/blog/firebase-email-authentication-in-ionic-apps/153"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Via Email</a> | <a href="https://enappd.com/blog/implement-anonymous-login-in-ionic-apps-with-firebase/154"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Anonymous</a> | <a href="https://enappd.com/blog/facebook-login-in-ionic-apps-using-firebase/150"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Facebook</a> | <a href="https://enappd.com/blog/implement-google-login-in-ionic-apps-using-firebase/147"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Google</a> | <a href="https://enappd.com/blog/twitter-login-in-ionic-4-apps-using-firebase/24"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Twitter</a> | <a href="https://enappd.com/blog/firebase-phone-authentication-in-ionic-5-apps/169"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Via Phone</a></li><li name="1310" class="graf graf--li graf-after--li">Ionic Features&hairsp;—&hairsp;<a href="https://enappd.com/blog/ionic-5-complete-guide-on-geolocation/141"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Geolocation</a>| <a href="https://enappd.com/blog/ionic-complete-guide-barcode-qrcode-scan/140"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">QR Code reader</a> | <a href="https://enappd.com/blog/best-fitness-plugins-for-ionic-4-how-to-use-pedometer/15"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Pedometer</a>| <a href="https://enappd.com/blog/implement-signature-pad-in-ionic-apps/145"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Signature</a> <a href="https://enappd.com/blog/implement-signature-pad-in-ionic-apps/145"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Pad</a> | <a href="https://enappd.com/blog/using-background-geolocation-in-ionic-angular-apps/165"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Background Geolocation</a> | <a href="https://enappd.com/blog/detect-wifi-and-data-network-in-ionic-5-apps/173"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Network Detection</a></li><li name="5a5f" class="graf graf--li graf-after--li">Media in Ionic&hairsp;—&hairsp;<a href="https://enappd.com/blog/spotify-like-music-in-ionic-4-apps/48"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Audio</a> | <a href="https://enappd.com/blog/adding-video-player-in-ionic-4-app/64"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Video</a> | <a href="https://enappd.com/blog/camera-and-image-picker-in-ionic-apps/148"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Image Picker</a> | <a href="https://enappd.com/blog/how-to-add-image-cropper-in-ionic-apps/149"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Image Cropper</a> | <a href="https://enappd.com/blog/implement-sounds-and-vibration-in-ionic-apps/172"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Sounds and Vibrations</a></li><li name="71f9" class="graf graf--li graf-after--li">Ionic Essentials&hairsp;—&hairsp;<a href="https://enappd.com/blog/debugging-ionic-apps-using-chrome-and-safari-developers-tools/167"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Debugging with browser</a>| <a href="https://enappd.com/blog/ionic-4-save-and-retrieve-data-locally-on-device/59"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Native Storage</a> | <a href="https://enappd.com/blog/how-to-translate-in-ionic-internationalization-and-localization/143"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Translations</a> | <a href="https://enappd.com/blog/how-to-use-rtl-right-to-left-in-ionic-apps-pwa/152"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">RTL</a> | <a href="https://enappd.com/blog/using-sentry-error-monitoring-with-ionic-angular-applications/164"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Sentry Error Monitoring</a> | <a href="https://enappd.com/blog/social-sharing-component-in-ionic-5-mobile-web-apps/168"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Social sharing</a> | <a href="https://enappd.com/blog/how-to-generate-and-download-pdf-in-ionic-apps/174"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">PDF generation</a></li><li name="58ce" class="graf graf--li graf-after--li">Ionic messaging&hairsp;—&hairsp;<a href="https://enappd.com/blog/how-to-implement-firebase-push-notifications-in-ionic-apps/157"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Firebase Push</a> | <a href="https://enappd.com/blog/how-to-automatically-read-sms-in-ionic-5-apps/175"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Reading SMS</a> | <a href="https://enappd.com/blog/local-notifications-in-ionic-5-capacitor/132"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Local Notifications</a></li><li name="0b6b" class="graf graf--li graf-after--li">Ionic with Firebase&hairsp;—&hairsp;<a href="https://enappd.com/blog/how-to-integrate-firebase-in-ionic-4-apps/23"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Basics</a> | <a href="https://enappd.com/blog/firebase-with-ionic-4-hosting-auth-and-db-connection/58"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Hosting and DB</a> | <a href="https://enappd.com/blog/firebase-cloud-functions-in-ionic-5-complete-guide/166"  class="markup--anchor markup--li-anchor" rel="noopener"    target="_blank">Cloud functions</a> | <a href="https://enappd.com/blog/github-actions-deploying-ionic-angular-app-to-firebase-hosting/146"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Deploy App to Firebase</a> | <a href="https://enappd.com/blog/how-to-use-firebase-emulators-with-ionic-angular-applications/163"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Firebase simulator</a></li><li name="eaef" class="graf graf--li graf-after--li graf--trailing">Unit Testing in Ionic&hairsp;—&hairsp;<a href="https://enappd.com/blog/beginners-guide-to-ionic-angular-unit-testing-part-1/151"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Part 1</a> | <a href="https://enappd.com/blog/beginners-guide-to-ionic-angular-unit-testing-part-2-mocks-and-spies/155"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Mocks &amp; Spies</a>| <a href="https://enappd.com/blog/beginners-guide-to-ionic-angular-unit-testing-async-testing/156"  class="markup--anchor markup--li-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Async Testing</a></li></ul></div></div></section><section name="5071" class="section section--body section--last"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="ad14" class="graf graf--h3 graf--leading">Ionic React Full App with Capacitor</h3><p name="4fa5" class="graf graf--p graf-after--h3">If you need a base to start your next Ionic 5 React Capacitor app, you can make your next awesome app using <a href="https://store.enappd.com/product/ionic-react-full-app-capacitor/"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic 5 React Full App in Capacitor</a></p><figure tabindex="0" contenteditable="false" name="0d2d" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 441px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 63%;"></div><img class="graf-image" data-image-id="1*G3yOFVX4oABx9MhUy-3kng.png" data-width="760" data-height="479" alt="Ionic 5 React Full App in Capacitor from Enappd" src="https://cdn-images-1.medium.com/max/800/1*G3yOFVX4oABx9MhUy-3kng.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Ionic 5 React Full App in Capacitor from&nbsp;Enappd</figcaption><br></figure><h3 name="30ba" class="graf graf--h3 graf-after--figure">Ionic Capacitor Full App (Angular)</h3><p name="9a60" class="graf graf--p graf-after--h3">If you need a base to start your next Angular <strong class="markup--strong markup--p-strong">Capacitor app</strong>, you can make your next awesome app using <a href="https://store.enappd.com/product/capacitor-full-app-with-ionic-angular/"  class="markup--anchor markup--p-anchor" rel="noopener nofollow noopener noopener noopener"    target="_blank">Capacitor Full App</a></p><figure tabindex="0" contenteditable="false" name="c219" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 761px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 108.7%;"></div><img class="graf-image" data-image-id="0*UwSHcofTMStMnzAX.png" data-width="736" data-height="800" alt="Capacitor Full App with huge number of layouts and features" src="https://cdn-images-1.medium.com/max/800/0*UwSHcofTMStMnzAX.png"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Capacitor Full App with huge number of layouts and&nbsp;features</figcaption><br></figure><h3 name="2f1c" class="graf graf--h3 graf-after--figure">Ionic Full App (Angular and&nbsp;Cordova)</h3><p name="1008" class="graf graf--p graf-after--h3">If you need a base to start your next Ionic 5 app, you can make your next awesome app using <a href="https://store.enappd.com/product/ionic-full-app"  class="markup--anchor markup--p-anchor" rel="noopener"    target="_blank">Ionic 5 Full App</a></p><figure tabindex="0" contenteditable="false" name="16d6" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 700px; max-height: 442px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 63.1%;"></div><img class="graf-image" data-image-id="0*1ArpKyhiDHZnevwB.jpeg" data-width="700" data-height="442" alt="Ionic Full App with huge number of layouts and features" src="https://cdn-images-1.medium.com/max/800/0*1ArpKyhiDHZnevwB.jpeg"><div class="crosshair u-ignoreBlock"></div></div><figcaption class="imageCaption" contenteditable="true" data-default-value="Type caption for image (optional)">Ionic Full App in Cordova, with huge number of layouts and&nbsp;features</figcaption><br></figure><p name="2fc6" class="graf graf--p graf--empty graf-after--figure graf--trailing is-selected" ><br></p></div></div></section></div></article></main>