In today’s digital era, offering seamless payment options is crucial for the success of online businesses. Stripe stands out as a comprehensive and flexible solution to accept payments online. This post will guide you step-by-step on how to create a simple and automated payment system using Stripe, ensuring a smooth checkout process for your customers.

This article is part of a comprehensive series. Be sure to check out the previous post, where we build the foundation for implementing your own payment system.

Disclaimer: I am not affiliated with Stripe. All insights shared in this article are based on my personal experience and opinions.

Table of Contents

Our Objective: Creating a Custom Checkout

Building a custom checkout system involves creating a shopping basket and handling the checkout procedure in your application, as well as tapping into Stripe webhooks for payment processing. This guide will provide a conceptual overview and code snippets to help you implement a custom checkout system.

The complete source code is available on GitHub.

Creating a custom checkout involves:

  1. Shopping Basket Management: Handling items a user (customer) added to a shopping basket in order to purchase them later on.
  2. Checkout Procedure: Order summary and collecting payment information from the customer.
  3. Payment Processing: Processing the payment in a secure and reliable manner.

Secure Payment Processing via Payment Intent

A PaymentIntent in Stripe is an object that represents your intent to collect a payment from a customer. It encompasses details such as the amount of money you wish to charge and the currency, and it plays a crucial role in the payment lifecycle.

The PaymentIntent object acts as a guide through the process of collecting a payment. It’s recommended to create exactly one PaymentIntent for each order or customer session. This object goes through several statuses during its lifecycle, which reflect the different stages of the payment process. Right from when it’s initially created to when the payment is finally confirmed or when it fails.

Here’s a brief overview of how PaymentIntent works:

  1. Creation: Create a PaymentIntent with the amount and currency you want to charge the customer.
  2. Customer Payment: The PaymentIntent can be confirmed using a payment method provided by the customer, which triggers Stripe to attempt payment. For asynchronous payment methods (like bank debits), the PaymentIntent may move to a processing state, whereas card payments typically confirm more quickly.
  3. Confirmation: Once the customer confirms the payment method and and confirms the purchase, Stripe will attempt to complete the payment. If the payment requires any further actions (such as authentication), the PaymentIntent status indicates that customer action is needed.
  4. Completion: After all necessary actions have been handled, and the payment method has been fully processed, the PaymentIntent reaches the succeeded state, indicating that the payment was successful.

The beauty of PaymentIntent lies in its ability to handle various payment states, providing you with clear signals on what needs to happen next. This helps to make your payment flow robust and user-friendly. Use of this object also helps to ensure that you meet the Strong Customer Authentication (SCA) requirements in Europe, which helps to decrease the likelihood of fraudulent payments.

Implementing a Custom Checkout

After having covered the essential preparatory steps in my last post, it’s time to proceed with integrating Stripe into our Java application for processing payments. This guide will walk you through creating a simple and effective checkout using the Stripe Java SDK.

Step 1: Define the Shopping Basket

First, create a simple BasketItem model to represent items in the shopping basket and a Basket class to manage these items.

BasketItem.java:

public record BasketItem(
        String id,
        String name,
        int quantity,
        // Pricing in cents to avoid floating point arithmetic issues
        long priceCents
) {} 

Basket.java:

public class Basket {
    private final List<BasketItem> items = new ArrayList<>();

    public void addItem(BasketItem item) {
        items.add(item);
    }

    public long getTotalAmount() {
        return items.stream()
                 .mapToLong(item -> 
                   item.priceCents() * item.quantity()
                 ).sum();
    }
    
    // Additional methods such as removeItem, clearBasket, getItems
}

Step 2: Initiate Payment Intent with Stripe

When the user decides to checkout, create a PaymentIntent for the total amount. This involves interacting with Stripe’s API, so you’ll need to add the Stripe Java library to your project and use your secret key to initialize Stripe.

First, initialize the Stripe SDK with your secret key:

Stripe.apiKey = "your_secret_key_here";

Next, you need to create a PaymentIntent for the transaction.

Let’s combine both of these steps into a dedicated service class simply called StripeService:


public class StripeService {
    public StripeService(String secretKey) {
        Stripe.apiKey = secretKey;
    }

    public PaymentIntent createPaymentIntent(long amount, String currency)
        throws StripeException {
        return PaymentIntent.create(
                new PaymentIntentCreateParams.Builder()
                        .setAmount(amount)
                        .setCurrency(currency)
                        .build()
        );
    }
}

Step 3: Collect Payment Information

For the checkout phase, you’ll need a front-end to collect user’s payment details. Stripe provides Stripe.js to securely handle this process. Here, we’ll focus on the server-side operation.

When the client-side successfully creates a payment method, it will send a payment method ID to your server. You’ll use this ID along with the PaymentIntent ID to confirm the payment.

Step 4: Confirm the Payment Intent

Once you have both the PaymentIntent ID and the payment method ID, you can confirm the payment on the server side.

In StripeService.java:

public PaymentIntent confirmPaymentIntent(
  String paymentIntentId, 
  String paymentMethodId
) throws StripeException {
        PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentIntentId);

        var params =
                PaymentIntentConfirmParams.builder()
                        .setPaymentMethod(paymentMethodId)
                        .build();

        return paymentIntent.confirm(params);
    }

Step 5: Finalize the Checkout

After confirming the payment, listen to Stripe webhooks for events like payment_intent.succeeded to finalize the order in your system. We will explore implementation details in the next post.

Summary

This guide has provided a server-side perspective on handling a custom checkout system with Stripe in a Java application. The integration with the frontend, using Stripe.js for secure payment information handling and triggering the backend logic for payment processing, is a crucial aspect that depends on your web application’s specific technology stack.

Remember to thoroughly test your integration using Stripe’s test mode to ensure everything works smoothly before going live. We’ll expand on testing and verification later on in this series.

Thanks for reading and see you around in the next part of this series where we look into checking payment status using webhooks.

The complete source code is available on GitHub.


Tomas Zezula

Hello! I'm a technology enthusiast with a knack for solving problems and a passion for making complex concepts accessible. My journey spans across software development, project management, and technical writing. I specialise in transforming rough sketches of ideas to fully launched products, all the while breaking down complex processes into understandable language. I believe a well-designed software development process is key to driving business growth. My focus as a leader and technical writer aims to bridge the tech-business divide, ensuring that intricate concepts are available and understandable to all. As a consultant, I'm eager to bring my versatile skills and extensive experience to help businesses navigate their software integration needs. Whether you're seeking bespoke software solutions, well-coordinated product launches, or easily digestible tech content, I'm here to make it happen. Ready to turn your vision into reality? Let's connect and explore the possibilities together.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *