Skip to content

Commit

Permalink
Merge pull request #18 from Xaxxis/master
Browse files Browse the repository at this point in the history
v3.1.3 Add concurrent tests in the unit test and fix readme on Client Initialization and Configuration
  • Loading branch information
Xaxxis authored Jun 2, 2022
2 parents 74ca79b + c32301a commit f3ccf54
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 25 deletions.
96 changes: 74 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Midtrans Client - Java
[![Download](https://maven-badges.herokuapp.com/maven-central/com.midtrans/java-library/badge.svg)](https://search.maven.org/artifact/com.midtrans/java-library/)
[![Build Status](https://travis-ci.org/Xaxxis/midtrans-java.svg?branch=master)](https://travis-ci.org/Xaxxis/midtrans-java)
[![Demo apps](https://img.shields.io/badge/Go%20to-Demo%20Apps-green)](https://midtrans-java.herokuapp.com/)
[![Demo apps](https://img.shields.io/badge/Go%20to-Demo%20Apps-green)](https://sample-demo-dot-midtrans-support-tools.et.r.appspot.com/)

Midtrans :heart: Java, This is the Official Java API client/library for Midtrans Payment API. Visit [https://midtrans.com](https://midtrans.com). More information about the product and see documentation at [http://docs.midtrans.com](https://beta-docs.midtrans.com/) for more technical details. This library used java version 1.8

Expand All @@ -14,21 +14,21 @@ Maven:
<dependency>
<groupId>com.midtrans</groupId>
<artifactId>java-library</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
</dependency>
```
Gradle:
If you're using Gradle as the build tools for your project, please add jcenter repository to your build script then add the following dependency to your project's build definition (build.gradle):
```Gradle
dependencies {
implementation 'com.midtrans:java-library:3.1.2'
implementation 'com.midtrans:java-library:3.1.3'
}
```
> **IMPORTANT NOTE**: Since April 13, 2021 We already migrate the repository from jcenter/bintray repository to [Maven central](https://search.maven.org/artifact/com.midtrans/java-library).
### 1.b Using JAR File

If you are not using project build management like Maven, Gradle or Ant you can use manual jar library download JAR Library on [here](https://search.maven.org/remotecontent?filepath=com/midtrans/java-library/3.1.2/java-library-3.1.2.jar)
If you are not using project build management like Maven, Gradle or Ant you can use manual jar library download JAR Library on [here](https://search.maven.org/remotecontent?filepath=com/midtrans/java-library/3.1.3/java-library-3.1.3.jar)

## 2. Usage

Expand All @@ -46,7 +46,7 @@ Choose one that you think best for your unique needs.

Get your client key and server key from [Midtrans Dashboard](https://dashboard.midtrans.com)

Create API client object, You can also check the [project's functional tests](library/src/test/java/com/midtrans/java) for more examples.
Create API client object, Select one from any of methods below that you most prefer. You can also check the [project's functional tests](library/src/test/java/com/midtrans/java) for more examples.

Set a config with globally, except iris api
```java
Expand All @@ -66,31 +66,76 @@ JSONObject result = CoreApi.chargeTransaction(param);
// SnapApi request with global config
JSONObject result = SnapApi.createTransaction(param);
```
#### Per-request Configuration
It is also possible if on each of individual requests you want to set a unique/different configuration; like idempotency key,
proxy, override/append notification url, or multiple account API keys via Config options and set as param on static method e.g: `CoreApi.chargeTransaction(param, configOptions)` method available on `CoreApi.class`, `SnapApi.class`, `TransactionApi.class` or `IrisApi.class`. Please follow the steps given below.

All the request can accept an optional Config object. This is used if you want to set an others' config like idempotency key, proxy,
or if you want to pass the server-key on each method, or use multiple account on each method.
>Note: This method is expected to be thread-safe, you should prefer this if you are implementing multi-threading/concurrency in your code.
```java
import com.midtrans.Config;
import com.midtrans.Midtrans;
import com.midtrans.httpclient.CoreApi;
import com.midtrans.httpclient.error.MidtransError;
import org.json.JSONObject;

Config configOptions = Config.builder()
Config coreApiConfigOptions = Config.builder()
.setServerKey("YOUR_SERVER_KEY")
.setClientKey("YOUR_CLIENT_KEY")
.setIrisIdempotencyKey("UNIQUE_ID")
.setPaymentIdempotencyKey("UNIQUE_ID")
.setProxyConfig(PROXY_CONFIG)
.setPaymentOverrideNotification("WEBHOOK_ENDPOINT")
.setIsProduction(false)
.build();

// CoreApi request with config options
JSONObject result = CoreApi.chargeTransaction(param, configOptions);
JSONObject result = CoreApi.chargeTransaction(param, coreApiConfigOptions);

Config snapConfigOptions = Config.builder()
.setServerKey("YOUR_SERVER_KEY")
.setClientKey("YOUR_CLIENT_KEY")
.setIrisIdempotencyKey("UNIQUE_ID")
.setPaymentIdempotencyKey("UNIQUE_ID")
.setProxyConfig(PROXY_CONFIG)
.setPaymentOverrideNotification("WEBHOOK_ENDPOINT")
.setIsProduction(false)
.build();

// SnapApi request with config options
JSONObject result = SnapApi.createTransaction(param, configOptions);
JSONObject result = SnapApi.createTransaction(param, snapConfigOptions);
```

In case you are using a single account API key, but you need to set the config options value dynamically from the config object. Please follow the steps given below.
```java
import com.midtrans.Midtrans;
import com.midtrans.httpclient.CoreApi;
import com.midtrans.httpclient.SnapApi;
import com.midtrans.httpclient.error.MidtransError;
import org.json.JSONObject;

//1. Set credentials key globally
Midtrans.serverKey = "YOUR_SERVER_KEY";
Midtrans.clientKey = "YOUR_CLIENT_KEY";
Midtrans.isProduction = false;

//2. Set config options for core-api charge request
Config configOptions = Config.builder()
.setPaymentIdempotencyKey("UNIQUE_ID")
.setPaymentOverrideNotification("DYNAMIC_WEBHOOK_ENDPOINT")
.build();

//3. CoreApi request with config options
JSONObject result = CoreApi.chargeTransaction(param, configOptions);

//4. Set config options for Snap charge request
Config snapConfigOptions = Config.builder()
.setPaymentOverrideNotification("DYNAMIC_WEBHOOK_ENDPOINT")
.build();

//5. SnapApi request with config options
JSONObject result = SnapApi.createTransaction(param, snapConfigOptions);
```

#### Alternative way to initialize
```java
import com.midtrans.Config;
Expand Down Expand Up @@ -132,6 +177,8 @@ MidtransIrisApi irisApi = new ConfigFactory(new Config("IRIS-CREDENTIALS",null ,
```

You can also re-set config using `apiConfig()` method on MidtransCoreApi.Class, MidtransSnapApi.Class or MidtransIrisApi.class like `coreApi.apiConfig().set( ... )`
> Please note that if you are using multi-thread concurrency, this method is not thread-safe. For that, you should use the method of creating & setting individual Config option instances per request. [Refer to this section](README.md#per-request-configuration)
example:

```java
Expand All @@ -146,9 +193,6 @@ coreApi.apiConfig().setServerKey("YOUR_SERVER_KEY");

// For Iris Disbursement can set creator & approver credentials with apiConfig()
irisApi.apiConfig().setServerKey("IRIS-CREDENTIALS");

irisApi.apiConfig().setIrisIdempotencyKey("IRIS-IDEMPOTENCY-KEY");

```

In production environment, LOG is by default turned off, you can enable by `setEnabledLog`, e.g:
Expand Down Expand Up @@ -202,19 +246,19 @@ Midtrans.setMaxConnectionPool(16);
Midtrans.setKeepAliveDuration(300000);
Midtrans.setHttpClientTimeUnit(TimeUnit.MILLISECONDS);

// Set connection timeout from initiate api object
coreApi.apiConfig().setConnectionTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setReadTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setWriteTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setKeepAliveDuration(300000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setMaxConnectionPool(16);

// set connection timeout with Config class
config.setConnectionTimeout(10000, TimeUnit.MILLISECONDS);
config.setReadTimeout(10000, TimeUnit.MILLISECONDS);
config.setWriteTimeout(10000, TimeUnit.MILLISECONDS);
config.setKeepAliveDuration(300000, TimeUnit.MILLISECONDS);
config.setMaxConnectionPool(16);

// Set connection timeout from initiate api object
coreApi.apiConfig().setConnectionTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setReadTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setWriteTimeout(10000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setKeepAliveDuration(300000, TimeUnit.MILLISECONDS);
coreApi.apiConfig().setMaxConnectionPool(16);
```

#### Override Notification Url
Expand All @@ -230,10 +274,18 @@ Both header can only receive up to maximum of 3 urls.

```java
//X-Append-Notification
coreApi.apiConfig().paymentAppendNotification("https://example.com/test1,https://example.com/test");
Config configOptions = Config.builder()
.setPaymentAppendNotification("https://example.com/test1,https://example.com/test")
.build();

CoreApi.chargeTransaction(param, configOptions);

//X-Override-Notification
coreApi.apiConfig().paymentOverrideNotification("https://example.com/test1,https://example.com/test");
Config configOptions = Config.builder()
.setPaymentOverrideNotification("https://example.com/test1,https://example.com/test")
.build();;

CoreApi.chargeTransaction(param, configOptions);
```
When both `X-Append-Notification` and `X-Override-Notification` are used together then only override will be used.

Expand Down
15 changes: 14 additions & 1 deletion library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.midtrans</groupId>
<artifactId>java-library</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
<packaging>jar</packaging>

<name>Midtrans Java Official Library</name>
Expand Down Expand Up @@ -67,6 +67,12 @@
<version>2.6.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vmlens</groupId>
<artifactId>concurrent-junit</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-test</artifactId>-->
Expand All @@ -79,6 +85,12 @@
<version>5.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -144,6 +156,7 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
Expand Down
97 changes: 97 additions & 0 deletions library/src/test/java/com/midtrans/java/ConcurrentTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.midtrans.java;

import com.midtrans.Config;
import com.midtrans.Midtrans;
import com.midtrans.httpclient.CoreApi;
import com.midtrans.java.mockupdata.DataMockup;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;

import java.sql.Timestamp;

import static com.midtrans.java.mockupdata.Constant.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* This is concurrent test to make sure each request is a thread safe, perform multiple request the same action
* at the same time.
*/
@Execution(ExecutionMode.CONCURRENT)
public class ConcurrentTest {

Timestamp timestamp = new Timestamp(System.currentTimeMillis());
private final static String WEBHOOK_ENDPOINT = "https://example.com";

@BeforeAll
public static void setUp() {

/*
Set credential-key globally that will be used by {@link #firstRequest() firstRequest} method for initiate
request.
*/
Midtrans.serverKey = mainServerKey;
Midtrans.clientKey = mainClientKey;
Midtrans.isProduction = false;
}

@Test
public void firstRequest() throws Exception{
System.out.println("ParallelUnitTest first() start => " + Thread.currentThread().getName());

//1. Create order-id with prefix 1
String orderId = "CONCURRENT-TEST-1-" +timestamp.getTime();

//2. Create object config as configOptions1 will be used for the first request. We don't set credential-key
// in the config object, will be used credential-key from global config (Midtrans.serverKey)
Config configOptions1 = Config.builder()
.setPaymentOverrideNotification(WEBHOOK_ENDPOINT + "?id=" + orderId)
.build();

//3. set thread sleep to interfere the first request
Thread.sleep(500);

//3. Charge transaction to Midtrans CoreAPI using configOptions1
JSONObject result = CoreApi.chargeTransaction(DataMockup.simpleDataMock(orderId, "gopay"), configOptions1);

//4. Compare the response to check the value of merchant_id it's expected or not. the expected value merchant_id
// should with merchantId1
assertEquals(merchantId1, result.getString("merchant_id"));
assertEquals(orderId, result.getString("order_id"));

assertEquals("201", result.getString("status_code"));
assertEquals("gopay", result.getString("payment_type"));

System.out.println("ParallelUnitTest first() end => " + Thread.currentThread().getName());
}

@Test
public void secondRequest() throws Exception {
System.out.println("ParallelUnitTest second() start => " + Thread.currentThread().getName());

//1. Create order-id with prefix 2
String orderId = "CONCURRENT-TEST-2-" +timestamp.getTime();

//2. Create object config as configOptions2 will be used for the first request. We set the credential-key
// in this config object to simulate the request using multiple Midtrans account
Config configOptions2 = Config.builder()
.setServerKey(secondServerKey)
.setPaymentOverrideNotification(WEBHOOK_ENDPOINT + "?id=" + orderId)
.build();

//3. Charge transaction to Midtrans CoreAPI using configOptions2
JSONObject result = CoreApi.chargeTransaction(DataMockup.simpleDataMock(orderId, "gopay"), configOptions2);

//4. Compare the response to check the value of merchant_id it's expected or not. the expected value merchant_id
// should with merchantId2
assertEquals(merchantId2, result.getString("merchant_id"));
assertEquals(orderId, result.getString("order_id"));

assertEquals("201", result.getString("status_code"));
assertEquals("gopay", result.getString("payment_type"));

System.out.println("ParallelUnitTest second() end => " + Thread.currentThread().getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.midtrans.java.mockupdata;

public class Constant {
public static final String mainServerKey = "SB-Mid-server-TOq1a2AVuiyhhOjvfs3U_KeO";
public static final String mainClientKey = "SB-Mid-client-nKsqvar5cn60u2Lv";
public static final String mainServerKey = "SB-Mid-server-LDMQH0mOAUofApUWMmXxUIFj";
public static final String mainClientKey = "SB-Mid-client-_CIibU2GiWPARC2l";
public static final String merchantId1 = "G331392016";
public static final String secondServerKey = "SB-Mid-server-9Nm5c-HJE65AjLNtTX-bRjqm";
public static final String secondClientKey = "SB-Mid-client-GtbzK39rvs5El-bC";
public static final String merchantId2 = "G902985977";

public static boolean isProduction = false;

Expand Down
12 changes: 12 additions & 0 deletions library/src/test/java/com/midtrans/java/mockupdata/DataMockup.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,16 @@ public Map<String, Object> jsonToMap(String json) {
return map;
}

public static Map<String, Object> simpleDataMock(String orderId, String paymentType) {
Map<String, String> transDetail = new HashMap<>();
transDetail.put("order_id", orderId);
transDetail.put("gross_amount", "265000");

Map<String, Object> body = new HashMap<>();
body.put("transaction_details", transDetail);
body.put("payment_type", paymentType);

return body;
}

}
1 change: 1 addition & 0 deletions library/src/test/resources/junit-platform.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
junit.jupiter.execution.parallel.enabled = true

0 comments on commit f3ccf54

Please sign in to comment.