java
39 lines · 6 steps
Swapping payment gateways by profile in Spring
One config class defines three PaymentGateway beans, and the active Spring profile picks exactly which one loads.
Explained by
highlit
1package com.example.payments.config;
2
3import com.stripe.StripeClient;
4import org.springframework.beans.factory.annotation.Value;
5import org.springframework.context.annotation.Bean;
6import org.springframework.context.annotation.Configuration;
7import org.springframework.context.annotation.Profile;
8
9@Configuration
10public class PaymentGatewayConfig {
11
12 @Bean
13 @Profile("local")
14 public PaymentGateway sandboxPaymentGateway() {
15 return new StubPaymentGateway();
16 }
17
18 @Bean
19 @Profile("staging")
20 public PaymentGateway stagingPaymentGateway(
21 @Value("${stripe.test-key}") String testKey) {
22 StripeClient client = StripeClient.builder()
23 .setApiKey(testKey)
24 .build();
25 return new StripePaymentGateway(client, false);
26 }
27
28 @Bean
29 @Profile("production")
30 public PaymentGateway livePaymentGateway(
31 @Value("${stripe.live-key}") String liveKey,
32 @Value("${stripe.webhook-secret}") String webhookSecret) {
33 StripeClient client = StripeClient.builder()
34 .setApiKey(liveKey)
35 .setConnectTimeout(5_000)
36 .build();
37 return new StripePaymentGateway(client, true, webhookSecret);
38 }
39}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Binding beans to profiles lets one interface resolve to different implementations per environment without conditional code.
- 2Injecting secrets through @Value keeps keys out of source and scoped to the environment that needs them.
- 3A stub implementation for local development removes any dependency on live external services.
Related explainers
rust
use std::env; use std::time::Duration; #[derive(Debug, Clone)]
Loading typed config from environment variables in Rust
configuration
error-handling
generics
Intermediate
7 steps
java
public class EventBus { private final Map<Class<?>, List<Subscriber>> subscribers = new ConcurrentHashMap<>(); private final Executor executor;
Building an annotation-driven EventBus in Java
publish-subscribe
reflection
annotations
Intermediate
7 steps
typescript
import { Injectable, Scope, Inject } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { Request } from 'express';
Request-scoped context service in NestJS
dependency-injection
request-scope
immutability
Intermediate
8 steps
java
public class OrderSerializer { private final ObjectMapper mapper;
Configuring a reusable Jackson ObjectMapper
serialization
json
builder-pattern
Intermediate
8 steps
java
package com.example.maintenance; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
How a scheduled cleanup job runs in Spring
scheduling
cron
transactions
Intermediate
6 steps
java
@Component public class RateLimitingFilter extends OncePerRequestFilter { private static final int MAX_REQUESTS = 100;
How a rate-limiting filter works in Spring
rate limiting
concurrency
servlet filter
Advanced
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/swapping-payment-gateways-by-profile-in-spring-explained-java-46e1/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.