From f32a6174f0d0ef028070210b342cd8c38d9dd0c5 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Mon, 10 Nov 2025 20:32:12 -0500 Subject: [PATCH 1/5] Update v2 array parameter serialization to use indexed format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change array parameter serialization for v2 endpoints to use indexed format (e.g., ?include[0]=foo&include[1]=bar) instead of the repeated parameter format (e.g., ?include=foo&include=bar). This aligns v2 behavior with v1 for consistency. Changes: - Modified FormEncoder.java flattenParamsCollection to always use indexed format - Updated tests to expect indexed format for v2 arrays 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Committed-By-Agent: claude --- src/main/java/com/stripe/net/FormEncoder.java | 3 ++- src/test/java/com/stripe/net/FormEncoderTest.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index d05e4be6ee3..135e92b50ba 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -249,7 +249,8 @@ private static List> flattenParamsCollection( int index = 0; for (Object value : collection) { - String newPrefix = arraysAsRepeated ? keyPrefix : String.format("%s[%d]", keyPrefix, index); + // Always use indexed format for arrays + String newPrefix = String.format("%s[%d]", keyPrefix, index); flatParams.addAll(flattenParamsValue(value, newPrefix, arraysAsRepeated)); index += 1; } diff --git a/src/test/java/com/stripe/net/FormEncoderTest.java b/src/test/java/com/stripe/net/FormEncoderTest.java index e3e0289628c..c96064f5fc8 100644 --- a/src/test/java/com/stripe/net/FormEncoderTest.java +++ b/src/test/java/com/stripe/net/FormEncoderTest.java @@ -376,17 +376,17 @@ class TestCase { "array", new Object[] {new String[] {"foo", "bar"}, new int[] {1, 2, 3}}), "array[0][0]=foo&array[0][1]=bar&array[1][0]=1&array[1][1]=2&array[1][2]=3")); - // Array (arrayAsRepeated) + // Array (now always uses indexed format) add(new TestCase(Collections.singletonMap("array", new String[] {}), "", true)); add( new TestCase( Collections.singletonMap("array", new String[] {"1", "2", "3"}), - "array=1&array=2&array=3", + "array[0]=1&array[1]=2&array[2]=3", true)); add( new TestCase( Collections.singletonMap("array", new Object[] {123, "foo"}), - "array=123&array=foo", + "array[0]=123&array[1]=foo", true)); // Collection add( From 2f68acfd8c454b3da0fe76b8281ca045732f70c9 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Tue, 11 Nov 2025 11:45:49 -0500 Subject: [PATCH 2/5] Remove references to arraysAsRepeated --- src/main/java/com/stripe/net/FormEncoder.java | 34 ++++++++----------- .../java/com/stripe/net/StripeRequest.java | 8 ++--- .../java/com/stripe/net/FormEncoderTest.java | 18 ++++------ 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index 135e92b50ba..b9498ecfeec 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -22,7 +22,7 @@ public static HttpContent createHttpContent(Map params) throws I return HttpContent.buildFormURLEncodedContent(new ArrayList>()); } - Collection> flatParams = flattenParams(params, false); + Collection> flatParams = flattenParams(params); // If all parameters have been encoded as strings, then the content can be represented // with application/x-www-form-url-encoded encoding. Otherwise, use @@ -56,14 +56,16 @@ public static String createQueryString(Map params) { * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults * to brackets encoding ({@code a[]=1,2}). * @return The query string. + * @deprecated We no longer support the {@code arraysAsRepeated} argument */ + @Deprecated public static String createQueryString(Map params, boolean arraysAsRepeated) { if (params == null) { return ""; } Collection> flatParams = - flattenParams(params, arraysAsRepeated).stream() + flattenParams(params).stream() .filter(kvp -> kvp.getValue() instanceof String) .map(kvp -> new KeyValuePair(kvp.getKey(), (String) kvp.getValue())) .collect(Collectors.toList()); @@ -117,13 +119,11 @@ public static String createQueryString( * } * * @param params The map of parameters. - * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults - * to brackets encoding ({@code a[]=1,2}). * @return The flattened list of parameters. */ public static List> flattenParams( - Map params, boolean arraysAsRepeated) { - return flattenParamsValue(params, null, arraysAsRepeated); + Map params) { + return flattenParamsValue(params, null); } /** @@ -157,12 +157,10 @@ private static String urlEncode(String value) { * * @param value The value for which to create the list of parameters. * @param keyPrefix The key under which new keys should be nested, if any. - * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults - * to brackets encoding ({@code a[]=1,2}). * @return The list of parameters. */ private static List> flattenParamsValue( - Object value, String keyPrefix, boolean arraysAsRepeated) { + Object value, String keyPrefix) { List> flatParams = null; // I wish Java had pattern matching :( @@ -171,7 +169,7 @@ private static List> flattenParamsValue( flatParams = singleParam(keyPrefix, ""); } else if (value instanceof Map) { - flatParams = flattenParamsMap((Map) value, keyPrefix, arraysAsRepeated); + flatParams = flattenParamsMap((Map) value, keyPrefix); } else if (value instanceof String) { flatParams = singleParam(keyPrefix, value); @@ -183,12 +181,12 @@ private static List> flattenParamsValue( flatParams = singleParam(keyPrefix, value); } else if (value instanceof Collection) { - flatParams = flattenParamsCollection((Collection) value, keyPrefix, arraysAsRepeated); + flatParams = flattenParamsCollection((Collection) value, keyPrefix); } else if (value.getClass().isArray()) { Object[] array = getArrayForObject(value); Collection collection = Arrays.stream(array).collect(Collectors.toList()); - flatParams = flattenParamsCollection(collection, keyPrefix, arraysAsRepeated); + flatParams = flattenParamsCollection(collection, keyPrefix); } else if (value.getClass().isEnum()) { flatParams = @@ -211,7 +209,7 @@ private static List> flattenParamsValue( * @return The list of parameters. */ private static List> flattenParamsMap( - Map map, String keyPrefix, boolean arraysAsRepeated) { + Map map, String keyPrefix) { List> flatParams = new ArrayList>(); if (map == null) { return flatParams; @@ -223,7 +221,7 @@ private static List> flattenParamsMap( String newPrefix = newPrefix(key, keyPrefix); - flatParams.addAll(flattenParamsValue(value, newPrefix, arraysAsRepeated)); + flatParams.addAll(flattenParamsValue(value, newPrefix)); } return flatParams; @@ -236,12 +234,10 @@ private static List> flattenParamsMap( * * @param collection The collection for which to create the list of parameters. * @param keyPrefix The key under which new keys should be nested. - * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults - * to brackets encoding ({@code a[]=1,2}). * @return The list of parameters. */ private static List> flattenParamsCollection( - Collection collection, String keyPrefix, boolean arraysAsRepeated) { + Collection collection, String keyPrefix) { List> flatParams = new ArrayList>(); if (collection == null) { return flatParams; @@ -251,14 +247,14 @@ private static List> flattenParamsCollection( for (Object value : collection) { // Always use indexed format for arrays String newPrefix = String.format("%s[%d]", keyPrefix, index); - flatParams.addAll(flattenParamsValue(value, newPrefix, arraysAsRepeated)); + flatParams.addAll(flattenParamsValue(value, newPrefix)); index += 1; } /* Because application/x-www-form-urlencoded cannot represent an empty list, convention * is to take the list parameter and just set it to an empty string. (E.g. A regular * list might look like `a[0]=1&b[1]=2`. Emptying it would look like `a=`.) */ - if (!arraysAsRepeated && flatParams.isEmpty()) { + if (flatParams.isEmpty()) { flatParams.add(new KeyValuePair(keyPrefix, "")); } diff --git a/src/main/java/com/stripe/net/StripeRequest.java b/src/main/java/com/stripe/net/StripeRequest.java index d30d3e4fc6a..a2fe9956806 100644 --- a/src/main/java/com/stripe/net/StripeRequest.java +++ b/src/main/java/com/stripe/net/StripeRequest.java @@ -71,7 +71,7 @@ private StripeRequest( this.params = (params != null) ? Collections.unmodifiableMap(params) : null; this.options = (options != null) ? options : RequestOptions.getDefault(); this.method = method; - this.url = buildURL(method, url, params, apiMode); + this.url = buildURL(method, url, params); this.headers = buildHeaders(method, this.options, this.content, apiMode); this.apiMode = apiMode; } catch (IOException e) { @@ -107,7 +107,7 @@ private StripeRequest( this.params = (params != null) ? Collections.unmodifiableMap(params) : null; this.options = options; this.method = method; - this.url = buildURL(method, url, params, apiMode); + this.url = buildURL(method, url, params); this.content = buildContent(method, params, apiMode); this.headers = buildHeaders(method, this.options, this.content, apiMode); this.apiMode = apiMode; @@ -219,7 +219,7 @@ public StripeRequest withAdditionalHeader(String name, String value) { } private static URL buildURL( - ApiResource.RequestMethod method, String spec, Map params, ApiMode apiMode) + ApiResource.RequestMethod method, String spec, Map params) throws IOException { StringBuilder sb = new StringBuilder(); @@ -230,7 +230,7 @@ private static URL buildURL( if ((method != ApiResource.RequestMethod.POST) && (params != null)) { String queryString = - FormEncoder.createQueryString(params, apiMode == ApiMode.V2 ? true : false); + FormEncoder.createQueryString(params); if (queryString != null && !queryString.isEmpty()) { if (specQueryString != null && !specQueryString.isEmpty()) { diff --git a/src/test/java/com/stripe/net/FormEncoderTest.java b/src/test/java/com/stripe/net/FormEncoderTest.java index c96064f5fc8..add0d692a88 100644 --- a/src/test/java/com/stripe/net/FormEncoderTest.java +++ b/src/test/java/com/stripe/net/FormEncoderTest.java @@ -29,7 +29,6 @@ import javax.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.RequiredArgsConstructor; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; @@ -130,12 +129,10 @@ public void testCreateQueryString() { org.junit.Assume.assumeTrue(!System.getProperty("java.version").startsWith("10.")); @Data - @RequiredArgsConstructor @AllArgsConstructor class TestCase { private final Map data; private final String want; - @Nullable private Boolean arrayAsRepeated = false; } List testCases = @@ -376,18 +373,17 @@ class TestCase { "array", new Object[] {new String[] {"foo", "bar"}, new int[] {1, 2, 3}}), "array[0][0]=foo&array[0][1]=bar&array[1][0]=1&array[1][1]=2&array[1][2]=3")); - // Array (now always uses indexed format) - add(new TestCase(Collections.singletonMap("array", new String[] {}), "", true)); + // Array + add(new TestCase(Collections.singletonMap("array", new String[] {}), "array=")); add( new TestCase( Collections.singletonMap("array", new String[] {"1", "2", "3"}), - "array[0]=1&array[1]=2&array[2]=3", - true)); + "array[0]=1&array[1]=2&array[2]=3" + )); add( new TestCase( Collections.singletonMap("array", new Object[] {123, "foo"}), - "array[0]=123&array[1]=foo", - true)); + "array[0]=123&array[1]=foo")); // Collection add( new TestCase( @@ -431,7 +427,7 @@ class TestCase { for (TestCase testCase : testCases) { assertEquals( testCase.getWant(), - FormEncoder.createQueryString(testCase.getData(), testCase.getArrayAsRepeated())); + FormEncoder.createQueryString(testCase.getData())); } } @@ -481,7 +477,7 @@ class TestCase { }; for (TestCase testCase : testCases) { - assertEquals(testCase.getWant(), FormEncoder.flattenParams(testCase.getData(), false)); + assertEquals(testCase.getWant(), FormEncoder.flattenParams(testCase.getData())); } } From cd78fc6f35ad873005d0800b40ae4ed1c606bb44 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Tue, 11 Nov 2025 11:50:08 -0500 Subject: [PATCH 3/5] Format code --- src/main/java/com/stripe/net/FormEncoder.java | 3 +-- src/main/java/com/stripe/net/StripeRequest.java | 3 +-- src/test/java/com/stripe/net/FormEncoderTest.java | 8 ++------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index b9498ecfeec..abba488dba1 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -121,8 +121,7 @@ public static String createQueryString( * @param params The map of parameters. * @return The flattened list of parameters. */ - public static List> flattenParams( - Map params) { + public static List> flattenParams(Map params) { return flattenParamsValue(params, null); } diff --git a/src/main/java/com/stripe/net/StripeRequest.java b/src/main/java/com/stripe/net/StripeRequest.java index a2fe9956806..89433bfde9f 100644 --- a/src/main/java/com/stripe/net/StripeRequest.java +++ b/src/main/java/com/stripe/net/StripeRequest.java @@ -229,8 +229,7 @@ private static URL buildURL( String specQueryString = specUrl.getQuery(); if ((method != ApiResource.RequestMethod.POST) && (params != null)) { - String queryString = - FormEncoder.createQueryString(params); + String queryString = FormEncoder.createQueryString(params); if (queryString != null && !queryString.isEmpty()) { if (specQueryString != null && !specQueryString.isEmpty()) { diff --git a/src/test/java/com/stripe/net/FormEncoderTest.java b/src/test/java/com/stripe/net/FormEncoderTest.java index add0d692a88..65bc3aefc7d 100644 --- a/src/test/java/com/stripe/net/FormEncoderTest.java +++ b/src/test/java/com/stripe/net/FormEncoderTest.java @@ -26,7 +26,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import javax.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Data; import org.hamcrest.CoreMatchers; @@ -378,8 +377,7 @@ class TestCase { add( new TestCase( Collections.singletonMap("array", new String[] {"1", "2", "3"}), - "array[0]=1&array[1]=2&array[2]=3" - )); + "array[0]=1&array[1]=2&array[2]=3")); add( new TestCase( Collections.singletonMap("array", new Object[] {123, "foo"}), @@ -425,9 +423,7 @@ class TestCase { }; for (TestCase testCase : testCases) { - assertEquals( - testCase.getWant(), - FormEncoder.createQueryString(testCase.getData())); + assertEquals(testCase.getWant(), FormEncoder.createQueryString(testCase.getData())); } } From 743e6f944081029dc8abd787e098cb346cac42e2 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Mon, 17 Nov 2025 12:46:34 -0500 Subject: [PATCH 4/5] Remove deprecated function --- src/main/java/com/stripe/net/FormEncoder.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index abba488dba1..fc75586f05e 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -39,27 +39,14 @@ public static HttpContent createHttpContent(Map params) throws I } } - /** - * Creates the HTTP query string for a given map of parameters. - * - * @param params The map of parameters. - * @return The query string. - */ - public static String createQueryString(Map params) { - return FormEncoder.createQueryString(params, false); - } /** * Creates the HTTP query string for a given map of parameters. * * @param params The map of parameters. - * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults - * to brackets encoding ({@code a[]=1,2}). * @return The query string. - * @deprecated We no longer support the {@code arraysAsRepeated} argument */ - @Deprecated - public static String createQueryString(Map params, boolean arraysAsRepeated) { + public static String createQueryString(Map params) { if (params == null) { return ""; } From 44477397744fae5ab5ce8e4b2d00f9d61ff15527 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Mon, 17 Nov 2025 12:50:11 -0500 Subject: [PATCH 5/5] Fix formatting --- src/main/java/com/stripe/net/FormEncoder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index fc75586f05e..ec608236a3d 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -39,7 +39,6 @@ public static HttpContent createHttpContent(Map params) throws I } } - /** * Creates the HTTP query string for a given map of parameters. *