Using Stripe payments in Spring Boot + Angular application

Ihor Sokolyk
ORIL Software
Published in
7 min readNov 19, 2017

Introduction

Nowadays, almost all customers want to add in-app purchases to their mobile apps or websites. And if you are a developer and still didn’t face with such a project then I am more than 100% sure that in the nearest future you will definitely deal with it. This article will help you to implement payments in your application very quick and easy.

There is a lot of different payment services like PayPal, Stripe, Authorize.net etc., but in this article I’m going to use Stripe as it is one of the most popular, easy to use and fast to integrate. I will show you how to use Stripe in your Spring Boot application and communicate with Angular web application.

Enough talking, let’s start…

Account Creation

First of all you need to create your account on Stripe.com website. You don’t need to add your credit card info and other stuff just verify your email address and phone number to be able to use their service.

After doing this you should get your api keys. Go to API tab on the leftside menu and you will find Publishable key and Secret key. We will be using these keys later for making our requests.

Stripe API keys

Before we start coding you need to understand one thing which is very important. Stripe service is based on tokens. When you want to charge credit card you need to create token passing credit card info as parameters and then charge card using created token as a source. All token can be used only once, so if you’ll try to make some other operation with the same token you will get an error that token is invalid or expired.

Token can be generated on you back-end using appropriate method from Stripe, but it’s not a good practice to send credit card info directly to your server, because someone can intercept this data (Man-in-the-middle attack).

The best way is sending credit card info directly to Stripe using Stripe.js library. Stripe servers are more secured than yours so the risk of stealing data is very small. The flow for such case is the following (and we will use it in our project):

  • Send data from web page to Stripe;
  • Stripe returns token;
  • Send token to backend server;
  • Charge credit card using token;

Such approach has two benefits:

  • you don’t pass any sensitive data to your backend server
  • even if token will be intercepted by a man-in-the-middle he cannot retrieve any credit card info from this token

Angular Part

So let’s start by creating our angular project. Use the next command:

ng new stripe-example

This command will create new angular project. If ng is not recognized then you don’t have angular CLI installed, so install it using command

npm install -g @angular/cli

Now you can run the project using the following command:

ng serve

Application will start on port 4200. So go to localhost:4200 in your browser and you should see welcome page.

We will create a credit card form and on submit button we will send this data to Stripe for generation token. Let’s add the simplest form without any styles and validation to app.component.html file

<form>
<input type="text" placeholder="Credit Card #" name="cardNumber">
<input type="text" placeholder="Expiration Month" name="expMonth">
<input type="text" placeholder="Expiration Year" name="expYear">
<input type="text" placeholder="CVC" name="cvc">
<button (click)="chargeCreditCard()">Submit</button>
</form>

Now we need to add Stripe.js to our project. For doing this we should add the following script to our index.hml file:

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('pk_test_XXXXXXXXXXXXXXXXX');
</script>

As you can see we set Publishable Key here that was mentioned before.

Now add chargeCreditCard() method to app.component.ts.

chargeCreditCard() {
let form = document.getElementsByTagName("form")[0];
(<any>window).Stripe.card.createToken({
number: form.cardNumber.value,
exp_month: form.expMonth.value,
exp_year: form.expYear.value,
cvc: form.cvc.value
}, (status: number, response: any) => {
if (status === 200) {
let token = response.id;
this.chargeCard(token);
} else {
console.log(response.error.message);
}
});
}

When you will enter credit card info and press submit button chargeCreditCard() method will be called and token will be generated on Stripe side. If some info will be invalid then error message will be received.

We only left to add method that will send token to our backend server.

chargeCard(token: string) {
const headers = new Headers({'token': token, 'amount': 100});
this.http.post('http://localhost:8080/payment/charge', {}, {headers: headers})
.subscribe(resp => {
console.log(resp);
})
}

This method should be called after retrieving token from Stripe. Also we need to import Http into our component and add it to constructor:

import { Http, Headers} from '@angular/http';
....
constructor(private http: Http) {}

And don’t forget to add HttpModule into your app.module.ts under imports section:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {HttpModule} from "@angular/http";

import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Frontend part is done! As you can see we will send our token and amount as headers to localhost:8080/payment/charge. You can send them as request body or in other way. I just used header because request body requires the same object to be created on the backend, but we don’t need it in this tutorial.

Of course it’s not working for now because we don’t have our backend server running yet. So let’s proceed to Spring Boot Part.

Spring Boot Part

Create new maven projetct with spring-boot-starter-web depencency. To use Stripe in our app we should add stripe dependency:

<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>5.21.0</version>
</dependency>

Let’s create our PaymentController with one request mapping “payment/charge” to match an url that we’re referencing from angular app.

@RestController
@RequestMapping("/payment")
public class PaymentController {

private StripeClient stripeClient;

@Autowired
PaymentController(StripeClient stripeClient) {
this.stripeClient = stripeClient;
}

@PostMapping("/charge")
public Charge chargeCard(HttpServletRequest request) throws Exception {
String token = request.getHeader("token");
Double amount = Double.parseDouble(request.getHeader("amount"));
return this.stripeClient.chargeCreditCard(token, amount);
}
}

In the code snippet above you can see that we’re retrieving stripe token and amout from headers which we are sending from angular app.

Now let’s create StripeClient with one method chargeCreditCard() class.

@Component
public class StripeClient {

@Autowired
StripeClient() {
Stripe.apiKey = "sk_test_XXXXXXXXXXXXXXXXX";
}

public Charge chargeCreditCard(String token, double amount) throws Exception {
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", (int)(amount * 100));
chargeParams.put("currency", "USD");
chargeParams.put("source", token);
Charge charge = Charge.create(chargeParams);
return charge;
}
}

In the constructor we assigned apiKey to Stripe. Insted of Publishable key we are using Secret key here. By doing this we’re telling Stripe to add this key to each request that we are making, so there is no need to add api key manually requests.

NOTE: we multplied amount by 100 because Stripe requires not dollars, but cents. So if we have to pay $100 then we should pass 100000 to Stripe. Also this value should be always integer.

Keep in mind that the minimum amount that can be charged is 50 cents. Also we are using test API keys so your credit card will not be actually charged.

If you want to save user’s credit card on Stripe side then you should create customer using the following method:

public Customer createCustomer(String token, String email) throws Exception {
Map<String, Object> customerParams = new HashMap<String, Object>();
customerParams.put("email", email);
customerParams.put("source", token);
return Customer.create(customerParams);
}

This method will return Customer object and the good practice is to save customerId to your user’s model in order not to loose it.

If you created customer with credit card then you do not need to create token and prompt user to enter his credit card. You can just charge existing customer using such method for example:

public Charge chargeCustomerCard(String customerId, int amount) throws Exception {
String sourceCard = Customer.retrieve(customerId).getDefaultSource();
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", amount);
chargeParams.put("currency", "USD");
chargeParams.put("customer", customerId);
chargeParams.put("source", sourceCard);
Charge charge = Charge.create(chargeParams);
return charge;
}

When your’re creating customer, Stripe is setting user’s credit card as default source for charging. So you can get this source and pass it as a source for charging in the same way as we passed token.

One thing is left to do is to add some configuration to avoid Cross Origin Resource Sharing (CORS) problem while making request from our angular app. So let’s create the MVCProperties.class with @Configuration annotation

@Configuration
public class MVCProperties extends WebMvcConfigurerAdapter {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS");
}
}

Testing Time

Both part of our application are ready so we can try to test it. I will use test credit card with following data:

Credit Card Info

After pressing “Submit” button you should receive a response with Charge object in your console in browser. Also you can open your Stripe account, go to Payments tab and you should see $100 charge.

Stripe Payments

If I will enter for example invalid expiration year then Stripe will response with following message “Your card’s expiration year is invalid.”

Conclusion

Now you know how to integrate payments using Stripe and this will not be a problem for you when you’ll faced with this on the real project. If you want to read more about all Stripe methods, here is a link to documentation.

Thank you guys for reading this article, I really appreciate it. If you have any question feel free to leave a comment. Here is the link to this project on GitHub. As well you can check it on Oril Software GitHub.

--

--