From 319d0b6f966d09eea5e4bbddb297d496d4c68a07 Mon Sep 17 00:00:00 2001 From: "gocardless-ci-robot[bot]" <123969075+gocardless-ci-robot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:14:08 +0000 Subject: [PATCH] Changes generated by 7b3e7d8cb474d6c244f62eb24218f4f8819e5d4e This commit was automatically created from gocardless/client-library-templates@7b3e7d8cb474d6c244f62eb24218f4f8819e5d4e by the `push-files` action. Workflow run: https://github.com/gocardless/client-library-templates/actions/runs/22634265690 --- MIGRATION_V3.md | 69 ++++++ MIGRATION_V5.md | 36 +++ MIGRATION_V8.md | 135 ++++++++++ README.md | 111 +-------- build.gradle | 2 +- .../gocardless/helpers/MetadataHelper.java | 165 +++++++++++++ .../java/com/gocardless/http/HttpClient.java | 4 +- .../gocardless/resources/BillingRequest.java | 28 +-- .../resources/BillingRequestTemplate.java | 12 +- .../resources/BillingRequestWithAction.java | 28 +-- .../resources/CreditorBankAccount.java | 4 +- .../com/gocardless/resources/Customer.java | 4 +- .../resources/CustomerBankAccount.java | 4 +- .../resources/InstalmentSchedule.java | 4 +- .../com/gocardless/resources/Mandate.java | 4 +- .../gocardless/resources/OutboundPayment.java | 4 +- .../resources/PayerAuthorisation.java | 12 +- .../com/gocardless/resources/Payment.java | 4 +- .../java/com/gocardless/resources/Payout.java | 4 +- .../gocardless/resources/RedirectFlow.java | 4 +- .../java/com/gocardless/resources/Refund.java | 4 +- .../gocardless/resources/Subscription.java | 4 +- .../helpers/MetadataHelperTest.java | 231 ++++++++++++++++++ 23 files changed, 705 insertions(+), 172 deletions(-) create mode 100644 MIGRATION_V3.md create mode 100644 MIGRATION_V5.md create mode 100644 MIGRATION_V8.md create mode 100644 src/main/java/com/gocardless/helpers/MetadataHelper.java create mode 100644 src/test/java/com/gocardless/helpers/MetadataHelperTest.java diff --git a/MIGRATION_V3.md b/MIGRATION_V3.md new file mode 100644 index 00000000..43ee3140 --- /dev/null +++ b/MIGRATION_V3.md @@ -0,0 +1,69 @@ +# Upgrading from v2.x to v3.x or above + +To upgrade from v2.x, you will need to switch from calling `GoCardlessClient.create` with arguments to +using the `Builder` returned by `GoCardlessClient.newBuilder()` and its `withX()` and `build()` methods. + +If you're only setting an access token and using the default GoCardless environment (live): + +```java +import com.gocardless.GoCardlessClient; + +String accessToken = "foo"; + +// Before +GoCardlessClient client = GoCardlessClient.create(accessToken) + +// After +GoCardlessClient client = GoCardlessClient.newBuilder(accessToken).build(); +``` + +If you're customising the environment as well: + +```java +import com.gocardless.GoCardlessClient; + +String accessToken = "foo"; + +// Before +GoCardlessClient client = GoCardlessClient.create(accessToken, GoCardlessClient.Environment.SANDBOX) + +// After +GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) + .withEnvironment(GoCardlessClient.Environment.SANDBOX) + .build(); +``` + +Or, if you're customising the base URL: + +```java +import com.gocardless.GoCardlessClient; + +String accessToken = "foo"; +String baseUrl = "https://api.gocardless.com"; + +// Before +GoCardlessClient client = GoCardlessClient.create(accessToken, baseUrl) + +// After +GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) + .withBaseUrl(baseUrl) + .build(); +``` + +If you were instantiating your own `com.gocardless.http.HttpClient` (which is very unlikely unless you +were patching the internals of the library), you'll now need to supply an `OkHttpClient` as well as the +access token and base URL. + +```java +String accessToken = "foo"; +String baseUrl = "https://api.gocardless.com"; + +// Before +HttpClient rawClient = new HttpClient(accessToken, baseUrl); + +// After +HttpClient rawClient = new HttpClient(accessToken, baseUrl, new OkHttpClient()); +``` + +Once you've instantiated the client, everything works exactly the same as before, so you won't need to +change any of your other code. \ No newline at end of file diff --git a/MIGRATION_V5.md b/MIGRATION_V5.md new file mode 100644 index 00000000..196e992a --- /dev/null +++ b/MIGRATION_V5.md @@ -0,0 +1,36 @@ +# Upgrading from v4.x or earlier to v5.x or above + +## Breaking Changes + +- Stop support for apps using Java 7, supports apps using only Java 8 and above +- Response Header names are all in lower case now +- GoCardless client initiation method `withsslSocketFactory` replaced with `withSslSocketFactoryAndTrustManager` + +```java +import com.gocardless.GoCardlessClient; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.security.KeyStore; + +String accessToken = "AO00000123"; +TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( +TrustManagerFactory.getDefaultAlgorithm()); +trustManagerFactory.init((KeyStore) null); +TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); +if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); +} +X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + +SSLContext sslContext = SSLContext.getInstance("TLS"); +sslContext.init(null, new TrustManager[] { trustManager }, null); +SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + +GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) + .withSslSocketFactoryAndTrustManager(sslSocketFactory, trustManager) + .build(); +``` diff --git a/MIGRATION_V8.md b/MIGRATION_V8.md new file mode 100644 index 00000000..93e27e10 --- /dev/null +++ b/MIGRATION_V8.md @@ -0,0 +1,135 @@ +# Migration Guide from v7 or earlier to v8 + +## Breaking Changes + +### Metadata values must be strings + +**Why**: The GoCardless API only accepts string values for metadata, but the Java types previously allowed `Map`. This caused confusing runtime errors. The new version fixes the types to match the API requirements. + +**Impact**: Code that passes non-string values to `metadata` fields will fail to compile. + +--- + +## Quick Migration + +### Option 1: Use MetadataHelper (Recommended) ✅ + +The easiest way to migrate is using the new `MetadataHelper` class: + +```java +import com.gocardless.helpers.MetadataHelper; +import java.util.HashMap; +import java.util.Map; + +// ❌ BEFORE - Compiled but failed at runtime +Map metadata = new HashMap<>(); +metadata.put("user_id", 12345); // Integer +metadata.put("is_active", true); // Boolean +metadata.put("tags", Arrays.asList("vip", "premium")); // List + +client.customers().create() + .withEmail("user@example.com") + .withMetadata(metadata) // Runtime error! + .execute(); + +// ✅ AFTER - One function call +client.customers().create() + .withEmail("user@example.com") + .withMetadata(MetadataHelper.toMetadata(metadata)) // Converts all values + .execute(); +``` + +### Option 2: Manual Conversion + +If you prefer explicit control: + +```java +Map metadata = new HashMap<>(); +metadata.put("user_id", String.valueOf(12345)); // "12345" +metadata.put("is_active", String.valueOf(true)); // "true" + +Gson gson = new Gson(); +metadata.put("tags", gson.toJson(Arrays.asList("vip", "premium"))); // JSON + +client.customers().create() + .withEmail("user@example.com") + .withMetadata(metadata) + .execute(); +``` + +--- + +## Helper Functions Reference + +### `MetadataHelper.toMetadata(obj)` + +Converts a map with mixed value types to metadata format: + +```java +Map data = new HashMap<>(); +data.put("user_id", 12345); +data.put("is_premium", true); +data.put("signup_date", new Date()); +data.put("preferences", Map.of("theme", "dark")); + +Map metadata = MetadataHelper.toMetadata(data); +// Result: { +// "user_id": "12345", +// "is_premium": "true", +// "signup_date": "Mon Jan 15 10:30:00 GMT 2024", +// "preferences": "{\"theme\":\"dark\"}" +// } +``` + +### `MetadataHelper.toMetadataValue(value)` + +Converts a single value: + +```java +MetadataHelper.toMetadataValue(12345); // "12345" +MetadataHelper.toMetadataValue(true); // "true" +MetadataHelper.toMetadataValue(Arrays.asList("vip")); // "[\"vip\"]" +MetadataHelper.toMetadataValue(Map.of("theme", "dark")); // "{\"theme\":\"dark\"}" +``` + +### `MetadataHelper.isValidMetadata(obj)` + +Check if metadata is valid: + +```java +Map metadata = new HashMap<>(); +metadata.put("key", "value"); + +if (MetadataHelper.isValidMetadata(metadata)) { + // All values are strings + client.customers().create() + .withMetadata((Map) (Object) metadata) + .execute(); +} +``` + +### `MetadataHelper.parseMetadataValue(value, type)` + +Parse metadata values back to their original types: + +```java +Customer customer = client.customers().get("CU123").execute(); +Map metadata = customer.getMetadata(); + +// Parse back to original types +Integer userId = MetadataHelper.parseMetadataValue( + metadata.get("user_id"), + Integer.class +); // 12345 + +Boolean isActive = MetadataHelper.parseMetadataValue( + metadata.get("is_active"), + Boolean.class +); // true + +// For generic types, use TypeToken +List tags = MetadataHelper.parseMetadataValue( + metadata.get("tags"), + new TypeToken>(){}.getType() +); // ["vip", "premium"] +``` diff --git a/README.md b/README.md index d1917a9e..3f45f0a9 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ With Maven: com.gocardless gocardless-pro - 7.6.0 + 8.0.0 ``` With Gradle: ``` -implementation 'com.gocardless:gocardless-pro:7.6.0' +implementation 'com.gocardless:gocardless-pro:8.0.0' ``` ## Initializing the client @@ -228,112 +228,9 @@ public class WebhookHandler { For more details on working with webhooks, see our ["Getting started" guide](https://developer.gocardless.com/getting-started/api/introduction/?lang=java). -## Upgrading from v4.x to v5.x or above +## Upgrading from older versions -### Breaking Changes - -- Stop support for apps using Java 7, supports apps using only Java 8 and above -- Response Header names are all in lower case now -- GoCardless client initiation method `withsslSocketFactory` replaced with `withSslSocketFactoryAndTrustManager` - -```java -import com.gocardless.GoCardlessClient; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import java.security.KeyStore; - -String accessToken = "AO00000123"; -TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( -TrustManagerFactory.getDefaultAlgorithm()); -trustManagerFactory.init((KeyStore) null); -TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); -if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { - throw new IllegalStateException("Unexpected default trust managers:" - + Arrays.toString(trustManagers)); -} -X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; - -SSLContext sslContext = SSLContext.getInstance("TLS"); -sslContext.init(null, new TrustManager[] { trustManager }, null); -SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - -GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) - .withSslSocketFactoryAndTrustManager(sslSocketFactory, trustManager) - .build(); -``` - -## Upgrading from v2.x to v3.x or above - -To upgrade from v2.x, you will need to switch from calling `GoCardlessClient.create` with arguments to -using the `Builder` returned by `GoCardlessClient.newBuilder()` and its `withX()` and `build()` methods. - -If you're only setting an access token and using the default GoCardless environment (live): - -```java -import com.gocardless.GoCardlessClient; - -String accessToken = "foo"; - -// Before -GoCardlessClient client = GoCardlessClient.create(accessToken) - -// After -GoCardlessClient client = GoCardlessClient.newBuilder(accessToken).build(); -``` - -If you're customising the environment as well: - -```java -import com.gocardless.GoCardlessClient; - -String accessToken = "foo"; - -// Before -GoCardlessClient client = GoCardlessClient.create(accessToken, GoCardlessClient.Environment.SANDBOX) - -// After -GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) - .withEnvironment(GoCardlessClient.Environment.SANDBOX) - .build(); -``` - -Or, if you're customising the base URL: - -```java -import com.gocardless.GoCardlessClient; - -String accessToken = "foo"; -String baseUrl = "https://api.gocardless.com"; - -// Before -GoCardlessClient client = GoCardlessClient.create(accessToken, baseUrl) - -// After -GoCardlessClient client = GoCardlessClient.newBuilder(accessToken) - .withBaseUrl(baseUrl) - .build(); -``` - -If you were instantiating your own `com.gocardless.http.HttpClient` (which is very unlikely unless you -were patching the internals of the library), you'll now need to supply an `OkHttpClient` as well as the -access token and base URL. - -```java -String accessToken = "foo"; -String baseUrl = "https://api.gocardless.com"; - -// Before -HttpClient rawClient = new HttpClient(accessToken, baseUrl); - -// After -HttpClient rawClient = new HttpClient(accessToken, baseUrl, new OkHttpClient()); -``` - -Once you've instantiated the client, everything works exactly the same as before, so you won't need to -change any of your other code. +If you're upgrading from v7 or earlier to v8 or later, see: MIGRATION_V8.md ## Compatibility diff --git a/build.gradle b/build.gradle index 405367dc..88e30383 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ plugins { sourceCompatibility = 1.8 targetCompatibility = 1.8 group = 'com.gocardless' -version = '7.6.0' +version = '8.0.0' apply plugin: 'ch.raffael.pegdown-doclet' diff --git a/src/main/java/com/gocardless/helpers/MetadataHelper.java b/src/main/java/com/gocardless/helpers/MetadataHelper.java new file mode 100644 index 00000000..37e4539f --- /dev/null +++ b/src/main/java/com/gocardless/helpers/MetadataHelper.java @@ -0,0 +1,165 @@ +package com.gocardless.helpers; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.HashMap; +import java.util.Map; + +/** + * Helper utilities for working with metadata fields. + * + *

+ * The GoCardless API requires metadata values to be strings. These helpers make it easy to convert + * other types to strings. + * + * @since 7.0.0 + */ +public class MetadataHelper { + private static final Gson GSON = new GsonBuilder().create(); + + /** + * Converts any value to a string suitable for metadata. + * + *

+ * Examples: + * + *

+     * toMetadataValue(12345)                  // "12345"
+     * toMetadataValue(true)                   // "true"
+     * toMetadataValue(Arrays.asList("a","b")) // "[\"a\",\"b\"]"
+     * 
+ * + * @param value the value to convert + * @return the string representation of the value + */ + public static String toMetadataValue(Object value) { + if (value == null) { + return "null"; + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof Number || value instanceof Boolean) { + return value.toString(); + } + // For complex types (Collections, Maps, custom objects), serialize to JSON + return GSON.toJson(value); + } + + /** + * Converts a map with mixed value types to a metadata-compatible format. + * + *

+ * This is useful for converting {@code Map} to {@code Map} + * where all values are automatically converted to strings. + * + *

+ * Example: + * + *

+     * Map<String, Object> data = new HashMap<>();
+     * data.put("user_id", 12345);
+     * data.put("is_active", true);
+     * data.put("tags", Arrays.asList("vip", "premium"));
+     * Map<String, String> metadata = MetadataHelper.toMetadata(data);
+     * // Result: {
+     * // "user_id": "12345",
+     * // "is_active": "true",
+     * // "tags": "[\"vip\",\"premium\"]"
+     * // }
+     * 
+ * + * @param obj the map to convert + * @return a map with all values converted to strings + */ + public static Map toMetadata(Map obj) { + Map result = new HashMap<>(obj.size()); + for (Map.Entry entry : obj.entrySet()) { + result.put(entry.getKey(), toMetadataValue(entry.getValue())); + } + return result; + } + + /** + * Checks if a map contains only string values. + * + *

+ * This can be used to verify that metadata is in the correct format before sending it to the + * API. + * + * @param obj the map to check + * @return true if all values are strings, false otherwise + */ + public static boolean isValidMetadata(Map obj) { + return obj.values().stream().allMatch(String.class::isInstance); + } + + /** + * Parses a metadata string value back to a specific type. + * + *

+ * Examples: + * + *

+     * Integer userId = MetadataHelper.parseMetadataValue("12345", Integer.class); // 12345
+     * Boolean isActive = MetadataHelper.parseMetadataValue("true", Boolean.class); // true
+     * List<String> tags = MetadataHelper.parseMetadataValue("[\"vip\"]",
+     *         new TypeToken<List<String>>() {}.getType()); // ["vip"]
+     * 
+ * + * @param value the string value to parse + * @param type the target type class + * @param the type to parse to + * @return the parsed value + * @throws com.google.gson.JsonSyntaxException if the value cannot be parsed + */ + public static T parseMetadataValue(String value, Class type) { + if (type == String.class) { + return type.cast(value); + } + if (type == Integer.class) { + return type.cast(Integer.valueOf(value)); + } + if (type == Long.class) { + return type.cast(Long.valueOf(value)); + } + if (type == Double.class) { + return type.cast(Double.valueOf(value)); + } + if (type == Float.class) { + return type.cast(Float.valueOf(value)); + } + if (type == Boolean.class) { + return type.cast(Boolean.valueOf(value)); + } + // For complex types, parse from JSON + return GSON.fromJson(value, type); + } + + /** + * Parses a metadata string value back to a specific type using a Gson TypeToken. + * + *

+ * This is useful for parsing generic types like {@code List}. + * + *

+ * Example: + * + *

+     * List<String> tags = MetadataHelper.parseMetadataValue("[\"vip\",\"premium\"]",
+     *         new TypeToken<List<String>>() {}.getType());
+     * 
+ * + * @param value the string value to parse + * @param type the target type (use {@code new TypeToken(){}.getType()}) + * @param the type to parse to + * @return the parsed value + * @throws com.google.gson.JsonSyntaxException if the value cannot be parsed + */ + public static T parseMetadataValue(String value, java.lang.reflect.Type type) { + if (type == String.class) { + return (T) value; + } + return GSON.fromJson(value, type); + } +} diff --git a/src/main/java/com/gocardless/http/HttpClient.java b/src/main/java/com/gocardless/http/HttpClient.java index 7f26128e..8f8fd6e2 100644 --- a/src/main/java/com/gocardless/http/HttpClient.java +++ b/src/main/java/com/gocardless/http/HttpClient.java @@ -35,7 +35,7 @@ public class HttpClient { private static final String DISALLOWED_USER_AGENT_CHARACTERS = "[^\\w!#$%&'\\*\\+\\-\\.\\^`\\|~]"; private static final String USER_AGENT = - String.format("gocardless-pro-java/7.6.0 java/%s %s/%s %s/%s", + String.format("gocardless-pro-java/8.0.0 java/%s %s/%s %s/%s", cleanUserAgentToken(System.getProperty("java.vm.specification.version")), cleanUserAgentToken(System.getProperty("java.vm.name")), cleanUserAgentToken(System.getProperty("java.version")), @@ -49,7 +49,7 @@ public class HttpClient { builder.put("GoCardless-Version", "2015-07-06"); builder.put("Accept", "application/json"); builder.put("GoCardless-Client-Library", "gocardless-pro-java"); - builder.put("GoCardless-Client-Version", "7.6.0"); + builder.put("GoCardless-Client-Version", "8.0.0"); HEADERS = builder.build(); } private final OkHttpClient rawClient; diff --git a/src/main/java/com/gocardless/resources/BillingRequest.java b/src/main/java/com/gocardless/resources/BillingRequest.java index b687bd20..a5f754bd 100644 --- a/src/main/java/com/gocardless/resources/BillingRequest.java +++ b/src/main/java/com/gocardless/resources/BillingRequest.java @@ -32,7 +32,7 @@ private BillingRequest() { private InstalmentScheduleRequest instalmentScheduleRequest; private Links links; private MandateRequest mandateRequest; - private Map metadata; + private Map metadata; private PaymentContextCode paymentContextCode; private String paymentPurposeCode; private PaymentRequest paymentRequest; @@ -104,7 +104,7 @@ public MandateRequest getMandateRequest() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -480,7 +480,7 @@ private InstalmentScheduleRequest() { private List instalmentsWithDates; private InstalmentsWithSchedule instalmentsWithSchedule; private Links links; - private Map metadata; + private Map metadata; private String name; private String paymentReference; private Boolean retryIfPossible; @@ -529,7 +529,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -856,7 +856,7 @@ private MandateRequest() { private String description; private FundsSettlement fundsSettlement; private Links links; - private Map metadata; + private Map metadata; private Boolean payerRequestedDualSignature; private String scheme; private Boolean sweeping; @@ -930,7 +930,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1193,7 +1193,7 @@ private PaymentRequest() { private String description; private FundsSettlement fundsSettlement; private Links links; - private Map metadata; + private Map metadata; private String reference; private String scheme; @@ -1251,7 +1251,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1357,7 +1357,7 @@ private Customer() { private String givenName; private String id; private String language; - private Map metadata; + private Map metadata; private String phoneNumber; /** @@ -1423,7 +1423,7 @@ public String getLanguage() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1457,7 +1457,7 @@ private CustomerBankAccount() { private Boolean enabled; private String id; private Links links; - private Map metadata; + private Map metadata; /** * Name of the account holder, as known by the bank. The full name provided when the @@ -1549,7 +1549,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1730,7 +1730,7 @@ private SubscriptionRequest() { private Integer interval; private IntervalUnit intervalUnit; private Links links; - private Map metadata; + private Map metadata; private Month month; private String name; private String paymentReference; @@ -1799,7 +1799,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/BillingRequestTemplate.java b/src/main/java/com/gocardless/resources/BillingRequestTemplate.java index 9fbc6d7f..8dcf681d 100644 --- a/src/main/java/com/gocardless/resources/BillingRequestTemplate.java +++ b/src/main/java/com/gocardless/resources/BillingRequestTemplate.java @@ -29,15 +29,15 @@ private BillingRequestTemplate() { private MandateRequestConstraints mandateRequestConstraints; private String mandateRequestCurrency; private String mandateRequestDescription; - private Map mandateRequestMetadata; + private Map mandateRequestMetadata; private String mandateRequestScheme; private MandateRequestVerify mandateRequestVerify; - private Map metadata; + private Map metadata; private String name; private String paymentRequestAmount; private String paymentRequestCurrency; private String paymentRequestDescription; - private Map paymentRequestMetadata; + private Map paymentRequestMetadata; private String paymentRequestScheme; private String redirectUri; private String updatedAt; @@ -93,7 +93,7 @@ public String getMandateRequestDescription() { * is fulfilled. Up to 3 keys are permitted, with key names up to 50 characters and values up to * 500 characters. */ - public Map getMandateRequestMetadata() { + public Map getMandateRequestMetadata() { return mandateRequestMetadata; } @@ -138,7 +138,7 @@ public MandateRequestVerify getMandateRequestVerify() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -180,7 +180,7 @@ public String getPaymentRequestDescription() { * is fulfilled. Up to 3 keys are permitted, with key names up to 50 characters and values up to * 500 characters. */ - public Map getPaymentRequestMetadata() { + public Map getPaymentRequestMetadata() { return paymentRequestMetadata; } diff --git a/src/main/java/com/gocardless/resources/BillingRequestWithAction.java b/src/main/java/com/gocardless/resources/BillingRequestWithAction.java index 6a580257..c4e1cd85 100644 --- a/src/main/java/com/gocardless/resources/BillingRequestWithAction.java +++ b/src/main/java/com/gocardless/resources/BillingRequestWithAction.java @@ -233,7 +233,7 @@ private BillingRequests() { private InstalmentScheduleRequest instalmentScheduleRequest; private Links links; private MandateRequest mandateRequest; - private Map metadata; + private Map metadata; private PaymentContextCode paymentContextCode; private String paymentPurposeCode; private PaymentRequest paymentRequest; @@ -305,7 +305,7 @@ public MandateRequest getMandateRequest() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -682,7 +682,7 @@ private InstalmentScheduleRequest() { private List instalmentsWithDates; private InstalmentsWithSchedule instalmentsWithSchedule; private Links links; - private Map metadata; + private Map metadata; private String name; private String paymentReference; private Boolean retryIfPossible; @@ -731,7 +731,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1058,7 +1058,7 @@ private MandateRequest() { private String description; private FundsSettlement fundsSettlement; private Links links; - private Map metadata; + private Map metadata; private Boolean payerRequestedDualSignature; private String scheme; private Boolean sweeping; @@ -1134,7 +1134,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1402,7 +1402,7 @@ private PaymentRequest() { private String description; private FundsSettlement fundsSettlement; private Links links; - private Map metadata; + private Map metadata; private String reference; private String scheme; @@ -1460,7 +1460,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1567,7 +1567,7 @@ private Customer() { private String givenName; private String id; private String language; - private Map metadata; + private Map metadata; private String phoneNumber; /** @@ -1634,7 +1634,7 @@ public String getLanguage() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to * 50 characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1668,7 +1668,7 @@ private CustomerBankAccount() { private Boolean enabled; private String id; private Links links; - private Map metadata; + private Map metadata; /** * Name of the account holder, as known by the bank. The full name provided when the @@ -1761,7 +1761,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to * 50 characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -1943,7 +1943,7 @@ private SubscriptionRequest() { private Integer interval; private IntervalUnit intervalUnit; private Links links; - private Map metadata; + private Map metadata; private Month month; private String name; private String paymentReference; @@ -2013,7 +2013,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/CreditorBankAccount.java b/src/main/java/com/gocardless/resources/CreditorBankAccount.java index 3b657ab3..63a81e00 100644 --- a/src/main/java/com/gocardless/resources/CreditorBankAccount.java +++ b/src/main/java/com/gocardless/resources/CreditorBankAccount.java @@ -33,7 +33,7 @@ private CreditorBankAccount() { private Boolean enabled; private String id; private Links links; - private Map metadata; + private Map metadata; private VerificationStatus verificationStatus; /** @@ -115,7 +115,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Customer.java b/src/main/java/com/gocardless/resources/Customer.java index 10de5c04..018d7fd1 100644 --- a/src/main/java/com/gocardless/resources/Customer.java +++ b/src/main/java/com/gocardless/resources/Customer.java @@ -27,7 +27,7 @@ private Customer() { private String givenName; private String id; private String language; - private Map metadata; + private Map metadata; private String phoneNumber; private String postalCode; private String region; @@ -138,7 +138,7 @@ public String getLanguage() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/CustomerBankAccount.java b/src/main/java/com/gocardless/resources/CustomerBankAccount.java index dfbe8d88..ee7d762d 100644 --- a/src/main/java/com/gocardless/resources/CustomerBankAccount.java +++ b/src/main/java/com/gocardless/resources/CustomerBankAccount.java @@ -35,7 +35,7 @@ private CustomerBankAccount() { private Boolean enabled; private String id; private Links links; - private Map metadata; + private Map metadata; /** * Name of the account holder, as known by the bank. The full name provided when the customer is @@ -125,7 +125,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/InstalmentSchedule.java b/src/main/java/com/gocardless/resources/InstalmentSchedule.java index 4c6a06aa..47135d25 100644 --- a/src/main/java/com/gocardless/resources/InstalmentSchedule.java +++ b/src/main/java/com/gocardless/resources/InstalmentSchedule.java @@ -28,7 +28,7 @@ private InstalmentSchedule() { private Currency currency; private String id; private Links links; - private Map metadata; + private Map metadata; private String name; private Map paymentErrors; private Status status; @@ -67,7 +67,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Mandate.java b/src/main/java/com/gocardless/resources/Mandate.java index 45d74fdc..f87eaab2 100644 --- a/src/main/java/com/gocardless/resources/Mandate.java +++ b/src/main/java/com/gocardless/resources/Mandate.java @@ -23,7 +23,7 @@ private Mandate() { private FundsSettlement fundsSettlement; private String id; private Links links; - private Map metadata; + private Map metadata; private String nextPossibleChargeDate; private String nextPossibleStandardAchChargeDate; private Boolean paymentsRequireApproval; @@ -96,7 +96,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/OutboundPayment.java b/src/main/java/com/gocardless/resources/OutboundPayment.java index 3ccbf976..e4f14491 100644 --- a/src/main/java/com/gocardless/resources/OutboundPayment.java +++ b/src/main/java/com/gocardless/resources/OutboundPayment.java @@ -32,7 +32,7 @@ private OutboundPayment() { private String id; private Boolean isWithdrawal; private Links links; - private Map metadata; + private Map metadata; private String reference; private Scheme scheme; private Status status; @@ -99,7 +99,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/PayerAuthorisation.java b/src/main/java/com/gocardless/resources/PayerAuthorisation.java index 1b4bc3a9..6efbbe11 100644 --- a/src/main/java/com/gocardless/resources/PayerAuthorisation.java +++ b/src/main/java/com/gocardless/resources/PayerAuthorisation.java @@ -161,7 +161,7 @@ private BankAccount() { private String countryCode; private String currency; private String iban; - private Map metadata; + private Map metadata; /** * Name of the account holder, as known by the bank. The full name provided when the @@ -253,7 +253,7 @@ public String getIban() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -286,7 +286,7 @@ private Customer() { private String familyName; private String givenName; private String locale; - private Map metadata; + private Map metadata; private String postalCode; private String region; private String swedishIdentityNumber; @@ -380,7 +380,7 @@ public String getLocale() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } @@ -493,7 +493,7 @@ private Mandate() { // blank to prevent instantiation } - private Map metadata; + private Map metadata; private String payerIpAddress; private String reference; private Scheme scheme; @@ -502,7 +502,7 @@ private Mandate() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Payment.java b/src/main/java/com/gocardless/resources/Payment.java index bf545ee7..f93f45dd 100644 --- a/src/main/java/com/gocardless/resources/Payment.java +++ b/src/main/java/com/gocardless/resources/Payment.java @@ -28,7 +28,7 @@ private Payment() { private Fx fx; private String id; private Links links; - private Map metadata; + private Map metadata; private String reference; private Boolean retryIfPossible; private String scheme; @@ -111,7 +111,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Payout.java b/src/main/java/com/gocardless/resources/Payout.java index 619f161b..9d087c32 100644 --- a/src/main/java/com/gocardless/resources/Payout.java +++ b/src/main/java/com/gocardless/resources/Payout.java @@ -24,7 +24,7 @@ private Payout() { private Fx fx; private String id; private Links links; - private Map metadata; + private Map metadata; private PayoutType payoutType; private String reference; private Status status; @@ -99,7 +99,7 @@ public Links getLinks() { * characters and values up to 500 characters. _Note:_ This should not be used for storing PII * data. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/RedirectFlow.java b/src/main/java/com/gocardless/resources/RedirectFlow.java index 5e0eaca7..b5dea604 100644 --- a/src/main/java/com/gocardless/resources/RedirectFlow.java +++ b/src/main/java/com/gocardless/resources/RedirectFlow.java @@ -52,7 +52,7 @@ private RedirectFlow() { private String id; private Links links; private String mandateReference; - private Map metadata; + private Map metadata; private String redirectUrl; private Scheme scheme; private String sessionToken; @@ -108,7 +108,7 @@ public String getMandateReference() { * characters and values up to 500 characters. _Note:_ This should not be used for storing PII * data. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Refund.java b/src/main/java/com/gocardless/resources/Refund.java index 76ad59da..787b4ad2 100644 --- a/src/main/java/com/gocardless/resources/Refund.java +++ b/src/main/java/com/gocardless/resources/Refund.java @@ -23,7 +23,7 @@ private Refund() { private Fx fx; private String id; private Links links; - private Map metadata; + private Map metadata; private String reference; private Status status; @@ -68,7 +68,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/main/java/com/gocardless/resources/Subscription.java b/src/main/java/com/gocardless/resources/Subscription.java index fb0550c7..69729820 100644 --- a/src/main/java/com/gocardless/resources/Subscription.java +++ b/src/main/java/com/gocardless/resources/Subscription.java @@ -58,7 +58,7 @@ private Subscription() { private Integer interval; private IntervalUnit intervalUnit; private Links links; - private Map metadata; + private Map metadata; private Month month; private String name; private Boolean parentPlanPaused; @@ -166,7 +166,7 @@ public Links getLinks() { * Key-value store of custom data. Up to 3 keys are permitted, with key names up to 50 * characters and values up to 500 characters. */ - public Map getMetadata() { + public Map getMetadata() { return metadata; } diff --git a/src/test/java/com/gocardless/helpers/MetadataHelperTest.java b/src/test/java/com/gocardless/helpers/MetadataHelperTest.java new file mode 100644 index 00000000..8fa664bb --- /dev/null +++ b/src/test/java/com/gocardless/helpers/MetadataHelperTest.java @@ -0,0 +1,231 @@ +package com.gocardless.helpers; + +import static org.junit.Assert.*; + +import com.google.gson.reflect.TypeToken; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class MetadataHelperTest { + @Test + public void testToMetadataValue_string() { + assertEquals("hello", MetadataHelper.toMetadataValue("hello")); + assertEquals("", MetadataHelper.toMetadataValue("")); + } + + @Test + public void testToMetadataValue_integers() { + assertEquals("12345", MetadataHelper.toMetadataValue(12345)); + assertEquals("0", MetadataHelper.toMetadataValue(0)); + assertEquals("-42", MetadataHelper.toMetadataValue(-42)); + assertEquals("9223372036854775807", MetadataHelper.toMetadataValue(9223372036854775807L)); + } + + @Test + public void testToMetadataValue_floats() { + assertEquals("3.14", MetadataHelper.toMetadataValue(3.14f)); + assertEquals("3.14159", MetadataHelper.toMetadataValue(3.14159)); + assertEquals("0.0", MetadataHelper.toMetadataValue(0.0)); + } + + @Test + public void testToMetadataValue_booleans() { + assertEquals("true", MetadataHelper.toMetadataValue(true)); + assertEquals("false", MetadataHelper.toMetadataValue(false)); + } + + @Test + public void testToMetadataValue_null() { + assertEquals("null", MetadataHelper.toMetadataValue(null)); + } + + @Test + public void testToMetadataValue_list() { + List list = Arrays.asList("vip", "premium"); + String result = MetadataHelper.toMetadataValue(list); + assertEquals("[\"vip\",\"premium\"]", result); + } + + @Test + public void testToMetadataValue_emptyList() { + List list = Arrays.asList(); + String result = MetadataHelper.toMetadataValue(list); + assertEquals("[]", result); + } + + @Test + public void testToMetadataValue_map() { + Map map = new HashMap<>(); + map.put("theme", "dark"); + String result = MetadataHelper.toMetadataValue(map); + assertEquals("{\"theme\":\"dark\"}", result); + } + + @Test + public void testToMetadataValue_emptyMap() { + Map map = new HashMap<>(); + String result = MetadataHelper.toMetadataValue(map); + assertEquals("{}", result); + } + + @Test + public void testToMetadata_mixedTypes() { + Map input = new HashMap<>(); + input.put("user_id", 12345); + input.put("is_active", true); + input.put("name", "John"); + Map result = MetadataHelper.toMetadata(input); + assertEquals(3, result.size()); + assertEquals("12345", result.get("user_id")); + assertEquals("true", result.get("is_active")); + assertEquals("John", result.get("name")); + } + + @Test + public void testToMetadata_withArraysAndObjects() { + Map input = new HashMap<>(); + input.put("tags", Arrays.asList("vip", "premium")); + Map prefs = new HashMap<>(); + prefs.put("theme", "dark"); + input.put("prefs", prefs); + Map result = MetadataHelper.toMetadata(input); + assertEquals(2, result.size()); + assertEquals("[\"vip\",\"premium\"]", result.get("tags")); + assertEquals("{\"theme\":\"dark\"}", result.get("prefs")); + } + + @Test + public void testToMetadata_emptyMap() { + Map input = new HashMap<>(); + Map result = MetadataHelper.toMetadata(input); + assertTrue(result.isEmpty()); + } + + @Test + public void testToMetadata_alreadyStrings() { + Map input = new HashMap<>(); + input.put("key1", "value1"); + input.put("key2", "value2"); + Map result = MetadataHelper.toMetadata(input); + assertEquals(2, result.size()); + assertEquals("value1", result.get("key1")); + assertEquals("value2", result.get("key2")); + } + + @Test + public void testToMetadata_nullValues() { + Map input = new HashMap<>(); + input.put("nullable", null); + Map result = MetadataHelper.toMetadata(input); + assertEquals(1, result.size()); + assertEquals("null", result.get("nullable")); + } + + @Test + public void testIsValidMetadata_valid() { + Map metadata = new HashMap<>(); + metadata.put("key1", "value1"); + metadata.put("key2", "value2"); + assertTrue(MetadataHelper.isValidMetadata(metadata)); + } + + @Test + public void testIsValidMetadata_invalidWithInt() { + Map metadata = new HashMap<>(); + metadata.put("key1", "value1"); + metadata.put("key2", 12345); + assertFalse(MetadataHelper.isValidMetadata(metadata)); + } + + @Test + public void testIsValidMetadata_invalidWithBoolean() { + Map metadata = new HashMap<>(); + metadata.put("key1", "value1"); + metadata.put("key2", true); + assertFalse(MetadataHelper.isValidMetadata(metadata)); + } + + @Test + public void testIsValidMetadata_invalidWithMap() { + Map metadata = new HashMap<>(); + metadata.put("key1", "value1"); + metadata.put("key2", new HashMap<>()); + assertFalse(MetadataHelper.isValidMetadata(metadata)); + } + + @Test + public void testIsValidMetadata_emptyMap() { + Map metadata = new HashMap<>(); + assertTrue(MetadataHelper.isValidMetadata(metadata)); + } + + @Test + public void testParseMetadataValue_string() { + String result = MetadataHelper.parseMetadataValue("hello", String.class); + assertEquals("hello", result); + } + + @Test + public void testParseMetadataValue_integer() { + Integer result = MetadataHelper.parseMetadataValue("12345", Integer.class); + assertEquals(Integer.valueOf(12345), result); + } + + @Test + public void testParseMetadataValue_long() { + Long result = MetadataHelper.parseMetadataValue("9223372036854775807", Long.class); + assertEquals(Long.valueOf(9223372036854775807L), result); + } + + @Test + public void testParseMetadataValue_double() { + Double result = MetadataHelper.parseMetadataValue("3.14", Double.class); + assertEquals(Double.valueOf(3.14), result); + } + + @Test + public void testParseMetadataValue_float() { + Float result = MetadataHelper.parseMetadataValue("3.14", Float.class); + assertEquals(Float.valueOf(3.14f), result); + } + + @Test + public void testParseMetadataValue_boolean() { + Boolean result = MetadataHelper.parseMetadataValue("true", Boolean.class); + assertEquals(Boolean.TRUE, result); + result = MetadataHelper.parseMetadataValue("false", Boolean.class); + assertEquals(Boolean.FALSE, result); + } + + @Test + public void testParseMetadataValue_list() { + List result = MetadataHelper.parseMetadataValue("[\"vip\",\"premium\"]", + new TypeToken>() {}.getType()); + assertEquals(2, result.size()); + assertEquals("vip", result.get(0)); + assertEquals("premium", result.get(1)); + } + + @Test + public void testParseMetadataValue_map() { + Map result = MetadataHelper.parseMetadataValue("{\"theme\":\"dark\"}", + new TypeToken>() {}.getType()); + assertEquals(1, result.size()); + assertEquals("dark", result.get("theme")); + } + + @Test(expected = NumberFormatException.class) + public void testParseMetadataValue_invalidInt() { + MetadataHelper.parseMetadataValue("not-a-number", Integer.class); + } + + @Test + public void testParseMetadataValue_invalidBooleanDefaultsFalse() { + // Boolean.valueOf returns false for invalid strings + Boolean result = MetadataHelper.parseMetadataValue("not-a-bool", Boolean.class); + assertEquals(Boolean.FALSE, result); + } +}