Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

Payment Request API With Stripe Elements

Episode 81 written by Jeff Delaney
full courses and content on fireship.io

Update! Watch the latest video and get the most up-to-date code by enrolling in the Stripe Payments Master Course on Fireship.io



The Payment Request API is poised to replace traditional checkout forms in progressive web apps. This is great news both for customers and developers. The customer doesn’t have to deal with filling out long annoying forms, the developer doesn’t have to build them.

In this lesson, I will show you how to leverage this feature in your PWA with a Stripe Elements Payment Request. Not only will it mount a payment request button in Chrome, but it will also mount an Apple Pay button in Safari (as long as you register your domain).

The Payment Request API has limited support as of early 2018 - just Chrome Desktop, Android, and MS Edge. Stripe elements will also work seamlessly with Apple Pay on safari browsers, but you will need to register your domain with Apple (although this is not technically part of the payment request API) .

Full source code for Angular PWA Payments with Stripe Elements.

Payment Request API demo in Angular

Configuring Stripe Elements in Angular

There are a couple of initial config steps to get up and running with Stripe Elements in Angular.

index.html

First, add the StripeJS v3 script tag inside the head of the document.

<head>

<!-- omitted -->

<script src="https://js.stripe.com/v3/"></script>

</head>
<body>
<app-root></app-root>
</body>

typings.d.ts

Register the Stripe class with Typescript in typings.d.ts.

declare var Stripe: any;

payment.service.ts

I am using an Angular service to instantiate the main StripeJS object. Services are a singleton in Angular and will allow us to inject the Stripe object into multiple components if needed.

ng g service payment --module app

Now we can simply instantiate a Stripe object by passing it the publishable key (found on your Stripe dashboard).

Ideally, you should import the Stripe publishable key in the environment.ts file to manage keys between development and production.

import { Injectable } from '@angular/core';

@Injectable()
export class PaymentService {

stripe = Stripe('pk_test_xxxxxxxxxxxxxxx');

constructor() { }

}

Payment Request Component

Here’s a breakdown of the steps that need to happen to configure Stripe Elements payment request in an Angular component.

  1. Instantiate a paymentRequest object.
  2. Initialize Stripe elements.
  3. Register an event listener that will fire when the user submits their card.
  4. Create a button instance with the desired theme/text.
  5. Mount the button asynchronously using the native HTML element.

payment-request.component.ts

Here’s how everything fits together in the component TypeScript.

Notice how I included a setTimeout inside the event listener. This is to simulate the response from your backend to charge the card - it would not be used in your actual implementation.

import { Component, AfterViewInit, Input, ViewChild } from '@angular/core';
import { PaymentService } from '../payment.service'

@Component({
selector: 'payment-request',
templateUrl: './payment-request.component.html',
styleUrls: ['./payment-request.component.sass']
})
export class PaymentRequestComponent implements AfterViewInit {

@Input() amount: number; // Total amount
@Input() label: string; // Label for product/purchase

elements: any;
paymentRequest: any;
prButton: any;

// Element used to mount the button
@ViewChild('payElement') payElement;

constructor(private pmt: PaymentService) { }

ngAfterViewInit() {

// 1. instantiate a paymentRequest object
this.paymentRequest = this.pmt.stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
amount: this.amount,
label: this.label,
},
});

// 2. initalize elements
this.elements = this.pmt.stripe.elements();


// 3. register listener
this.paymentRequest.on('source', async (event) => {
console.log(event)

// Fires when the user submits their card
// Make an HTTP call to charge on the backend (using a timeout to simulate the response)
setTimeout(() => {
event.complete('success')
}, 1000)
});



// 4. create the button
this.prButton = this.elements.create('paymentRequestButton', {
paymentRequest: this.paymentRequest,
style: {
paymentRequestButton: {
type: 'buy', // 'default' | 'donate' | 'buy',
theme: 'dark' // 'dark' | 'light' | 'light-outline',
},
}
});

// 5. mount the button asynchronously
this.mountButton()
}

async mountButton() {
const result = await this.paymentRequest.canMakePayment();

if (result) {
this.prButton.mount(this.payElement.nativeElement);
} else {
console.error('your browser is old school!');
}

}

}

payment-request.html

The HTML is nothing more than a div with a template variable for ViewChild.

<div #payElement>
<!-- A Stripe Element will be inserted here. -->
</div>

Example Usage

The component has some input properties, allowing you to attach the button to any parent component that holds the product details. Sidenote - an amount of 2300 in Stripe is equivalent to $23.00 in USD.

<payment-request 
[amount]="2300"
[label]="'Your Awesome Product!'">

</payment-request>

Serving your Localhost Angular App over HTTPS

The big caveat at this point is that our site needs to be served over HTTPS for the PR button to work - and that includes development on localhost. I present you with two solutions to this issue.

Method 1 - Ngrok

Ngrok is free tool that uses SSH tunneling to serve your localhost app on an SSL domain. It is super slow, but allows you to test your Payment Request button easily.

First, download the package for your OS and follow the setup instructions. Second, add the following commands to your package.json file. Keep in mind, you need to point to the path where Ngrok is installed. On my Ubuntu machine it looks like this:

"scripts": {
"ng": "ng",
// omitted...
"ngrok": "~/ngrok http 4200 --host-header=localhost:8080"
},

Open a terminal tab and run:

ng serve

Then open a second tab and run:

npm run ngrok

This gives you a url https://xxxxxx.ngrok.io that you can paste into the browser and your PR button should now be working.

Method 2 - SSL with the Angular CLI

If you happen have a signed SSL certificate lying around, you can use it to tell Angular to serve your site over HTTPS.

ng serve -ssl --ssl-key "your.key" --ssl-cert "your.cert"

While it’s possible to just self-sign a certificate from the command line, Chrome will not consider it valid and your PR button will not work. In other words, if you don’t have a valid SSL, use the Ngrok method.

Wait, how do I charge the card?

Now that you have a payment source, you’re probably wondering what to do with it. You can only charge a credit card from a secure backend server. If you’re ready to take the next step, I developed a secure backend for Stripe payments using Firebase Cloud Functions accompanied by a full-length course.

The End

The Payment Request API is an exciting tool for developers building PWAs. Validating a credit card form is very tedious and difficult, so good riddance. Let me know what you think in the comments or send me a message on Slack.