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.

Reply via email to