Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions MIGRATION_V3.md
Original file line number Diff line number Diff line change
@@ -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.
36 changes: 36 additions & 0 deletions MIGRATION_V5.md
Original file line number Diff line number Diff line change
@@ -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();
```
135 changes: 135 additions & 0 deletions MIGRATION_V8.md
Original file line number Diff line number Diff line change
@@ -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<String, Object>`. 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<String, Object> 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<String, String> 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<String, Object> 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<String, String> 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<String, Object> metadata = new HashMap<>();
metadata.put("key", "value");

if (MetadataHelper.isValidMetadata(metadata)) {
// All values are strings
client.customers().create()
.withMetadata((Map<String, String>) (Object) metadata)
.execute();
}
```

### `MetadataHelper.parseMetadataValue(value, type)`

Parse metadata values back to their original types:

```java
Customer customer = client.customers().get("CU123").execute();
Map<String, String> 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<String> tags = MetadataHelper.parseMetadataValue(
metadata.get("tags"),
new TypeToken<List<String>>(){}.getType()
); // ["vip", "premium"]
```
111 changes: 4 additions & 107 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ With Maven:
<dependency>
<groupId>com.gocardless</groupId>
<artifactId>gocardless-pro</artifactId>
<version>7.6.0</version>
<version>8.0.0</version>
</dependency>
```

With Gradle:

```
implementation 'com.gocardless:gocardless-pro:7.6.0'
implementation 'com.gocardless:gocardless-pro:8.0.0'
```

## Initializing the client
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
Loading
Loading