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
5 changes: 5 additions & 0 deletions .github/scripts/resolve-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Resolves the library version from a release tag: "v1.0.83" and "1.0.83" both become "1.0.83".
set -euo pipefail
tag="${1:?usage: resolve-version.sh <tag>}"
echo "VERSION=${tag#v}" >> "$GITHUB_ENV"
68 changes: 61 additions & 7 deletions .github/workflows/craftgate-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,77 @@ name: Craftgate Java CI
on: [ push, pull_request ]

jobs:
build:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java-version: [ '8', '11', '17', '21', '25' ]
name: Build with Java ${{ matrix.java-version }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v7

- name: Set up JDK 1.8
uses: actions/setup-java@v1
# The library is compiled with Java 8 (the last listed version becomes JAVA_HOME);
# the tests run on the matrix version.
- name: Set up JDK ${{ matrix.java-version }} and JDK 8
uses: actions/setup-java@v6
with:
java-version: 1.8
distribution: 'zulu'
java-version: |
${{ matrix.java-version }}
8

- name: Cache Gradle packages
uses: actions/cache@v3
uses: actions/cache@v6
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle

- name: Build with Gradle
run: ./gradlew build test
run: |
TEST_JAVA_HOME=$(printenv "JAVA_HOME_${{ matrix.java-version }}_X64")
"$TEST_JAVA_HOME/bin/java" -version
./gradlew build test -PtestJavaHome="$TEST_JAVA_HOME"

release-dry-run:
runs-on: ubuntu-latest
name: Release dry run

steps:
- uses: actions/checkout@v7

- name: Set up JDK 8
uses: actions/setup-java@v6
with:
distribution: 'zulu'
java-version: '8'

# Same version resolution as the release workflow, with a fake "v" prefixed tag.
- name: Resolve version from tag
run: .github/scripts/resolve-version.sh v0.0.0-dryrun

- name: Build release artifacts without signing or publishing
run: |
test "$VERSION" = "0.0.0-dryrun"
./gradlew clean test publishToMavenLocal -Pversion="$VERSION" -x signMavenJavaPublication

- name: Verify the version in the artifacts
run: |
dir=~/.m2/repository/io/craftgate/craftgate/$VERSION
ls "$dir"
grep -q "<version>$VERSION</version>" "$dir/craftgate-$VERSION.pom"
unzip -p "$dir/craftgate-$VERSION.jar" io/craftgate/craftgate-version.properties | grep -qx "version=$VERSION"

# Single "build" status for branch protection: passes only if every job above passed.
build:
if: always()
needs: [ test, release-dry-run ]
runs-on: ubuntu-latest

steps:
- name: Check results
run: |
test "${{ needs.test.result }}" = "success"
test "${{ needs.release-dry-run.result }}" = "success"
16 changes: 11 additions & 5 deletions .github/workflows/craftgate-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v7

- name: Set up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v6
with:
java-version: 1.8
distribution: 'zulu'
java-version: '8'

- name: Cache Gradle packages
uses: actions/cache@v3
uses: actions/cache@v6
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
restore-keys: ${{ runner.os }}-gradle

- name: Resolve version from tag
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: .github/scripts/resolve-version.sh "$TAG_NAME"

- name: Build and Publish with Gradle
run: openssl enc -aes-256-cbc -md sha256 -pass pass:${{secrets.ENC_KEY}} -d -A -in craftgate.gpg.enc -out craftgate.gpg &&
./gradlew clean &&
./gradlew publish -Dorg.gradle.internal.http.socketTimeout=120000 -Dorg.gradle.internal.http.connectionTimeout=120000 -Pversion=${{github.event.release.tag_name}} -PrepoUser=${{secrets.CP_TOKEN_USERNAME}} -PrepoPassword=${{secrets.CP_TOKEN_PASSWORD}} -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}}
./gradlew publish -Dorg.gradle.internal.http.socketTimeout=120000 -Dorg.gradle.internal.http.connectionTimeout=120000 -Pversion=${VERSION} -PrepoUser=${{secrets.CP_TOKEN_USERNAME}} -PrepoPassword=${{secrets.CP_TOKEN_PASSWORD}} -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}}

- name: Trigger deployment visibility
run: |
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ build

.idea
out/

# OS and IDE files
.DS_Store
bin/
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,23 @@ This repo contains the Java client for Craftgate API.
- Java 1.8 or newer

## Installation
Replace `VERSION` with the latest version shown on the Maven Central badge above.

Apache Maven:
```bash
```xml
<dependency>
<groupId>io.craftgate</groupId>
<artifactId>craftgate</artifactId>
<version>1.0.82</version>
<version>VERSION</version>
</dependency>
```
Gradle Groovy DSL
```
implementation 'io.craftgate:craftgate:1.0.80'
implementation 'io.craftgate:craftgate:VERSION'
```
Gradle Kotlin DSL
```
implementation("io.craftgate:craftgate:1.0.80")
implementation("io.craftgate:craftgate:VERSION")
```

## Usage
Expand All @@ -41,7 +43,7 @@ Craftgate craftgate = new Craftgate("<YOUR API KEY>", "<YOUR SECRET KEY>");

```

By default the Craftgate client connects to the production API servers at `https://api.craftgate.io`. For testing purposes, please use the sandbox URL `https://sandbox-api.craftgate.io` using the .
By default the Craftgate client connects to the production API servers at `https://api.craftgate.io`. For testing purposes, please use the sandbox URL `https://sandbox-api.craftgate.io`.

```java

Expand All @@ -51,10 +53,21 @@ Craftgate craftgate = new Craftgate("<YOUR API KEY>", "<YOUR SECRET KEY>", "http
```

## Examples
Included in the project are a number of examples that cover almost all use-cases. Refer to [the `sample/` folder](./src/test/java/io/craftgate/sample)] for more info.
Included in the project are a number of examples that cover almost all use-cases. Refer to [the `sample/` folder](./src/test/java/io/craftgate/sample) for more info.

### Running the Examples
If you've cloned this repo on your development machine and wish to run the examples you can run an example with the command `./gradlew test` or run single test with the command `./gradlew test --tests PaymentSample.create_payment_sample`
The examples call the sandbox API, so first replace `api-key` and `secret-key` in the sample class you want to run with your sandbox credentials.

Run all examples:
```
./gradlew sampleTest
```
Run a single example:
```
./gradlew sampleTest --tests io.craftgate.sample.PaymentSample.create_payment
```

`./gradlew test` runs only the unit tests and does not call the API.

### Credit Card Payment Use Case
Let's quickly review an example where we implement a credit card payment scenario.
Expand Down
Binary file removed bin/.DS_Store
Binary file not shown.
23 changes: 23 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,36 @@ dependencies {
test {
useJUnitPlatform()
exclude 'io/craftgate/sample/**'
systemProperty 'craftgate.expectedVersion', project.version.toString() == 'unspecified' ? 'unknown' : project.version.toString()
}

tasks.register('sampleTest', Test) {
description = 'Runs the samples against the Craftgate sandbox API.'
group = 'verification'
useJUnitPlatform()
testClassesDirs = sourceSets.test.output.classesDirs
classpath = sourceSets.test.runtimeClasspath
include 'io/craftgate/sample/**'
}

if (project.hasProperty('testJavaHome')) {
tasks.withType(Test).configureEach {
executable = new File(project.property('testJavaHome').toString(), 'bin/java').absolutePath
}
}

java {
withJavadocJar()
withSourcesJar()
}

processResources {
inputs.property 'version', project.version
filesMatching('io/craftgate/craftgate-version.properties') {
expand(version: project.version)
}
}

group = 'io.craftgate'
sourceCompatibility = '1.8'

Expand Down
1 change: 0 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version=0.0.1
repoUser=[Nexus Repo User]
repoPassword=[Nexus Repo Password]
signing.keyId=[Signing Key ID]
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/craftgate/adapter/BaseAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private static Map<String, String> createHttpHeaders(BaseRequest request, String
headers.put(API_KEY_HEADER_NAME, requestOptions.getApiKey());
headers.put(RANDOM_HEADER_NAME, randomString);
headers.put(AUTH_VERSION_HEADER_NAME, API_VERSION_HEADER_VALUE);
headers.put(CLIENT_VERSION_HEADER_NAME, CLIENT_VERSION_HEADER_VALUE + ":1.0.82");
headers.put(CLIENT_VERSION_HEADER_NAME, CLIENT_VERSION_HEADER_VALUE + ":" + ClientVersion.get());
headers.put(SIGNATURE_HEADER_NAME, prepareAuthorizationString(request, path, randomString, requestOptions));
if (Objects.nonNull(requestOptions.getLanguage())) {
headers.put(LANGUAGE_HEADER_NAME, requestOptions.getLanguage());
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/io/craftgate/request/common/ClientVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.craftgate.request.common;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public final class ClientVersion {

private static final String UNKNOWN = "unknown";
private static final String VERSION = readVersion();

private ClientVersion() {
}

public static String get() {
return VERSION;
}

private static String readVersion() {
try (InputStream inputStream = ClientVersion.class.getResourceAsStream("/io/craftgate/craftgate-version.properties")) {
if (inputStream == null) {
return UNKNOWN;
}
Properties properties = new Properties();
properties.load(inputStream);
String version = properties.getProperty("version");
if (version == null || version.isEmpty() || version.startsWith("${") || "unspecified".equals(version)) {
return UNKNOWN;
}
return version;
} catch (IOException e) {
return UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version=${version}
13 changes: 13 additions & 0 deletions src/test/java/io/craftgate/adapter/BaseRequestFoundationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class BaseRequestFoundationTest {
private static final String IDEMPOTENCY_KEY_HEADER_NAME = "x-idempotency-key";
private static final String RANDOM_HEADER_NAME = "x-rnd-key";
private static final String SIGNATURE_HEADER_NAME = "x-signature";
private static final String CLIENT_VERSION_HEADER_NAME = "x-client-version";

private final RequestOptions requestOptions = RequestOptions.builder()
.apiKey("api-key")
Expand Down Expand Up @@ -107,6 +108,18 @@ void header_options_do_not_leak_into_query_params_of_read_requests() {
assertFalse(query.contains("idempotency-key-1"));
}

@Test
void request_sends_client_version_from_build() {
CreatePaymentTokenRequest request = CreatePaymentTokenRequest.builder()
.value("card-value")
.build();

Map<String, String> headers = bodyAdapter.createHeaders(request, "/payment/v1/payment-tokens", requestOptions);

String expectedVersion = System.getProperty("craftgate.expectedVersion", "unknown");
assertEquals("craftgate-java-client:" + expectedVersion, headers.get(CLIENT_VERSION_HEADER_NAME));
}

@Test
void body_request_sends_idempotency_key_header() {
CreatePaymentTokenRequest request = CreatePaymentTokenRequest.builder()
Expand Down
21 changes: 21 additions & 0 deletions src/test/java/io/craftgate/unit/ClientVersionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.craftgate.unit;

import io.craftgate.request.common.ClientVersion;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class ClientVersionTest {

@Test
void should_read_client_version_from_build() {
//given
String expectedVersion = System.getProperty("craftgate.expectedVersion", "unknown");

//when
String version = ClientVersion.get();

//then
assertEquals(expectedVersion, version);
}
}
Loading