Integrating Stripe Payments in Spring Boot: Step-by-Step Beginner’s Guide (2025)

    Integrating Stripe Payments in Spring Boot: Step-by-Step Beginner’s Guide (2025)

    Learn how to integrate Stripe payments in Spring Boot with this beginner-friendly guide. Covers setting up Stripe API keys, creating checkout sessions, handling webhooks, and confirming bookings.

    default profile

    Munaf Badarpura

    August 22, 2025

    6 min read

    Integrating payment processing into your Spring Boot application can seem daunting, but with Stripe's robust API and Spring Boot's flexibility, it’s a manageable task. In this guide, we'll walk through the process of integrating Stripe payments into a Spring Boot application, focusing on a booking system example. We'll cover setting up Stripe, initiating a payment session, handling webhooks, and confirming payments.

    Prerequisites#

    1. A Stripe account with API keys (secret key and webhook secret).
    2. A Spring Boot project set up with dependencies like spring-boot-starter-web and stripe-java.
    3. Basic knowledge of Spring Boot, REST APIs, and Java.
    4. Add the Stripe Java library to your pom.xml:
    <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>25.6.0</version> </dependency>

    Step 1: Configuring Stripe in Spring Boot#

    To use Stripe's API, you need to set up your Stripe secret key in the application. This key authenticates your API requests.

    In your application.properties or application.yml, add:

    stripe.secret.key=sk_test_your_stripe_secret_key stripe.webhook.secret=whsec_your_webhook_secret

    Create a configuration class to initialize the Stripe API with the secret key:

    import com.stripe.Stripe; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @Configuration public class StripeConfig { @Value("${stripe.secret.key}") private String stripeSecretKey; @PostConstruct public void init() { Stripe.apiKey = stripeSecretKey; // Set up Stripe API key } }

    This ensures the Stripe API key is set when the application starts.

    Step 2: Initiating a Payment Session#

    To process payments, you’ll create a Stripe Checkout Session, which redirects users to a Stripe-hosted payment page. Below is an example of initiating a payment for a booking.

    Controller for Initiating Payment#

    Create a REST endpoint to initiate a payment for a booking:

    import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/bookings") public class BookingController { private final BookingService bookingService; public BookingController(BookingService bookingService) { this.bookingService = bookingService; } @PostMapping("/{bookingId}/payments") public ResponseEntity<BookingPaymentInitResponseDto> initiateBookingPayment(@PathVariable Long bookingId) { BookingPaymentInitResponseDto response = new BookingPaymentInitResponseDto(bookingService.initiateBookingPayment(bookingId)); return new ResponseEntity<>(response, HttpStatus.OK); } }

    The BookingPaymentInitResponseDto is a simple data transfer object (DTO) to return the Stripe session URL:

    import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class BookingPaymentInitResponseDto { private String sessionUrl; }

    Service Logic for Payment Initiation#

    In the service layer, validate the booking, check user authorization, and create a Stripe Checkout Session:

    import com.stripe.exception.StripeException; import com.stripe.model.Customer; import com.stripe.model.Session; import com.stripe.param.CustomerCreateParams; import com.stripe.param.SessionCreateParams; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Slf4j public class BookingService { private final BookingRepository bookingRepository; private final InventoryRepository inventoryRepository; private final String frontendUrl; public BookingService(BookingRepository bookingRepository, InventoryRepository inventoryRepository, @Value("${frontend.url}") String frontendUrl) { this.bookingRepository = bookingRepository; this.inventoryRepository = inventoryRepository; this.frontendUrl = frontendUrl; } @Transactional(noRollbackFor = BookingExpiredException.class) public String initiateBookingPayment(Long bookingId) { Booking booking = bookingRepository.findById(bookingId) .orElseThrow(() -> new ResourceNotFoundException("Booking Not Found With Id: " + bookingId)); User user = getCurrentUser(); // Assume this retrieves the authenticated user if (!user.equals(booking.getUser())) { throw new UnAuthorisedException("Booking Does Not Belong To This User With Id: " + user.getId()); } if (hasBookingExpired(booking.getCreatedAt())) { inventoryRepository.expireBooking(booking.getRoom().getId(), booking.getCheckInDate(), booking.getCheckOutDate(), booking.getNumberOfRooms()); booking.setBookingStatus(BookingStatus.EXPIRED); bookingRepository.save(booking); throw new BookingExpiredException("Booking Has Been Already Expired"); } String sessionUrl = getCheckoutSession(booking, frontendUrl + "/payment/success", frontendUrl + "/payment/failure"); booking.setBookingStatus(BookingStatus.PAYMENT_PENDING); bookingRepository.save(booking); return sessionUrl; } public String getCheckoutSession(Booking booking, String successUrl, String failureUrl) { log.info("Creating session for booking with Id: {}", booking.getId()); User user = getCurrentUser(); try { CustomerCreateParams customerParams = CustomerCreateParams.builder() .setName(user.getName()) .setEmail(user.getEmail()) .build(); Customer customer = Customer.create(customerParams); SessionCreateParams sessionParams = SessionCreateParams.builder() .setMode(SessionCreateParams.Mode.PAYMENT) .setBillingAddressCollection(SessionCreateParams.BillingAddressCollection.REQUIRED) .setCustomer(customer.getId()) .setSuccessUrl(successUrl) .setCancelUrl(failureUrl) .addLineItem( SessionCreateParams.LineItem.builder() .setQuantity(Long.valueOf(booking.getNumberOfRooms())) .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency("inr") .setUnitAmount(booking.getAmount().multiply(BigDecimal.valueOf(100)).longValue()) .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName(booking.getHotel().getName() + " : " + booking.getRoom().getType()) .setDescription("Booking ID: " + booking.getId()) .build() ) .build() ) .build() ) .build(); Session session = Session.create(sessionParams); booking.setPaymentSessionId(session.getId()); bookingRepository.save(booking); log.info("Session created successfully for booking with Id: {}", booking.getId()); return session.getUrl(); } catch (StripeException e) { throw new RuntimeException("Failed to create Stripe session", e); } } }

    This code:

    1. Validates the booking and user.
    2. Checks if the booking has expired.
    3. Creates a Stripe customer and a checkout session with details like the hotel name, room type, and amount (in INR, multiplied by 100 for Stripe’s cent-based system).
    4. Updates the booking status to PAYMENT_PENDING and saves the session ID.

    Step 3: Handling Stripe Webhooks#

    Stripe uses webhooks to notify your application of payment events, such as a completed checkout session. Set up a webhook endpoint to handle these events.

    Webhook Controller#

    import com.stripe.exception.SignatureVerificationException; import com.stripe.model.Event; import com.stripe.net.Webhook; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/webhook") public class StripeWebhookController { private final BookingService bookingService; private final String endpointSecret; public StripeWebhookController(BookingService bookingService, @Value("${stripe.webhook.secret}") String endpointSecret) { this.bookingService = bookingService; this.endpointSecret = endpointSecret; } @PostMapping("/payment") public ResponseEntity<Void> capturePayments(@RequestBody String payload, @RequestHeader("Stripe-Signature") String sigHeader) { try { Event event = Webhook.constructEvent(payload, sigHeader, endpointSecret); bookingService.capturePayment(event); return ResponseEntity.noContent().build(); } catch (SignatureVerificationException e) { throw new RuntimeException("Invalid webhook signature", e); } } }

    Webhook Handling Logic#

    Add the webhook handling logic to the BookingService:

    import com.stripe.model.Session; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Slf4j public class BookingService { // Other methods as above... @Transactional public void capturePayment(Event event) { if ("checkout.session.completed".equals(event.getType())) { Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null); if (session == null) return; String sessionId = session.getId(); Booking booking = bookingRepository.findByPaymentSessionId(sessionId) .orElseThrow(() -> new ResourceNotFoundException("Booking not found for session Id: " + sessionId)); booking.setBookingStatus(BookingStatus.CONFIRMED); bookingRepository.save(booking); List<Inventory> lockReservedInventory = inventoryRepository.findAndLockReservedInventory( booking.getRoom().getId(), booking.getCheckInDate(), booking.getCheckOutDate(), booking.getNumberOfRooms()); inventoryRepository.confirmBooking( booking.getRoom().getId(), booking.getCheckInDate(), booking.getCheckOutDate(), booking.getNumberOfRooms()); log.info("Successfully confirmed the booking for Booking Id: {}", booking.getId()); } else { log.warn("Unhandled event type: {}", event.getType()); } } }

    This code:

    1. Verifies the webhook event using the Stripe webhook secret.
    2. Processes the checkout.session.completed event to confirm the booking.
    3. Updates the booking status to CONFIRMED and adjusts inventory (e.g., decreases reserved rooms and increases booked rooms).

    Step 4: Testing the Integration#

    1. Set Up Webhooks Locally: Use a tool like ngrok to expose your local server to Stripe’s webhook events. Configure the webhook URL in the Stripe Dashboard (e.g., https://your-ngrok-url/webhook/payment).
    2. Test Payment Flow:
      • Create a booking and initiate a payment via the /api/bookings/{bookingId}/payments endpoint.
      • Use Stripe’s test card (e.g., 4242 4242 4242 4242) to complete the payment.
      • Verify that the webhook updates the booking status to CONFIRMED.
    3. Handle Errors: Test edge cases like expired bookings or invalid payments to ensure proper exception handling.

    Best Practices#

    • Security: Store Stripe keys securely (e.g., in environment variables) and validate webhook signatures to prevent unauthorized requests.
    • Error Handling: Handle Stripe exceptions gracefully and provide meaningful error messages to users.
    • Logging: Log important events (e.g., session creation, payment confirmation) for debugging and auditing.
    • Testing: Use Stripe’s test mode extensively before going live.

    Conclusion#

    Integrating Stripe into a Spring Boot application is straightforward with the right setup. By following this guide, you can enable secure payment processing for your application, handle payment confirmations via webhooks, and manage booking statuses effectively.

    Want to Master Spring Boot and Land Your Dream Job?

    Struggling with coding interviews? Learn Data Structures & Algorithms (DSA) with our expert-led course. Build strong problem-solving skills, write optimized code, and crack top tech interviews with ease

    Learn more
    Java
    Stripe
    Payment integration
    Stripe Integration With Spring Boot
    Spring Boot Stripe Beginner Guide

    Subscribe to our newsletter

    Read articles from Coding Shuttle directly inside your inbox. Subscribe to the newsletter, and don't miss out.

    More articles