responseClass,
+ String rawJson, String secretKey) throws IncorrectSealException, IllegalArgumentException {
+ ObjectMapper copy = ObjectMapperHolder.INSTANCE.get().copy();
+ JsonNode jsonObj;
+ try {
+ jsonObj = copy.readTree(rawJson);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ verifySeal(jsonObj.get("Data").textValue(), jsonObj.get("Seal").textValue(), secretKey);
+ return copy.convertValue(jsonObj, responseClass);
+ }
+
+ /**
+ * Verify the seal of a sips response. To avoid tampered data,
+ * the seal for the received response should always be verified before returning the object to the user.
+ *
+ * @param data the received response's Data attribute
+ * @param seal the received response's Seal attribute
+ * @throws IncorrectSealException when the received seal is different from the one calculated
+ */
+ private static void verifySeal(String data, String seal, String secretKey) throws IncorrectSealException {
+ String correctSeal = DigestUtils.sha256Hex(data + secretKey);
+ if (! StringUtils.equals(correctSeal, seal)) {
+ throw new IncorrectSealException("The payment page response has been tampered with!");
+ }
+ }
+
+ /**
+ * Send a request to sips and get the response synchronously.
+ *
+ * The seal of the request is calculated, the request is sent, the response is received and its seal is checked.
+ *
+ * @param request the request that will be sent to SIPS
+ * @param the type of the response
+ * @return a response object mapping the response sent by Sips
+ * @throws SipsRequestException if an error occurred while serializing or sending the request
+ * @throws SipsException if an error occurred while receiving or deserializing the response
+ * @throws SealCalculationException if a seal calculation failed
+ * @throws IncorrectSealException if the response's seal is incorrect
+ */
+ public Response send(SIPSRequest request)
+ throws SipsRequestException, SipsException, SealCalculationException, IncorrectSealException {
+ String fullPath = environment.getUrl() + "/" + request.getEndpoint();
+ try {
+ HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
+ if (this.proxyEnabled) {
+ HttpHost httpHost = new HttpHost(this.proxyHost, this.proxyPort);
+ httpClientBuilder.setProxy(httpHost);
+ }
+ CloseableHttpClient httpClient = httpClientBuilder.build();
+ request.setMerchantId(merchantId);
+ request.setKeyVersion(keyVersion);
+ request.calculateSeal(secretKey);
+ StringEntity requestEntity = new StringEntity(
+ ObjectMapperHolder.INSTANCE.get().writerFor(request.getRealType())
+ .writeValueAsString(request),
+ ContentType.APPLICATION_JSON);
+
+ HttpPost postMethod = new HttpPost(new URI(fullPath));
+ postMethod.setEntity(requestEntity);
+
+ CloseableHttpResponse rawResponse = httpClient.execute(postMethod);
+ Response response =
+ ObjectMapperHolder.INSTANCE.get().readerFor(request.getResponseType())
+ .readValue(EntityUtils.toString(rawResponse.getEntity()));
+
+ response.verifySeal(secretKey);
+ return response;
+ } catch (JsonParseException | JsonMappingException e) {
+ throw new SipsRequestException("Exception while parsing request!", e);
+ } catch (IOException | ParseException e) {
+ throw new SipsException("Exception while processing response from server!", e);
+ } catch (URISyntaxException e) {
+ throw new SipsRequestException("Invalid endpoint: '" + fullPath + "' !", e);
+ }
+ }
+
+ /**
+ * Decode a SIPS response object from a map of parameters.
+ *
+ * @param responseClass the type of the response to construct
+ * @param parameters the content of the received request, mapped as key-value pairs.
+ * @param the type of the response
+ * @return The constructed response.
+ * @throws IncorrectSealException - If the response has been tampered with.
+ * @throws IllegalArgumentException – If conversion fails due to incompatible type; if so, root cause will contain underlying
+ * checked exception data binding functionality threw
+ */
+ public Response decodeResponse(Class responseClass, Map parameters)
+ throws IncorrectSealException, IllegalArgumentException {
+ return decodeResponse(responseClass, parameters, secretKey);
+ }
+
+ /**
+ * Verify the seal of a sips response. To avoid tampered responses,
+ * the seal for the received response should always be verified before returning the object to the user.
+ *
+ * @param response the received response upon initialization
+ * @throws IncorrectSealException when the received seal is different from the one calculated
+ * @throws SealCalculationException when seal calculation fails, see inner exception for details.
+ * @see SIPSResponse#verifySeal(String) = identical
+ */
+ private void verifySeal(SIPSResponse response) throws IncorrectSealException, SealCalculationException {
+ response.verifySeal(secretKey);
+ }
+}
diff --git a/sdk-common/src/main/java/com/worldline/sips/exception/IncorrectProxyConfException.java b/sdk-common/src/main/java/com/worldline/sips/exception/IncorrectProxyConfException.java
new file mode 100644
index 0000000..b577412
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/IncorrectProxyConfException.java
@@ -0,0 +1,9 @@
+package com.worldline.sips.exception;
+
+public class IncorrectProxyConfException extends Exception {
+
+ public IncorrectProxyConfException(String message) {
+ super(message);
+ }
+
+}
diff --git a/payment-sdk/src/main/java/com/worldline/sips/api/exception/IncorrectSealException.java b/sdk-common/src/main/java/com/worldline/sips/exception/IncorrectSealException.java
similarity index 77%
rename from payment-sdk/src/main/java/com/worldline/sips/api/exception/IncorrectSealException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/IncorrectSealException.java
index fdf5468..7a95910 100644
--- a/payment-sdk/src/main/java/com/worldline/sips/api/exception/IncorrectSealException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/IncorrectSealException.java
@@ -1,8 +1,9 @@
-package com.worldline.sips.api.exception;
+package com.worldline.sips.exception;
public class IncorrectSealException extends Exception {
public IncorrectSealException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidEnvironmentException.java b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidEnvironmentException.java
similarity index 78%
rename from payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidEnvironmentException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/InvalidEnvironmentException.java
index 9bf973e..1072375 100644
--- a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidEnvironmentException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidEnvironmentException.java
@@ -1,8 +1,9 @@
-package com.worldline.sips.api.exception;
+package com.worldline.sips.exception;
public class InvalidEnvironmentException extends Exception {
public InvalidEnvironmentException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidKeyException.java b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidKeyException.java
similarity index 76%
rename from payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidKeyException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/InvalidKeyException.java
index 62a1d03..820afb0 100644
--- a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidKeyException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidKeyException.java
@@ -1,8 +1,9 @@
-package com.worldline.sips.api.exception;
+package com.worldline.sips.exception;
public class InvalidKeyException extends Exception {
public InvalidKeyException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidMerchantException.java b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidMerchantException.java
similarity index 77%
rename from payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidMerchantException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/InvalidMerchantException.java
index 1d4bdf5..58fe480 100644
--- a/payment-sdk/src/main/java/com/worldline/sips/api/exception/InvalidMerchantException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/InvalidMerchantException.java
@@ -1,8 +1,10 @@
-package com.worldline.sips.api.exception;
+package com.worldline.sips.exception;
public class InvalidMerchantException extends Exception {
public InvalidMerchantException(String message) {
super(message);
}
+
}
+
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java b/sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java
similarity index 99%
rename from payment-sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java
index 5b33148..98d15ef 100644
--- a/payment-sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/SealCalculationException.java
@@ -1,7 +1,9 @@
package com.worldline.sips.exception;
public class SealCalculationException extends Exception {
+
public SealCalculationException(String message, Throwable cause) {
super(message, cause);
}
+
}
diff --git a/sdk-common/src/main/java/com/worldline/sips/exception/SipsException.java b/sdk-common/src/main/java/com/worldline/sips/exception/SipsException.java
new file mode 100644
index 0000000..1686678
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/SipsException.java
@@ -0,0 +1,9 @@
+package com.worldline.sips.exception;
+
+public class SipsException extends Exception {
+
+ public SipsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/sdk-common/src/main/java/com/worldline/sips/exception/SipsRequestException.java b/sdk-common/src/main/java/com/worldline/sips/exception/SipsRequestException.java
new file mode 100644
index 0000000..12e9b87
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/SipsRequestException.java
@@ -0,0 +1,9 @@
+package com.worldline.sips.exception;
+
+public class SipsRequestException extends Exception {
+
+ public SipsRequestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java b/sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java
similarity index 98%
rename from payment-sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java
index eb16a1b..41c56bf 100644
--- a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/UnknownStatusException.java
@@ -1,7 +1,9 @@
package com.worldline.sips.exception;
public class UnknownStatusException extends Exception {
+
public UnknownStatusException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java b/sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java
similarity index 98%
rename from payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java
index 1706e03..0cee7ed 100644
--- a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedCurrencyException.java
@@ -1,7 +1,9 @@
package com.worldline.sips.exception;
public class UnsupportedCurrencyException extends Exception {
+
public UnsupportedCurrencyException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java b/sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java
similarity index 98%
rename from payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java
rename to sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java
index 499e908..a2c31f8 100644
--- a/payment-sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java
+++ b/sdk-common/src/main/java/com/worldline/sips/exception/UnsupportedLanguageException.java
@@ -1,7 +1,9 @@
package com.worldline.sips.exception;
public class UnsupportedLanguageException extends Exception {
+
public UnsupportedLanguageException(String message) {
super(message);
}
+
}
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalFieldComparator.java b/sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalFieldComparator.java
similarity index 100%
rename from payment-sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalFieldComparator.java
rename to sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalFieldComparator.java
diff --git a/sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalReflectionToStringBuilder.java b/sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalReflectionToStringBuilder.java
new file mode 100644
index 0000000..87bd153
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/helper/AlphabeticalReflectionToStringBuilder.java
@@ -0,0 +1,17 @@
+package com.worldline.sips.helper;
+
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class AlphabeticalReflectionToStringBuilder extends SortedReflectionToStringBuilder {
+
+ private AlphabeticalReflectionToStringBuilder(Object object, ToStringStyle style) {
+ super(object, style);
+ setComparator(new AlphabeticalFieldComparator());
+ }
+
+ public static AlphabeticalReflectionToStringBuilder newInstance(Object object, SealStringStyle sealStringStyle) {
+ AlphabeticalReflectionToStringBuilder res = new AlphabeticalReflectionToStringBuilder(object, sealStringStyle);
+ sealStringStyle.setReflectionToStringBuilder(res);
+ return res;
+ }
+}
diff --git a/payment-sdk-common/src/main/java/com/worldline/sips/helper/BooleanDeserializer.java b/sdk-common/src/main/java/com/worldline/sips/helper/BooleanDeserializer.java
similarity index 100%
rename from payment-sdk-common/src/main/java/com/worldline/sips/helper/BooleanDeserializer.java
rename to sdk-common/src/main/java/com/worldline/sips/helper/BooleanDeserializer.java
diff --git a/sdk-common/src/main/java/com/worldline/sips/helper/SealStringStyle.java b/sdk-common/src/main/java/com/worldline/sips/helper/SealStringStyle.java
new file mode 100644
index 0000000..c644a70
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/helper/SealStringStyle.java
@@ -0,0 +1,64 @@
+package com.worldline.sips.helper;
+
+import java.time.YearMonth;
+import java.util.Arrays;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.RecursiveToStringStyle;
+
+/**
+ * A {@link RecursiveToStringStyle} parametrized to the seal format
+ *
+ * @see com.worldline.sips.security.Sealable
+ */
+public final class SealStringStyle extends RecursiveToStringStyle {
+
+ private SortedReflectionToStringBuilder reflectionToStringBuilder;
+
+ public void setReflectionToStringBuilder(SortedReflectionToStringBuilder reflectionToStringBuilder) {
+ this.reflectionToStringBuilder = reflectionToStringBuilder;
+ }
+
+ public SealStringStyle() {
+ super();
+ setUseClassName(false);
+ setUseIdentityHashCode(false);
+ setUseFieldNames(false);
+ setNullText(StringUtils.EMPTY);
+ setContentStart(StringUtils.EMPTY);
+ setContentEnd(StringUtils.EMPTY);
+ setFieldSeparator(StringUtils.EMPTY);
+ setArrayStart(StringUtils.EMPTY);
+ setArrayEnd(StringUtils.EMPTY);
+ setArraySeparator(StringUtils.EMPTY);
+ }
+
+ @Override
+ public void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ if (! ClassUtils.isPrimitiveWrapper(value.getClass()) &&
+ ! String.class.equals(value.getClass()) &&
+ accept(value.getClass())) {
+ buffer.append(reflectionToStringBuilder.initFrom(value).toString());
+ } else {
+ if (value instanceof YearMonth) {
+ value = ((YearMonth) value).toString().replace("-", "");
+ }
+ super.appendDetail(buffer, fieldName, value);
+ }
+ }
+
+ @Override
+ protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
+ try {
+ Arrays.sort(array);
+ } catch (ClassCastException ignored) {
+ System.out.println("test");
+ }
+ super.appendDetail(buffer, fieldName, array);
+ }
+
+ @Override
+ protected boolean accept(Class> clazz) {
+ return ! clazz.isEnum() && clazz.getPackage().getName().startsWith("com.worldline");
+ }
+}
diff --git a/sdk-common/src/main/java/com/worldline/sips/helper/SortedReflectionToStringBuilder.java b/sdk-common/src/main/java/com/worldline/sips/helper/SortedReflectionToStringBuilder.java
new file mode 100644
index 0000000..f349913
--- /dev/null
+++ b/sdk-common/src/main/java/com/worldline/sips/helper/SortedReflectionToStringBuilder.java
@@ -0,0 +1,95 @@
+package com.worldline.sips.helper;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.commons.lang3.builder.ToStringSummary;
+
+public class SortedReflectionToStringBuilder extends ReflectionToStringBuilder {
+
+ private Comparator comparator;
+ private final Map, Function