Implementation of in-app payments using Stripe and Firebase inside an Ionic 4 app.
- (3) and (5) ensure that the app securely collects and transmits credit card information to the backend.
- (2) and (7) ensure secure charge of the user's credit card.
PaymentExperimentApp
folder contains the Ionic 4 application that uses Stripe JS SDK (ngx-stripe) and Firebase JS SDK (angularfire2) to take payments from the user.
Specifically, the app does the following:
- Uses Firebase Authentication to log the user in anonymously (for simplicity), so that payments done by the user can be associated with their anonymous Firebase account
// inside **app.component.ts**
this.firebaseAuth.auth.signInAnonymously()
- Uses Stripe Elements to initialize an HTML form to securely collect credit card information from the user and tokenize it before taking payments
// inside **pay.page.ts**
this.stripeService.elements()
this.elements.create(...)
element1.mount(...)
- Uses tokenized, secure version of the credit card to charge the user through secure Firebase Cloud Function-based backend (see PaymentExperimentBackend for details)
// inside **pay.page.ts**
this.stripeService.createToken(this.cardNumberElement, { address_zip: this.zipCode })
...
this.firebaseFunctions.httpsCallable('makePayment')
- Uses basic Firebase Firestore database to retrieve user's payments (saved by the backend upon successful payment)
// inside **history.page.ts**
this.stripeService.createToken(this.cardNumberElement, { address_zip: this.zipCode })
Using Stripe Elements and Firebase Cloud Function backend allows the app to avoid having to comply with the majority of the PCI Data Security Standards, offloading that onto the payment processor (Stripe).
PaymentExperimentBackend
folder contains the Firebase Cloud Functions used by the app to process user payments.
To protect both, the user of the app as well as the app company taking payments from the user, all payment processors require that the actual charge to the user is done from a secure backend and cannot be made directly from within the app (the source code of which is inherently public).
Specifically, the backend does the following:
- Uses an Firebase Authentication's
user().onCreate
trigger to automatically add a newly signed in user as a Customer in Stripe, so that all payment methods (credit cards) and future payments can be associated with the same customer/user. This function is calledcreateStripeCustomer
. (Note that Stripe Customer creation could also occur at a later point, for example, when the first payment is made.)
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
...
});
- Defines an Firebase
https.onCall
function, exposed as an HTTPS endpoint (API), that securely processes payment requests coming from the Ionic app by forwarding them to Stripe, and then saves them inside the Firestore database so the user can see their past payments. This function is calledmakePayment
.
exports.makePayment = functions.https.onCall((data, context) => {
...
});
-
Create a Stripe account to process payments (test mode)
- Ionic App will use Stripe's
publishable key
to securely collect and tokenize sensitive credit card information. - Firebase backend will use Stripe's
secret key
to securely forward payment requests to Stripe.
- Ionic App will use Stripe's
-
Create a Firebase account to handle basic authentication, and securely take payment requests from the app and forward them to Stripe
- Create a new Firebase project.
- Change its plan to
BLAZE
(pay-as-you-go) to be able to make external API calls (have to call Stripe APIs). - Enable
Anonymous Authentication
(or more) on the project, to be able to associate payments we a user account. - Create a test
Firestore Database
to hold mappings between Firebase user and Stripe customer accounts, and to maintain payment history for each user.
-
Run
npm install
to install dependencies -
Update
environment.ts
file with configuration settings from the Firebase Console (under Project Overview - Add Web App) [firebaseConfig
property]
export const environment = {
...
firebaseConfig: {
apiKey: "******************************",
authDomain: "******************************",
databaseURL: "******************************",
projectId: "****************",
storageBucket: "******************************",
messagingSenderId: "*********"
},
...
};
- Update
environment.ts
file with Stripe's public key (under Developers => API Keys - Publishable key) [stripeKey
property]
export const environment = {
...
stripeKey: '******************************',
...
};
- Open the terminal inside the
functions
subfolder and install NPM dependencies:
cd functions
npm install
- From the same terminal, log into Firebase by running:
firebase login
NOTE: Use firebase logout
command if you need to switch accounts.
- Point Firebase CLI to the Firebase project you've created for this project by running:
firebase use YOUR_FIREBASE_PROJECT_ID
- Save sensitive Stripe settings into Firebase Function configuration so they are not checked into source control by running:
firebase functions:config:set stripe.token="****STRIPE_SECRET_TOKEN****"
firebase functions:config:set stripe.currency="USD"
NOTE: Grab your STRIPE_SECRET_TOKEN
from your Stripe console, under Developers => API Keys
- Deploy the two Firebase Functions (
createStripeCustomer
andmakePayment
) - to your Firebase project by running:
firebase deploy
-
Run the app using
ionic serve --devapp
and test inside the browser or Ionic DevApp on a mobile device, or -
Run the app on an emulator or actual device using
ionic cordova prepare ios
and then running/deploying the app using XCode (there are other CLI commands to do this too). -
Once you have the app running, try this:
-
Make a few payments using various test credit cards provided by Stripe by clicking the
Pay
button:4242 4242 4242 4242
(Visa)5555 5555 5555 4444
(Mastercard)4000 0000 0000 0069
(Visa - expired card)- NOTE: Use any expiry and CVC code
-
See your payment history under the
History
page. -
See detailed payment processing logs under
Profile
page. -
Check your Firebase Firestore and Function Logs to see how your payment requests are processed and stored on the backend.
-
NOTE: Search for
// IFS:
(IonicFireStripe) inside the Ionic app to quickly find the most important bits of code that make payments work.
-
Platform-specific payment methods (Apple Pay/Google Pay) have been partially implemented, but have not been verified as Apple/Google require additional app store configuration for their payment methods to work.
-
Fetching of existing payment methods (credit cards) for the current user is not implemented.