cft

More Secure Passwords With Angular and Django

Turn your users into customers today


user

Klement Omeri

3 years ago | 11 min read

In this post, we will implement a full check-out flow with Stripe using Angular on the front end and a Node.js back end.

Stripe is a payment processor, which means it supports the electronic transfer of money from a customer’s bank (issuing bank), into a merchant’s bank (gaining bank) as payment for goods or services bought with a credit card.

I will skip project creations and Stripe account creation, I want to focus on Stripe.js and the Stripe API.

The flow we will build in this post follows these steps. Only steps one and two are on the front-end side.

  1. Collecting card information.
  2. Tokenization of that information and sending it to the back end.
  3. Placing a hold on the card (authorization).
  4. Some custom business logic.
  5. Clearing process of the authorized funds.

What We Will Build

From the Stripe docs

Front End

Let’s start by implementing the first step. To do that, we can take some support from Stripe.js, which is a library built by Stripe for the front end.
The first thing we need to do is to include this library in index.html.

Include Stripe.js in the project:

<!-- index.html -->
<body>
<app-root></app-root>
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
const stripe = Stripe('use your test publishable key');
const elements = stripe.elements();
</script>
</body>

We have included v3 of stripe.js and we have told Stripe who we are using for the Stripe publishable key which can be found on the Stripe dashboard.

Now, to use elements and stripe constants, we need to declare them in typings.d.ts because TypeScript would normally complain when trying to access stripe or elements.

Now we are done including Stripe.js in our project so the next step is to initialize a Stripe element.

Stripe elements are customizable UI components used to collect sensitive information in payment forms.

Let’s write a form that will take card information from the customer. Our form will look like this:

<!-- stripe-payment.component.html -->
<form (ngSubmit)="createStripeToken()" class="checkout">
<label for="card-info">Card Info</label>
<div id="form-field">
<div id="card-info" #cardInfo></div>
<button id="submit-button" type="submit">
Pay ${{_totalAmount}}
</button>
<mat-error id="card-errors" role="alert" *ngIf="cardError">
<mat-icon style="color: #f44336">cancel</mat-icon>
&nbsp;{{ cardError }}
</mat-error>
</div>
</form>

Don’t forget to include FormsModule in your app.module or first parent module’s imports array.

Now, the TypeScript for that template.

import {AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, ViewChild} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
@Component({
selector: 'app-stripe-payment',
templateUrl: './stripe-payment.component.html',
styleUrls: ['./stripe-payment.component.scss'],
})
export class StripePaymentComponent implements OnDestroy, AfterViewInit {
@ViewChild('cardInfo') cardInfo: ElementRef;
_totalAmount: number;
card: any;
cardHandler = this.onChange.bind(this);
cardError: string;
constructor(
private cd: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA) private data: any,
private dialogRef: MatDialogRef<StripePaymentComponent>,
) {
this._totalAmount = data['totalAmount'];
}
ngOnDestroy() {
if (this.card) {
// We remove event listener here to keep memory clean
this.card.removeEventListener('change', this.cardHandler);
this.card.destroy();
}
}
ngAfterViewInit() {
this.initiateCardElement();
}
initiateCardElement() {
// Giving a base style here, but most of the style is in scss file
const cardStyle = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4',
},
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a',
},
};
this.card = elements.create('card', {cardStyle});
this.card.mount(this.cardInfo.nativeElement);
this.card.addEventListener('change', this.cardHandler);
}
onChange({error}) {
if (error) {
this.cardError = error.message;
} else {
this.cardError = null;
}
this.cd.detectChanges();
}
async createStripeToken() {
const {token, error} = await stripe.createToken(this.card);
if (token) {
this.onSuccess(token);
} else {
this.onError(error);
}
}
onSuccess(token) {
this.dialogRef.close({token});
}
onError(error) {
if (error.message) {
this.cardError = error.message;
}
}
}

What we do here is create a card element and mount it to the #cardInfo element in our template.

The createStripeToken() method will try to create a Stripe token using the card element and will call onSuccess() or onError() according to the case. I have implemented the form on a MatDialog, it is just a preference.

After the dialog is closed, I take the result and call the service with an order already taken from the customer and a token which will capture the required funds for that order.

The response from Stripe for a successful token creation would be:

{
"id": "tok_1FqKbyIPlNca4kE9oThoJxfc",
"object": "token",
"card": {
"id": "card_1FqKbyIPlNca4kE9GuiRRvSL",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"cvc_check": null,
"dynamic_last4": null,
"exp_month": 8,
"exp_year": 2020,
"fingerprint": "0iHSoz5pAY9Jqycl",
"funding": "credit",
"last4": "4242",
"metadata": {},
"name": null,
"tokenization_method": null
},
"client_ip": null,
"created": 1576506710,
"livemode": false,
"type": "card",
"used": false
}

We will use only the ID attribute from this response. The method below is the method that opened up the payment dialog and it is waiting for the dialog’s result object.

// for example, this can be checkout button on the final step of an order form, let's name it like
// final-step.component.ts
checkout() {
const dialogRef = this.dialog.open(StripePaymentComponent, {
// opening dialog here which will give us back stripeToken
data: {totalAmount: this.getTotal()},
});
dialogRef.afterClosed()
// waiting for stripe token that will be given back
.subscribe((result: any) => {
if (result) {
this.createOrder(result.token.id);
}
});
}

At this moment, all we need to do is to call the required service with order data and the Stripe token in the request payload. This is done by the helper method createOrder(token: string).

Now, the front-end side of the check-out flow has ended. We have created a Stripe element to take card information, tokenized that information using stripe.js to not pass any sensitive data to our back end, and sent that single-use token to the back end.

Let’s move on to the back-end side and see what happens here.

Move to the Back End

First, let’s install the package. We have a great library called Stripe for npm that helps us so much using the Stripe API.

npm install stripe

After the installation has finished, we need to include stripe in the file we will work on, which is controller.js or helper.js in this case. Add the following with your secret key to the head of the file.

Include in project:

const stripe = require('stripe')('change this to your secret key');

Stripe Integration

What we will do at the back-end side is to take order data and the Stripe token and implement our custom logic. So, we will do it in three main steps.

  1. We will immediately create a Stripe Charge but do not capture it (capture: false).
  2. Operate with the creation of the order.
  3. After the order creation, if it is successful, we will charge the authorized funds, otherwise, we will refund them back to the client.

Let’s suppose that we have an API endpoint like so:

// index.js
router.post('order',
token({ required: true }),
controller.create)

Inside the create() controller, we need to take pricing of that order and then create a charge on Stripe for that price with the Stripe token in the request.body.

// controller.js
exports.create = function(req, res, next) {
const stripeToken = req.body.stripeToken;
const price = Helper.getPrice(req.body.order);
const priceInPence = price * 100;
stripe.charges.create({
amount: priceInPence,
currency: 'usd',
source: stripeToken,
capture: false, // note that capture: false
}).then(response => {
// do something in success here
}).catch(error => {
// do something in error here
});
};

Let’s note two key things here.

1. We get pricing with a helper function.
2. We convert it to pence.

You may already have pricing inside the request.body and now you may think: “Oh yes, he is getting the price with a helper because he doesn’t have it already in the order data,” but that is wrong.

Even if you have an attribute like req.body.order.price, do not use it to create a charge object on Stripe. It is easy to manipulate the request payload coming from the front end, and that price in req.body may not be the real price of that order.

Always recalculate it at the back-end side, which is a more secure environment compared to the front end.

“What about this conversion?” you might ask. That’s because Stripe accepts price only as pence and it has to be an integer, not a decimal. So, if you want to charge $1, the amount is $1 x 100 = 100 pence. If you want to charge $100, the amount must be 10,000 pence.

This is an important thing that requires some attention. If charge creation will take place on many controllers, I strongly recommend writing createCharge() and getChargeData() helper functions to be used everywhere we must create a charge.

That’s the way I am working with my implementation at my every-day job to make it less error-prone. So, the helpers would look something like:

Let’s get back to the point we created the charge. One important thing to note here is the capture: false attribute.

By default, Stripe will create and charge the client immediately for the amount given. What we want is to place a hold as we want to authorize those funds. That’s because we are not sure if the order will create successfully.

If the charge can be created successfully, Stripe’s response would be like:

At this moment, we have a charge object created but not captured on Stripe’s side for the client related to the token and for the amount we specified.

What does it mean that we have created a charge object? It means that we have transferred the required funds for the client’s order from the client’s bank to Stripe, and we’ve placed a hold on that card.

We do not transfer those funds to our side. They are just blocked by Stripe and will be on hold for seven days. This is another important part. This authorization process can last for a max. of seven days.

What happens after that? After that, Stripe will automatically refund all the funds that the charge is holding back from the client.

So, if you have a flow that requires some time to know if the service or goods you provide can be sent to the client, make sure that this time is no more than seven days or choose another checkout flow.

OK, now, let’s create the client’s order and then capture those funds.

What happens inside makeOrderCreation()?

So, we have used another helper function to create the order in the database and then after the order is created, we return the success response to the front end. After that, we will capture the authorized funds.

To capture a charge that was previously created, we need to perform a capture action and all we need is the charge’s ID.

Let’s perform the capture action, it is as simple as follows.

“Capturing a charge will always succeed, unless the charge is already refunded, expired, captured, or an invalid capture
amount is specified, in which case this method will throw an error.” — Stripe docs

Now, by default, if you don’t specify the amount to capture, Stripe will capture the full amount inside that charge object which is exactly what we want.

Refund Process

What happens if the order can not be created? We have already authorized the required funds, but we can’t create the order. So, what to do now?

Although Stripe will automatically refund this charge after seven days, it is not a good thing to see a charge on your statement as a client for a failed order. So, what we can do is to create a refund for that charge.

We create that refund in the createOrder().catch() method.

What we do here is create a full refund of that charge because we could not create the order so we don’t want to hold our client’s funds for no reason.

Conclusion

We have implemented a full check-out flow using Stripe, Angular, and Express. With Stripe, the flow of the checkout is highly customizable which enables you to fulfill your needs.

The flow we followed in this article was:

  1. Collection of information using Stripe.js.
  2. Tokenization of information using Stripe.js.
  3. Sending the token to the back end together with order data.
  4. Creating a charge on Stripe using that token.
  5. Creation of the order.
  6. Capture or refund of charge on Stripe.

Please feel free to ask me any questions or give any suggestions.

Thanks for reading and happy coding!

This article was originally published by Klement omeri on medium.

Upvote


user
Created by

Klement Omeri

Math and Informatics Engineering at the University of Tirana. Software Engineer at CardoAI.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles