Stripe Integration with Java Spring for Payment Processing

Introduction

It's no lie that "everything is going digital". A lot of products never reach the shelves, but rather get sold online.

With the arising number of entrepreneurs, startups, and online sales, a very high percentage of websites nowadays have a payment system, whether it's a subscription to a service or a one-time payment for a product.

Handling finances is a delicate subject, and coming up with your own logic to handle payments can sound off-putting for a lot of developers, especially if they don't have much prior experience with such systems.

To our aid come services like PayPal and Stripe. Both services are great and each have their pros and cons. While PayPal has certainly been the more popular one, Stripe offers more payment methods and simpler fees, though it does take a day more to withdraw funds - although for many, this isn't really important.

What is Stripe?

Stripe is a payment processor for small-to-medium size businesses which offers several products within their network - business data, fraud detection with machine learning, issuing cards, etc.

As it does with a lot of useful services, Spring offers support for integrating the Stripe API and implementing the payment functionality into your Spring applications.

Integrating Stripe with a Spring Boot Application

Stripe Account

First and foremost, to deal with the Stripe API, we need to open an account on Stripe.

Opening an account is very simple, and the only thing you need to do is verify your email address and phone number. Although this will only provide you with a test account while your application is approved by Stripe, which can take anywhere from a few minutes to a few days.

Right at the dashboard, you'll be able to find the live/test keys for the API:

These keys will be used by our Spring application to access the API. For now, let's simply store them in the application.properties file:

STRIPE_PUBLIC_KEY = pk_test_abc123
STRIPE_SECRET_KEY = sk_test_abc123

Obviously we've used some fake keys in the example here, so be sure to replace them with your own test keys.

Spring Boot Application

As always, for our Spring Boot application, we'll use Spring Initializr to generate a starting project. For now, we only need the spring-web-starter and spring-boot-starter-thymeleaf dependencies:

Since the stripe-java dependency isn't in the Spring Initializr list, we'll add it directly to our pom.xml file:

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

Our application will offer two options for potential buyers:

  • One-time Purchase: They can purchase a StackAbuse course for a one-time payment
  • Subscription: They can sign up for a place in our email list, where we send out weekly mini-courses (coming soon in an upcoming article)

Additionally, we'll be sending email receipts to our customers.

The Stripe API

The Stripe API works in a simple fashion. It accepts form-encoded request bodies and sends JSON-encoded responses.

For testing/learning purposes, we can use it in "test" mode, with test keys. These transactions won't actually perform any real transactions.

You can view them on the Stripe Dashboard:

With our account set-up, and our keys in mind, we could even make a request right now using curl:

$ curl https://api.stripe.com/v1/charges \
  -u {SECRET_kEY}: \
  -d amount=999 \
  -d currency=usd \
  -d description="Example charge" \
  -d source=tok_visa

Running this would yield us a JSON-encoded response:

{
  "id": "ch_1F3WU7DEYNNUvOkfbHegnPia",
  "object": "charge",
  "amount": 999,
  "amount_refunded": 0,
  "application": null,
  "application_fee": null,
  "application_fee_amount": null,
  ...
  ...
  ...
}

And if we check our dashboard, we've received $9.99 from the charge:

Now, let's make an application that'll be able to send such requests in a more human-friendly way by accepting a valid credit card.

The general flow of Stripe requests looks a little something like this:

  • Front-end: The front-end is used to send form-encoded credit card data to Stripe
  • Stripe: Stripe responds by sending back a token within the response
  • Front-end: The front-end communicates with our back-end by providing the token and amount required to pay
  • Back-end: Uses the private (secret) Stripe key, along with the token, amount and currency to process the payment

Therefore, it's only logical that we start with the front-end.

Front-End

Stripe provides us with Stripe.js, their essential library for building payment flows. Alongside the library, it provides us with Stripe Elements which are pre-built elements used for collecting sensitive consumer data - such as a credit card number.

This way, sensitive data doesn't really enter our own system and Stripe takes care of it for us in an iframe, making it safe and easy for both us and the end user.

There are several elements they offer for quick payment-info collecting:

Stripe will automatically add the preferred Stripe Element into your HTML, where you mark it. For an example, to add the Card Element:

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

<form action="/charge" method="post" id="payment-form">
  <div class="form-row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display form errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <button>Submit Payment</button>
</form>

We will be using checkout.js as it's a very commonly used element. It's a bit different than adding the Card Element:

<form action='/charge' method='POST' id='checkout-form' xmlns:th="http://www.w3.org/1999/xhtml">
    <input type='hidden' th:value='${amount/100}' name='amount' />
    <h1>Price:<span th:text='${amount/100}' /></h1>
    <script
            src='https://checkout.stripe.com/checkout.js'
            class='stripe-button'
            th:attr='data-key=${stripePublicKey},
         data-amount=${amount}'
            data-name='StackAbuse Services'
            data-description='Product Checkout'
            data-image
                    ='http://www.stackabuse.com/assets/images/sa-java-dark.svg?v=b5a08453df'
            data-locale='auto'
            data-zip-code='false'>
    </script>
</form>

For the sake of brevity and simplicity, it's just a form, pointing to our /charge endpoint. Don't mind the Thymeleaf attributes for now, we'll cover them in the controller section.

For now, it's important to note that we're simply adding the script to our page, which will result in the "Pay with Card" button:

Upon clicking it, we're greeted with our checkout window:

Controller

With the front-end, out of the way, let's focus a bit on the back-end side. Clicking "Submit" on the checkout window will send the information to Stripe, which will return a token that we need for authentication. Let's define a simple PaymentController:

@Controller
public class PaymentController {
    // Reading the value from the application.properties file
    @Value("${STRIPE_PUBLIC_KEY}")
    private String stripePublicKey;

    @RequestMapping("/")
    public String home(Model model) {
        model.addAttribute("amount", 50 * 100); // In cents
        model.addAttribute("stripePublicKey", stripePublicKey);
        return "index";
    }
}

What we do here is very simple. We assign the STRIPE_PUBLIC_KEY value to a string for later use and add it to the model so that we can use it in the front-end. In our form above, we're using the amount and stripePublicKey to send a request to Stripe, which generates a stripeToken for us in response.

Let's make an endpoint for the returned response:

@Autowired
private StripeService stripeService;

@RequestMapping(value = "/charge", method = RequestMethod.POST)
public String chargeCard(HttpServletRequest request) throws Exception {
    String token = request.getParameter("stripeToken");
    Double amount = Double.parseDouble(request.getParameter("amount"));
    stripeService.chargeNewCard(token, amount);
    return "result";
}

From the response, we simply extract the stripeToken and the amount parameters and pass them onto our service, which actually handles the communication with Stripe and the payment processing.

Service

The service layer introduces us with a few new classes:

@Service
public class StripeService {

    @Value("${STRIPE_SECRET_KEY}")
    private String API_SECRET_KEY;

    @Autowired
    public StripeService() {
        Stripe.apiKey = API_SECRET_KEY;
    }

    public Charge chargeNewCard(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;
    }
}

Typically, a service class would contain many more methods such as getCustomer(), createCustomer(), chargeCustomerCard(), etc. Though, for now and the sake of simplicity, let's just use this single method.

Here, we're using our secret key. Via the Charge class provided by Stripe, we set the charge parameters, such as the amount and currency.

The amount is multiplied by 100 since Stripe deals with the smallest option when it comes to currency - in this case cents. To express it in dollars, we're multiplying it.

At this point, we're able to run the application and test it out.

Running the Application

Running the application, we're greeted by our index page:

After clicking on the button, we're prompted with the checkout window. For testing purposes, you can use a test card number, such as 4242 4242 4242 4242:

After that, we're greeted with the result page, and we can also validate the payment on the dashboard:

When you'd like to transfer this into an actual application that will genuinelly charge people for a product, simply swap out the test keys and put in the real ones.

Sending Email Receipts

Now, we want to send an email with a receipt each time someone makes a purchase. To do this, instead of defining our own logic, which would be reduntant, Stripe sends out emails to customers once their payments have been accepted or refunded.

Note: The Test API will not send email confirmations/receipts. You can read more about this here.

You can configure the Stripe receipt settings in your dashboard. If you want any more info on this, check out Stripe's documentation.

Conclusion

With the rise of online dependency and payment, it's most probable that at some point your web application will have to handle/process some payments. Since it's a touchy subject, it's easiest and safest to let a third-party processor such as Stripe or PayPal handle the actual transaction and act as a trusted middleman.

In this article we demonstrated how to integrate Stripe's API in to a Spring Boot application, and how to successfully charge a customer's card.