This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new ca91c3fa8247 CAMEL-26318: camel-stripe: StripeProducer should use
request options instead of mutating JVM-global Stripe settings
ca91c3fa8247 is described below
commit ca91c3fa8247c6eec3a20bd6040824d58520c0c4
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Oct 27 10:15:54 2026 +0100
CAMEL-26318: camel-stripe: StripeProducer should use request options
instead of mutating JVM-global Stripe settings
- StripeProducer: construct endpoint-scoped RequestOptions carrying apiKey
and apiBase instead of setting global Stripe fields
- Pass RequestOptions to all Stripe SDK model calls across all operations
- Retain RequestOptions across shutdown so in-flight exchanges cannot fall
back to global SDK defaults
- Throw IllegalArgumentException on startup if apiKey is missing
- Pass null params to Subscription.cancel to preserve existing behavior of
ignoring exchange body
- Add StripeProducerTest covering RequestOptions isolation, startup
validation, and global state protection
- Document changes in 4.24 upgrade guide
Co-authored-by: Claude Opus 4.8 <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../camel/component/stripe/StripeProducer.java | 145 ++++++++-------
.../camel/component/stripe/StripeProducerTest.java | 198 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 13 ++
3 files changed, 295 insertions(+), 61 deletions(-)
diff --git
a/components/camel-stripe/src/main/java/org/apache/camel/component/stripe/StripeProducer.java
b/components/camel-stripe/src/main/java/org/apache/camel/component/stripe/StripeProducer.java
index d88a5649c8fc..a1c38bab4f85 100644
---
a/components/camel-stripe/src/main/java/org/apache/camel/component/stripe/StripeProducer.java
+++
b/components/camel-stripe/src/main/java/org/apache/camel/component/stripe/StripeProducer.java
@@ -20,8 +20,17 @@ import java.util.Collections;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.stripe.Stripe;
-import com.stripe.model.*;
+import com.stripe.model.BalanceTransaction;
+import com.stripe.model.Charge;
+import com.stripe.model.Customer;
+import com.stripe.model.Invoice;
+import com.stripe.model.PaymentIntent;
+import com.stripe.model.PaymentMethod;
+import com.stripe.model.Price;
+import com.stripe.model.Product;
+import com.stripe.model.Refund;
+import com.stripe.model.Subscription;
+import com.stripe.net.RequestOptions;
import org.apache.camel.Exchange;
import org.apache.camel.support.DefaultProducer;
import org.apache.camel.util.ObjectHelper;
@@ -30,6 +39,8 @@ public class StripeProducer extends DefaultProducer {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+ private volatile RequestOptions requestOptions;
+
public StripeProducer(StripeEndpoint endpoint) {
super(endpoint);
}
@@ -43,11 +54,23 @@ public class StripeProducer extends DefaultProducer {
protected void doStart() throws Exception {
super.doStart();
StripeConfiguration config = getEndpoint().getConfiguration();
- Stripe.apiKey = config.getApiKey();
+ if (ObjectHelper.isEmpty(config.getApiKey())) {
+ throw new IllegalArgumentException(
+ "Stripe apiKey must be configured for endpoint: " +
getEndpoint().getEndpointUri());
+ }
+
+ RequestOptions.RequestOptionsBuilder builder =
RequestOptions.builder();
+ builder.setApiKey(config.getApiKey());
if (ObjectHelper.isNotEmpty(config.getApiBase())) {
- Stripe.overrideApiBase(config.getApiBase());
+ builder.setBaseUrl(config.getApiBase());
}
+
+ this.requestOptions = builder.build();
+ }
+
+ RequestOptions getRequestOptions() {
+ return requestOptions;
}
@Override
@@ -129,20 +152,20 @@ public class StripeProducer extends DefaultProducer {
private Object handleCharges(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Charge.create(getParametersFromBody(exchange));
+ return Charge.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Charge.retrieve(id);
+ return Charge.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String chargeId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Charge charge = Charge.retrieve(chargeId);
- return charge.update(getParametersFromBody(exchange));
+ Charge charge = Charge.retrieve(chargeId, requestOptions);
+ return charge.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_LIST:
- return Charge.list(getParametersFromBody(exchange));
+ return Charge.list(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_CAPTURE:
String captureId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Charge captureCharge = Charge.retrieve(captureId);
- return captureCharge.capture(getParametersFromBody(exchange));
+ Charge captureCharge = Charge.retrieve(captureId,
requestOptions);
+ return captureCharge.capture(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
charges: " + method);
}
@@ -151,20 +174,20 @@ public class StripeProducer extends DefaultProducer {
private Object handleCustomers(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Customer.create(getParametersFromBody(exchange));
+ return Customer.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Customer.retrieve(id);
+ return Customer.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String customerId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Customer customer = Customer.retrieve(customerId);
- return customer.update(getParametersFromBody(exchange));
+ Customer customer = Customer.retrieve(customerId,
requestOptions);
+ return customer.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_DELETE:
String deleteId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Customer deleteCustomer = Customer.retrieve(deleteId);
- return deleteCustomer.delete();
+ Customer deleteCustomer = Customer.retrieve(deleteId,
requestOptions);
+ return deleteCustomer.delete(requestOptions);
case StripeConstants.METHOD_LIST:
- return Customer.list(getParametersFromBody(exchange));
+ return Customer.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
customers: " + method);
}
@@ -173,20 +196,20 @@ public class StripeProducer extends DefaultProducer {
private Object handlePaymentIntents(Exchange exchange, String method)
throws Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return PaymentIntent.create(getParametersFromBody(exchange));
+ return PaymentIntent.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return PaymentIntent.retrieve(id);
+ return PaymentIntent.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String piId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- PaymentIntent pi = PaymentIntent.retrieve(piId);
- return pi.update(getParametersFromBody(exchange));
+ PaymentIntent pi = PaymentIntent.retrieve(piId,
requestOptions);
+ return pi.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_CANCEL:
String cancelId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- PaymentIntent cancelPi = PaymentIntent.retrieve(cancelId);
- return cancelPi.cancel();
+ PaymentIntent cancelPi = PaymentIntent.retrieve(cancelId,
requestOptions);
+ return cancelPi.cancel(requestOptions);
case StripeConstants.METHOD_LIST:
- return PaymentIntent.list(getParametersFromBody(exchange));
+ return PaymentIntent.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
payment intents: " + method);
}
@@ -195,16 +218,16 @@ public class StripeProducer extends DefaultProducer {
private Object handlePaymentMethods(Exchange exchange, String method)
throws Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return PaymentMethod.create(getParametersFromBody(exchange));
+ return PaymentMethod.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return PaymentMethod.retrieve(id);
+ return PaymentMethod.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String pmId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- PaymentMethod pm = PaymentMethod.retrieve(pmId);
- return pm.update(getParametersFromBody(exchange));
+ PaymentMethod pm = PaymentMethod.retrieve(pmId,
requestOptions);
+ return pm.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_LIST:
- return PaymentMethod.list(getParametersFromBody(exchange));
+ return PaymentMethod.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
payment methods: " + method);
}
@@ -213,16 +236,16 @@ public class StripeProducer extends DefaultProducer {
private Object handleRefunds(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Refund.create(getParametersFromBody(exchange));
+ return Refund.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Refund.retrieve(id);
+ return Refund.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String refundId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Refund refund = Refund.retrieve(refundId);
- return refund.update(getParametersFromBody(exchange));
+ Refund refund = Refund.retrieve(refundId, requestOptions);
+ return refund.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_LIST:
- return Refund.list(getParametersFromBody(exchange));
+ return Refund.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
refunds: " + method);
}
@@ -231,20 +254,20 @@ public class StripeProducer extends DefaultProducer {
private Object handleSubscriptions(Exchange exchange, String method)
throws Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Subscription.create(getParametersFromBody(exchange));
+ return Subscription.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Subscription.retrieve(id);
+ return Subscription.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String subId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Subscription subscription = Subscription.retrieve(subId);
- return subscription.update(getParametersFromBody(exchange));
+ Subscription subscription = Subscription.retrieve(subId,
requestOptions);
+ return subscription.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_CANCEL:
String cancelId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Subscription cancelSub = Subscription.retrieve(cancelId);
- return cancelSub.cancel();
+ Subscription cancelSub = Subscription.retrieve(cancelId,
requestOptions);
+ return cancelSub.cancel((Map<String, Object>) null,
requestOptions);
case StripeConstants.METHOD_LIST:
- return Subscription.list(getParametersFromBody(exchange));
+ return Subscription.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
subscriptions: " + method);
}
@@ -253,16 +276,16 @@ public class StripeProducer extends DefaultProducer {
private Object handleInvoices(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Invoice.create(getParametersFromBody(exchange));
+ return Invoice.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Invoice.retrieve(id);
+ return Invoice.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String invoiceId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Invoice invoice = Invoice.retrieve(invoiceId);
- return invoice.update(getParametersFromBody(exchange));
+ Invoice invoice = Invoice.retrieve(invoiceId, requestOptions);
+ return invoice.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_LIST:
- return Invoice.list(getParametersFromBody(exchange));
+ return Invoice.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
invoices: " + method);
}
@@ -271,20 +294,20 @@ public class StripeProducer extends DefaultProducer {
private Object handleProducts(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Product.create(getParametersFromBody(exchange));
+ return Product.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Product.retrieve(id);
+ return Product.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String productId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Product product = Product.retrieve(productId);
- return product.update(getParametersFromBody(exchange));
+ Product product = Product.retrieve(productId, requestOptions);
+ return product.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_DELETE:
String deleteId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Product deleteProduct = Product.retrieve(deleteId);
- return deleteProduct.delete();
+ Product deleteProduct = Product.retrieve(deleteId,
requestOptions);
+ return deleteProduct.delete(requestOptions);
case StripeConstants.METHOD_LIST:
- return Product.list(getParametersFromBody(exchange));
+ return Product.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
products: " + method);
}
@@ -293,16 +316,16 @@ public class StripeProducer extends DefaultProducer {
private Object handlePrices(Exchange exchange, String method) throws
Exception {
switch (method) {
case StripeConstants.METHOD_CREATE:
- return Price.create(getParametersFromBody(exchange));
+ return Price.create(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return Price.retrieve(id);
+ return Price.retrieve(id, requestOptions);
case StripeConstants.METHOD_UPDATE:
String priceId =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- Price price = Price.retrieve(priceId);
- return price.update(getParametersFromBody(exchange));
+ Price price = Price.retrieve(priceId, requestOptions);
+ return price.update(getParametersFromBody(exchange),
requestOptions);
case StripeConstants.METHOD_LIST:
- return Price.list(getParametersFromBody(exchange));
+ return Price.list(getParametersFromBody(exchange),
requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
prices: " + method);
}
@@ -312,9 +335,9 @@ public class StripeProducer extends DefaultProducer {
switch (method) {
case StripeConstants.METHOD_RETRIEVE:
String id =
exchange.getIn().getHeader(StripeConstants.OBJECT_ID, String.class);
- return BalanceTransaction.retrieve(id);
+ return BalanceTransaction.retrieve(id, requestOptions);
case StripeConstants.METHOD_LIST:
- return
BalanceTransaction.list(getParametersFromBody(exchange));
+ return
BalanceTransaction.list(getParametersFromBody(exchange), requestOptions);
default:
throw new IllegalArgumentException("Unsupported method for
balance transactions: " + method);
}
diff --git
a/components/camel-stripe/src/test/java/org/apache/camel/component/stripe/StripeProducerTest.java
b/components/camel-stripe/src/test/java/org/apache/camel/component/stripe/StripeProducerTest.java
new file mode 100644
index 000000000000..c7364fc09d03
--- /dev/null
+++
b/components/camel-stripe/src/test/java/org/apache/camel/component/stripe/StripeProducerTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.stripe;
+
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.stripe.Stripe;
+import com.stripe.net.BearerTokenAuthenticator;
+import com.stripe.net.RequestOptions;
+import com.sun.net.httpserver.HttpServer;
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class StripeProducerTest {
+
+ private HttpServer server;
+
+ @AfterEach
+ public void tearDown() {
+ if (server != null) {
+ server.stop(0);
+ }
+ }
+
+ @Test
+ public void testProducerBuildsEndpointRequestOptions() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+ StripeComponent component = new StripeComponent();
+ component.setCamelContext(context);
+ StripeConfiguration config = new StripeConfiguration();
+ config.setOperation(StripeOperation.CHARGES);
+ config.setApiKey("sk_test_custom_key_1");
+ config.setApiBase("http://localhost:8999");
+
+ StripeEndpoint endpoint = new StripeEndpoint("stripe:charges",
component, config);
+ StripeProducer producer = new StripeProducer(endpoint);
+
+ producer.start();
+ try {
+ RequestOptions options = producer.getRequestOptions();
+ assertNotNull(options);
+ assertInstanceOf(BearerTokenAuthenticator.class,
options.getAuthenticator());
+ assertEquals("sk_test_custom_key_1",
((BearerTokenAuthenticator) options.getAuthenticator()).getApiKey());
+ assertEquals("http://localhost:8999", options.getBaseUrl());
+ assertNotEquals("sk_test_custom_key_1", Stripe.apiKey);
+ assertEquals(Stripe.LIVE_API_BASE, Stripe.getApiBase());
+ } finally {
+ producer.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleProducersIsolateOptions() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+ StripeComponent component = new StripeComponent();
+ component.setCamelContext(context);
+
+ StripeConfiguration config1 = new StripeConfiguration();
+ config1.setOperation(StripeOperation.CHARGES);
+ config1.setApiKey("sk_test_account_1");
+ config1.setApiBase("http://localhost:8001");
+ StripeEndpoint endpoint1 = new
StripeEndpoint("stripe:charges?apiKey=sk_test_account_1", component, config1);
+ StripeProducer producer1 = new StripeProducer(endpoint1);
+
+ StripeConfiguration config2 = new StripeConfiguration();
+ config2.setOperation(StripeOperation.CUSTOMERS);
+ config2.setApiKey("sk_test_account_2");
+ config2.setApiBase("http://localhost:8002");
+ StripeEndpoint endpoint2 = new
StripeEndpoint("stripe:customers?apiKey=sk_test_account_2", component, config2);
+ StripeProducer producer2 = new StripeProducer(endpoint2);
+
+ producer1.start();
+ producer2.start();
+ try {
+ assertEquals("sk_test_account_1",
+ ((BearerTokenAuthenticator)
producer1.getRequestOptions().getAuthenticator()).getApiKey());
+ assertEquals("http://localhost:8001",
producer1.getRequestOptions().getBaseUrl());
+
+ assertEquals("sk_test_account_2",
+ ((BearerTokenAuthenticator)
producer2.getRequestOptions().getAuthenticator()).getApiKey());
+ assertEquals("http://localhost:8002",
producer2.getRequestOptions().getBaseUrl());
+ } finally {
+ producer1.stop();
+ producer2.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testRequestOptionsIsolationInHttpRequests() throws Exception {
+ List<String> authHeaders = new ArrayList<>();
+
+ server = HttpServer.create(new InetSocketAddress(0), 0);
+ server.createContext("/v1/charges/ch_test_123", exchange -> {
+
authHeaders.add(exchange.getRequestHeaders().getFirst("Authorization"));
+ String response =
"{\"id\":\"ch_test_123\",\"object\":\"charge\",\"amount\":1000}";
+ byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+ exchange.getResponseHeaders().set("Content-Type",
"application/json");
+ exchange.sendResponseHeaders(200, bytes.length);
+ try (OutputStream os = exchange.getResponseBody()) {
+ os.write(bytes);
+ }
+ });
+ server.start();
+
+ String localBase = "http://localhost:" + server.getAddress().getPort();
+
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+ StripeComponent component = new StripeComponent();
+ component.setCamelContext(context);
+
+ StripeConfiguration config1 = new StripeConfiguration();
+ config1.setOperation(StripeOperation.CHARGES);
+ config1.setApiKey("sk_test_first_account");
+ config1.setApiBase(localBase);
+ StripeEndpoint endpoint1 = new StripeEndpoint("stripe:charges",
component, config1);
+ StripeProducer producer1 = new StripeProducer(endpoint1);
+
+ StripeConfiguration config2 = new StripeConfiguration();
+ config2.setOperation(StripeOperation.CHARGES);
+ config2.setApiKey("sk_test_second_account");
+ config2.setApiBase(localBase);
+ StripeEndpoint endpoint2 = new StripeEndpoint("stripe:charges",
component, config2);
+ StripeProducer producer2 = new StripeProducer(endpoint2);
+
+ // Start producer 1, then start producer 2 afterwards
+ producer1.start();
+ producer2.start();
+
+ try {
+ // Request through producer1
+ Exchange exchange1 = endpoint1.createExchange();
+ exchange1.getIn().setHeader(StripeConstants.METHOD_HEADER,
StripeConstants.METHOD_RETRIEVE);
+ exchange1.getIn().setHeader(StripeConstants.OBJECT_ID,
"ch_test_123");
+ producer1.process(exchange1);
+
+ // Request through producer2
+ Exchange exchange2 = endpoint2.createExchange();
+ exchange2.getIn().setHeader(StripeConstants.METHOD_HEADER,
StripeConstants.METHOD_RETRIEVE);
+ exchange2.getIn().setHeader(StripeConstants.OBJECT_ID,
"ch_test_123");
+ producer2.process(exchange2);
+
+ assertEquals(2, authHeaders.size());
+ assertEquals("Bearer sk_test_first_account",
authHeaders.get(0));
+ assertEquals("Bearer sk_test_second_account",
authHeaders.get(1));
+ } finally {
+ producer1.stop();
+ producer2.stop();
+ }
+ }
+ }
+
+ @Test
+ public void testMissingApiKeyFailsStartup() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+ StripeComponent component = new StripeComponent();
+ component.setCamelContext(context);
+ StripeConfiguration config = new StripeConfiguration();
+ config.setOperation(StripeOperation.CHARGES);
+
+ StripeEndpoint endpoint = new StripeEndpoint("stripe:charges",
component, config);
+ StripeProducer producer = new StripeProducer(endpoint);
+
+ assertThrows(IllegalArgumentException.class, () ->
producer.start());
+ }
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 28b8203e1e06..075a22fe9b8a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -3679,3 +3679,16 @@ options such as `exceptionHandler`) from the connection
string it hands to the d
as connection string parameters too.
The constant `Constants.PLC_NAME`, which was only used with the PLC4X scraper,
was removed.
+
+=== camel-stripe - endpoint request options isolate credentials across
endpoints
+
+The `camel-stripe` producer now applies `apiKey` and `apiBase` using
endpoint-scoped `RequestOptions` on each Stripe
+API request, rather than mutating JVM-global Stripe SDK static configuration
at startup. Multiple Stripe endpoints
+configured in the same application now operate with complete configuration
isolation.
+
+Additionally, configuring an `apiKey` on either the endpoint or the component
is now validated at startup, throwing
+an `IllegalArgumentException` if missing rather than falling back to
unauthenticated or globally-mutated SDK state.
+
+Applications whose custom code (such as beans calling `Customer.retrieve(...)`
directly) previously relied on
+`camel-stripe` setting `Stripe.apiKey` globally in the JVM at startup must now
configure `Stripe.apiKey` directly or
+pass `RequestOptions` explicitly.