diff --git a/build.gradle.kts b/build.gradle.kts index 6d751a2..bc78038 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,6 +25,15 @@ dependencies { implementation("com.google.guava:guava:28.0-jre") implementation("org.slf4j:slf4j-api:1.7.28") implementation("org.apache.httpcomponents:httpclient:4.5.10") + implementation("com.fasterxml.jackson.core:jackson-databind:2.9.2") + implementation("com.vaadin.external.google:android-json:0.0.20131108.vaadin1") + implementation("com.nimbusds:nimbus-jose-jwt:7.2.1") + + //Lombok annotations support + annotationProcessor("org.projectlombok:lombok:1.18.8") + compileOnly("org.projectlombok:lombok:1.18.8") + testAnnotationProcessor("org.projectlombok:lombok:1.18.8") + testCompileOnly("org.projectlombok:lombok:1.18.8") // Use JUnit Jupiter API for testing. testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.2") diff --git a/src/main/java/app/klix/GetOrderResponse.java b/src/main/java/app/klix/GetOrderResponse.java deleted file mode 100644 index 83b19cd..0000000 --- a/src/main/java/app/klix/GetOrderResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package app.klix; - -public class GetOrderResponse { -} diff --git a/src/main/java/app/klix/KlixClient.java b/src/main/java/app/klix/KlixClient.java index efb6a90..cbf53c2 100644 --- a/src/main/java/app/klix/KlixClient.java +++ b/src/main/java/app/klix/KlixClient.java @@ -3,16 +3,30 @@ */ package app.klix; +import app.klix.error.KlixException; +import app.klix.order.KlixRejectReason; +import app.klix.request.ApproveOrderRequest; +import app.klix.request.GetOrderRequest; +import app.klix.request.RejectOrderRequest; +import app.klix.rest.KlixConnector; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClients; +@Slf4j +@ToString public final class KlixClient { + private final HttpClient httpClient = defaultHttpClient(); + private final ObjectMapper objectMapper = objectMapper(); private KlixEnvironment environment = KlixEnvironment.STAGING; - private String apiKey; - - private HttpClient httpClient = defaultHttpClient(); + private byte[] privateKey; + private String merchantCertificateId; private HttpClient defaultHttpClient() { return HttpClients.createDefault(); @@ -34,20 +48,78 @@ public void setApiKey(String apiKey) { this.apiKey = apiKey; } + public byte[] getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(byte[] privateKey) { + this.privateKey = privateKey; + } + + public String getMerchantCertificateId() { + return merchantCertificateId; + } + + public void setMerchantCertificateId(String merchantCertificateId) { + this.merchantCertificateId = merchantCertificateId; + } + public HttpClient getHttpClient() { return httpClient; } - public void setHttpClient(HttpClient httpClient) { - this.httpClient = httpClient; + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + private void validateClientForRejectOrApprove() { + try { + Preconditions.checkNotNull(environment); + Preconditions.checkNotNull(merchantCertificateId); + Preconditions.checkNotNull(privateKey); + } catch (NullPointerException e) { + throw new KlixException("KlixClient properties apiKey or/and environment or/and privateKey are null", e); + } + } + + private void validateClientForGet() { + try { + Preconditions.checkNotNull(environment); + Preconditions.checkNotNull(apiKey); + } catch (NullPointerException e) { + throw new KlixException("KlixClient properties apiKey or/and environment are null", e); + } + } + + public GetOrderRequest retrieveOrderDetails(KlixConnector connector, String merchantId, String orderId) { + validateClientForGet(); + GetOrderRequest getOrderRequest = new GetOrderRequest(connector); + getOrderRequest.withMerchantId(merchantId); + getOrderRequest.withOrderId(orderId); + return getOrderRequest; + } + + public RejectOrderRequest rejectOrder(KlixConnector connector, String merchantId, String orderId, KlixRejectReason rejectReason) { + validateClientForRejectOrApprove(); + RejectOrderRequest rejectOrderRequest = new RejectOrderRequest(connector); + rejectOrderRequest.withMerchantId(merchantId); + rejectOrderRequest.withOrderId(orderId); + rejectOrderRequest.withReasonCode(rejectReason); + return rejectOrderRequest; } - public void validate() { - // TODO check all parameters + public ApproveOrderRequest approveOrder(KlixConnector connector, String merchantId, String orderId) { + validateClientForRejectOrApprove(); + ApproveOrderRequest approveOrderRequest = new ApproveOrderRequest(connector); + approveOrderRequest.withMerchantId(merchantId); + approveOrderRequest.withOrderId(orderId); + return approveOrderRequest; } - public GetOrderRequest retrieveOrderDetails() { - return new GetOrderRequest(); + private ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return objectMapper; } } diff --git a/src/main/java/app/klix/RejectOrderRequest.java b/src/main/java/app/klix/RejectOrderRequest.java deleted file mode 100644 index 939f463..0000000 --- a/src/main/java/app/klix/RejectOrderRequest.java +++ /dev/null @@ -1,4 +0,0 @@ -package app.klix; - -public class RejectOrderRequest { -} diff --git a/src/main/java/app/klix/VerifyOrderRequest.java b/src/main/java/app/klix/VerifyOrderRequest.java deleted file mode 100644 index ff08d08..0000000 --- a/src/main/java/app/klix/VerifyOrderRequest.java +++ /dev/null @@ -1,4 +0,0 @@ -package app.klix; - -public class VerifyOrderRequest { -} diff --git a/src/main/java/app/klix/callback/OrderVerificationCallback.java b/src/main/java/app/klix/callback/OrderVerificationCallback.java new file mode 100644 index 0000000..ddb7486 --- /dev/null +++ b/src/main/java/app/klix/callback/OrderVerificationCallback.java @@ -0,0 +1,9 @@ +package app.klix.callback; + +import lombok.Data; + +@Data +public class OrderVerificationCallback { + private String orderId; + private String externalOrderId; +} diff --git a/src/main/java/app/klix/callback/PurchaseConfirmationCallback.java b/src/main/java/app/klix/callback/PurchaseConfirmationCallback.java new file mode 100644 index 0000000..4097efb --- /dev/null +++ b/src/main/java/app/klix/callback/PurchaseConfirmationCallback.java @@ -0,0 +1,15 @@ +package app.klix.callback; + +import app.klix.order.KlixPaymentStatus; +import lombok.Data; +import lombok.ToString; + +@Data +@ToString +public class PurchaseConfirmationCallback { + + private KlixPaymentStatus status; + private String orderId; + private String externalOrderId; + +} diff --git a/src/main/java/app/klix/callback/ShippingCostCalculationCallback.java b/src/main/java/app/klix/callback/ShippingCostCalculationCallback.java new file mode 100644 index 0000000..07e96a5 --- /dev/null +++ b/src/main/java/app/klix/callback/ShippingCostCalculationCallback.java @@ -0,0 +1,18 @@ +package app.klix.callback; + +import app.klix.order.KlixAddress; +import app.klix.order.KlixOrderItem; +import app.klix.order.KlixPickupPoint; +import lombok.Data; + +@Data +public class ShippingCostCalculationCallback { + + private String order_id; + private KlixOrderItem[] items; + private String shipping_method_id; + private KlixAddress address; + private KlixPickupPoint pickup_point; + + //TODO check if actual structure is different +} diff --git a/src/main/java/app/klix/encoder/JwsEncoder.java b/src/main/java/app/klix/encoder/JwsEncoder.java new file mode 100644 index 0000000..cfae803 --- /dev/null +++ b/src/main/java/app/klix/encoder/JwsEncoder.java @@ -0,0 +1,43 @@ +package app.klix.encoder; + +import com.nimbusds.jose.*; +import com.nimbusds.jose.crypto.RSASSASigner; +import lombok.extern.slf4j.Slf4j; + + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +@Slf4j +public class JwsEncoder { + + public String encodeJwsBody(String payload, String merchantCertificateId, String privateKeyContent) { + try { + + PrivateKey privateKey = createPrivateKeyObject(privateKeyContent); + + JWSSigner signer = new RSASSASigner(privateKey, true); + JWSObject jwsObject = new JWSObject( + new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(merchantCertificateId).build(), new Payload(payload)); + jwsObject.sign(signer); + return jwsObject.serialize(); + } catch (Exception ex) { + log.error(ex.getLocalizedMessage()); + return null; + } + } + + private PrivateKey createPrivateKeyObject(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + + String privKeyPEM = privateKey.replace("-----BEGIN PRIVATE KEY-----", ""); + privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", ""); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privKeyPEM.replaceAll("\\n", "").replaceAll("\\r", ""))); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(keySpec); + } + +} \ No newline at end of file diff --git a/src/main/java/app/klix/encoder/JwsValidationResult.java b/src/main/java/app/klix/encoder/JwsValidationResult.java new file mode 100644 index 0000000..c8b8e63 --- /dev/null +++ b/src/main/java/app/klix/encoder/JwsValidationResult.java @@ -0,0 +1,10 @@ +package app.klix.encoder; + + +import lombok.Data; + +@Data +public class JwsValidationResult { + private boolean isValid; + private String payload; +} \ No newline at end of file diff --git a/src/main/java/app/klix/order/KlixAddress.java b/src/main/java/app/klix/order/KlixAddress.java new file mode 100644 index 0000000..5ef9e05 --- /dev/null +++ b/src/main/java/app/klix/order/KlixAddress.java @@ -0,0 +1,13 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixAddress { + private String city; + private String country; + private Double latitude; + private Double longitude; + private String postal_code; + private String street; +} diff --git a/src/main/java/app/klix/order/KlixCompany.java b/src/main/java/app/klix/order/KlixCompany.java new file mode 100644 index 0000000..c818059 --- /dev/null +++ b/src/main/java/app/klix/order/KlixCompany.java @@ -0,0 +1,11 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixCompany { + private String address; + private String name; + private String registration_number; + private String vat_number; +} diff --git a/src/main/java/app/klix/order/KlixCustomer.java b/src/main/java/app/klix/order/KlixCustomer.java new file mode 100644 index 0000000..a1539b9 --- /dev/null +++ b/src/main/java/app/klix/order/KlixCustomer.java @@ -0,0 +1,11 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixCustomer { + private String first_name; + private String last_name; + private String email; + private String phone_number; +} diff --git a/src/main/java/app/klix/order/KlixMerchantShippingMethod.java b/src/main/java/app/klix/order/KlixMerchantShippingMethod.java new file mode 100644 index 0000000..4f16fdd --- /dev/null +++ b/src/main/java/app/klix/order/KlixMerchantShippingMethod.java @@ -0,0 +1,9 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixMerchantShippingMethod { + private String id; + private String name; +} diff --git a/src/main/java/app/klix/order/KlixMerchantUrls.java b/src/main/java/app/klix/order/KlixMerchantUrls.java new file mode 100644 index 0000000..ddeea73 --- /dev/null +++ b/src/main/java/app/klix/order/KlixMerchantUrls.java @@ -0,0 +1,11 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixMerchantUrls { + private String confirmation; + private String place_order; + private String terms; + private String verification; +} diff --git a/src/main/java/app/klix/order/KlixOrderItem.java b/src/main/java/app/klix/order/KlixOrderItem.java new file mode 100644 index 0000000..221fb7c --- /dev/null +++ b/src/main/java/app/klix/order/KlixOrderItem.java @@ -0,0 +1,19 @@ +package app.klix.order; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class KlixOrderItem { + private BigDecimal amount; + private String label; + private BigDecimal tax_amount; + private BigDecimal total_amount; + private BigDecimal tax_rate; + private String order_item_id; + private BigDecimal quantity; + private String unit; + private String type; + private String reference; +} diff --git a/src/main/java/app/klix/order/KlixOrderStatus.java b/src/main/java/app/klix/order/KlixOrderStatus.java new file mode 100644 index 0000000..2ef69d3 --- /dev/null +++ b/src/main/java/app/klix/order/KlixOrderStatus.java @@ -0,0 +1,9 @@ +package app.klix.order; + +public enum KlixOrderStatus { + PENDING, + VERIFIED, + REJECTED, + INVALID, + DRAFT +} diff --git a/src/main/java/app/klix/order/KlixPaymentStatus.java b/src/main/java/app/klix/order/KlixPaymentStatus.java new file mode 100644 index 0000000..97f1328 --- /dev/null +++ b/src/main/java/app/klix/order/KlixPaymentStatus.java @@ -0,0 +1,6 @@ +package app.klix.order; + +public enum KlixPaymentStatus { + PAYMENT_APPROVED, + PAYMENT_REJECTED +} diff --git a/src/main/java/app/klix/order/KlixPickupPoint.java b/src/main/java/app/klix/order/KlixPickupPoint.java new file mode 100644 index 0000000..294e91e --- /dev/null +++ b/src/main/java/app/klix/order/KlixPickupPoint.java @@ -0,0 +1,11 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixPickupPoint { + private String comments; + private String external_id; + private String name; + private String service_hours; +} diff --git a/src/main/java/app/klix/order/KlixRejectReason.java b/src/main/java/app/klix/order/KlixRejectReason.java new file mode 100644 index 0000000..1591b1b --- /dev/null +++ b/src/main/java/app/klix/order/KlixRejectReason.java @@ -0,0 +1,5 @@ +package app.klix.order; + +public enum KlixRejectReason { + PRICE_CHANGED +} diff --git a/src/main/java/app/klix/order/KlixShipping.java b/src/main/java/app/klix/order/KlixShipping.java new file mode 100644 index 0000000..0ba4e50 --- /dev/null +++ b/src/main/java/app/klix/order/KlixShipping.java @@ -0,0 +1,13 @@ +package app.klix.order; + +import lombok.Data; + +@Data +public class KlixShipping { + private KlixAddress address; + private String contact_phone; + private String date; + private KlixMerchantShippingMethod method; + private KlixPickupPoint pickup_point; + private String type; +} diff --git a/src/main/java/app/klix/request/ApproveOrderRequest.java b/src/main/java/app/klix/request/ApproveOrderRequest.java new file mode 100644 index 0000000..f3bd346 --- /dev/null +++ b/src/main/java/app/klix/request/ApproveOrderRequest.java @@ -0,0 +1,44 @@ +package app.klix.request; + +import app.klix.rest.KlixConnector; +import com.google.common.base.Preconditions; +import lombok.ToString; +import org.apache.http.HttpResponse; + +@ToString +public class ApproveOrderRequest { + private final KlixConnector connector; + + private String merchantId; + private String orderId; + + public ApproveOrderRequest(KlixConnector connector) { + this.connector = connector; + } + + public ApproveOrderRequest withMerchantId(String merchantId) { + this.merchantId = merchantId; + return this; + } + + public ApproveOrderRequest withOrderId(String orderId) { + this.orderId = orderId; + return this; + } + + public String getMerchantId() { + return merchantId; + } + + public String getOrderId() { + return orderId; + } + + public HttpResponse call() { + Preconditions.checkNotNull(merchantId); + Preconditions.checkNotNull(orderId); + + return connector.approveOrder(this); + } + +} diff --git a/src/main/java/app/klix/GetOrderRequest.java b/src/main/java/app/klix/request/GetOrderRequest.java similarity index 89% rename from src/main/java/app/klix/GetOrderRequest.java rename to src/main/java/app/klix/request/GetOrderRequest.java index 0377ad3..a92569f 100644 --- a/src/main/java/app/klix/GetOrderRequest.java +++ b/src/main/java/app/klix/request/GetOrderRequest.java @@ -1,8 +1,11 @@ -package app.klix; +package app.klix.request; +import app.klix.response.GetOrderResponse; import app.klix.rest.KlixConnector; import com.google.common.base.Preconditions; +import lombok.ToString; +@ToString public class GetOrderRequest { private final KlixConnector connector; diff --git a/src/main/java/app/klix/request/RejectOrderRequest.java b/src/main/java/app/klix/request/RejectOrderRequest.java new file mode 100644 index 0000000..5f6e3a2 --- /dev/null +++ b/src/main/java/app/klix/request/RejectOrderRequest.java @@ -0,0 +1,57 @@ +package app.klix.request; + +import app.klix.order.KlixRejectReason; +import app.klix.rest.KlixConnector; +import com.google.common.base.Preconditions; +import lombok.ToString; +import org.apache.http.HttpResponse; + +@ToString +public class RejectOrderRequest { + + private final KlixConnector connector; + + private String merchantId; + private String orderId; + private KlixRejectReason reasonCode; + + public RejectOrderRequest(KlixConnector connector) { + this.connector = connector; + } + + public RejectOrderRequest withMerchantId(String merchantId) { + this.merchantId = merchantId; + return this; + } + + public RejectOrderRequest withOrderId(String orderId) { + this.orderId = orderId; + return this; + } + + public RejectOrderRequest withReasonCode(KlixRejectReason reasonCode) { + this.reasonCode = reasonCode; + return this; + } + + public String getMerchantId() { + return merchantId; + } + + public String getOrderId() { + return orderId; + } + + public KlixRejectReason getReasonCode() { + return reasonCode; + } + + public HttpResponse call() { + Preconditions.checkNotNull(merchantId); + Preconditions.checkNotNull(orderId); + Preconditions.checkNotNull(reasonCode); + + return connector.rejectOrder(this); + } + +} diff --git a/src/main/java/app/klix/response/GetOrderResponse.java b/src/main/java/app/klix/response/GetOrderResponse.java new file mode 100644 index 0000000..11401f7 --- /dev/null +++ b/src/main/java/app/klix/response/GetOrderResponse.java @@ -0,0 +1,32 @@ +package app.klix.response; + +import app.klix.order.KlixCompany; +import app.klix.order.KlixCustomer; +import app.klix.order.KlixMerchantUrls; +import app.klix.order.KlixOrderItem; +import app.klix.order.KlixOrderStatus; +import app.klix.order.KlixShipping; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@ToString(callSuper = true) +public class GetOrderResponse { + private String id; + private KlixOrderStatus status; + private KlixCustomer customer; + private KlixCompany company; + private String order_id; + private BigDecimal amount; + private BigDecimal tax_amount; + private BigDecimal total_amount; + private KlixOrderItem[] items; + private String currency; + private KlixMerchantUrls merchant_urls; + private KlixShipping shipping; + +} diff --git a/src/main/java/app/klix/rest/HttpDeleteWithBody.java b/src/main/java/app/klix/rest/HttpDeleteWithBody.java new file mode 100644 index 0000000..394fd31 --- /dev/null +++ b/src/main/java/app/klix/rest/HttpDeleteWithBody.java @@ -0,0 +1,30 @@ +package app.klix.rest; + + +import net.jcip.annotations.NotThreadSafe; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import java.net.URI; + +@NotThreadSafe +public +class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase { + private static final String METHOD_NAME = "DELETE"; + + public String getMethod() { + return METHOD_NAME; + } + + HttpDeleteWithBody(final String uri) { + super(); + setURI(URI.create(uri)); + } + + public HttpDeleteWithBody(final URI uri) { + super(); + setURI(uri); + } + + public HttpDeleteWithBody() { + super(); + } +} \ No newline at end of file diff --git a/src/main/java/app/klix/rest/KlixConnector.java b/src/main/java/app/klix/rest/KlixConnector.java index 1b3826e..100a0f4 100644 --- a/src/main/java/app/klix/rest/KlixConnector.java +++ b/src/main/java/app/klix/rest/KlixConnector.java @@ -1,27 +1,140 @@ package app.klix.rest; -import app.klix.GetOrderRequest; -import app.klix.GetOrderResponse; import app.klix.KlixClient; +import app.klix.encoder.JwsEncoder; import app.klix.error.KlixException; +import app.klix.request.ApproveOrderRequest; +import app.klix.request.GetOrderRequest; +import app.klix.request.RejectOrderRequest; +import app.klix.response.GetOrderResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Preconditions; +import lombok.ToString; +import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.json.JSONException; +import org.json.JSONObject; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +@ToString public final class KlixConnector { - public static final String X_KLIX_API_KEY = "X-KLIX-Api-Key"; + private static final String X_KLIX_API_KEY = "X-KLIX-Api-Key"; private final KlixClient klixClient; - public KlixConnector(KlixClient klixClient) { + private final JwsEncoder jwsEncoder; + + public KlixConnector(KlixClient klixClient, JwsEncoder jwsEncoder) { + this.klixClient = klixClient; + this.jwsEncoder = jwsEncoder; } public GetOrderResponse getOrderDetails(GetOrderRequest request) { + + HttpResponse response = getOrderDetailsEntity(request); + try { + String rawBody = EntityUtils.toString(response.getEntity()); + ObjectMapper objectMapper = klixClient.getObjectMapper(); + + return objectMapper.readValue(rawBody, GetOrderResponse.class); + + } catch (IOException e) { + throw new KlixException("Failed to deserialize order [" + request.getOrderId() + "] response object", e); + } + + } + + public HttpResponse rejectOrder(RejectOrderRequest request) { + + String baseUri = klixClient.getEnvironment().getUri(); + String merchantCertificateId = klixClient.getMerchantCertificateId(); + String privateKey = new String(klixClient.getPrivateKey()); + + + String merchantId; + String orderId; + String reasonCode; + try { + merchantId = URLEncoder.encode(request.getMerchantId(), "UTF-8"); + orderId = URLEncoder.encode(request.getOrderId(), "UTF-8"); + reasonCode = URLEncoder.encode(request.getReasonCode().name(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new KlixException("Failed to encode", e); + } + + HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(baseUri + "/merchants/" + merchantId + "/orders/" + orderId); + + HttpClient httpClient = klixClient.getHttpClient(); + try { + String jsonString = new JSONObject() + .put("id", orderId) + .put("reasonCode", reasonCode) + .toString(); + + String encodedBody = jwsEncoder.encodeJwsBody(jsonString, merchantCertificateId, privateKey); + + StringEntity input = new StringEntity(encodedBody, ContentType.APPLICATION_JSON); + httpDelete.setEntity(input); + + return httpClient.execute(httpDelete); + + } catch (IOException | JSONException e) { + throw new KlixException("Failed to reject order [" + orderId + "]", e); + } + + } + + public HttpResponse approveOrder(ApproveOrderRequest request) { + + String baseUri = klixClient.getEnvironment().getUri(); + String merchantCertificateId = klixClient.getMerchantCertificateId(); + String privateKey = new String(klixClient.getPrivateKey()); + + + String merchantId; + String orderId; + try { + merchantId = URLEncoder.encode(request.getMerchantId(), "UTF-8"); + orderId = URLEncoder.encode(request.getOrderId(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new KlixException("Failed to encode", e); + } + + HttpPut httpPut = new HttpPut(baseUri + "/merchants/" + merchantId + "/orders/" + orderId); + + HttpClient httpClient = klixClient.getHttpClient(); + try { + GetOrderRequest orderRequest = new GetOrderRequest(this); + orderRequest.withMerchantId(merchantId); + orderRequest.withOrderId(orderId); + + String fullOrderDetails = getOrderDetailsRaw(orderRequest); + + String encodedBody = jwsEncoder.encodeJwsBody(fullOrderDetails, merchantCertificateId, privateKey); + + StringEntity input = new StringEntity(encodedBody, ContentType.APPLICATION_JSON); + httpPut.setEntity(input); + + return httpClient.execute(httpPut); + + } catch (IOException e) { + throw new KlixException("Failed to approve order [" + orderId + "]", e); + } + + } + + private HttpResponse getOrderDetailsEntity(GetOrderRequest request) { + String baseUri = klixClient.getEnvironment().getUri(); String merchantId; @@ -37,13 +150,28 @@ public GetOrderResponse getOrderDetails(GetOrderRequest request) { httpGet.addHeader(X_KLIX_API_KEY, klixClient.getApiKey()); HttpClient httpClient = klixClient.getHttpClient(); + HttpResponse response; try { - httpClient.execute(httpGet); + response = httpClient.execute(httpGet); + Preconditions.checkArgument(response.getStatusLine().getStatusCode() == 200); + return response; + + } catch (IOException e) { + throw new KlixException("Failed to deserialize order [" + orderId + "] response object", e); + } + + } + + private String getOrderDetailsRaw(GetOrderRequest request) { + + HttpResponse response = getOrderDetailsEntity(request); + try { + return EntityUtils.toString(response.getEntity()); + } catch (IOException e) { - e.printStackTrace(); + throw new KlixException("Failed to deserialize order [" + request.getOrderId() + "] response object", e); } - return } } diff --git a/src/test/java/app/klix/LibraryTest.java b/src/test/java/app/klix/LibraryTest.java index 96064e1..512cd4e 100644 --- a/src/test/java/app/klix/LibraryTest.java +++ b/src/test/java/app/klix/LibraryTest.java @@ -3,12 +3,416 @@ */ package app.klix; +import app.klix.callback.OrderVerificationCallback; +import app.klix.callback.PurchaseConfirmationCallback; +import app.klix.callback.ShippingCostCalculationCallback; +import app.klix.encoder.JwsEncoder; +import app.klix.order.KlixMerchantUrls; +import app.klix.order.KlixOrderItem; +import app.klix.order.KlixOrderStatus; +import app.klix.order.KlixPaymentStatus; +import app.klix.order.KlixRejectReason; +import app.klix.request.ApproveOrderRequest; +import app.klix.request.GetOrderRequest; +import app.klix.request.RejectOrderRequest; +import app.klix.response.GetOrderResponse; +import app.klix.rest.KlixConnector; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Slf4j class LibraryTest { @Test - void testSomeLibraryMethod() { - KlixClient classUnderTest = new KlixClient(); - assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'"); + void GetOrderResponseMapping() { + KlixClient klixClient = new KlixClient(); + + String exampleJson = "{\n" + + " \"id\": \"7728d220-5723-4e20-a800-454418f31739\",\n" + + " \"status\": \"VERIFIED\",\n" + + " \"customer\": {\n" + + " \"first_name\": \"Name\",\n" + + " \"last_name\": \"Surname\",\n" + + " \"email\": \"email@email.com\",\n" + + " \"phone_number\": \"3712945600\"\n" + + " },\n" + + " \"company\": {\n" + + " \"address\": \"Example address\",\n" + + " \"name\": \"Great Company\",\n" + + " \"registration_number\": \"REG.NUM.2333\",\n" + + " \"vat_number\": \"VAT23232324\"\n" + + " },\n" + + " \"order_id\": \"37616d8b-8ed0-4cd7-88d7-13b320ae14dc\",\n" + + " \"amount\": null,\n" + + " \"tax_amount\": 115.19,\n" + + " \"total_amount\": 663.69,\n" + + " \"items\": [\n" + + " {\n" + + " \"amount\": 100,\n" + + " \"label\": \"Dummy Ticket\",\n" + + " \"tax_amount\": 21,\n" + + " \"total_amount\": 121,\n" + + " \"tax_rate\": 0.21,\n" + + " \"order_item_id\": null,\n" + + " \"quantity\": 1,\n" + + " \"unit\": \"PIECE\",\n" + + " \"type\": \"UNKNOWN\"\n" + + " },\n" + + " {\n" + + " \"amount\": 199.5,\n" + + " \"label\": \"Early Bird Ticket\",\n" + + " \"tax_amount\": 41.895,\n" + + " \"total_amount\": 241.395,\n" + + " \"tax_rate\": 0.21,\n" + + " \"order_item_id\": null,\n" + + " \"quantity\": 1,\n" + + " \"unit\": \"PIECE\",\n" + + " \"type\": \"UNKNOWN\"\n" + + " },\n" + + " {\n" + + " \"amount\": 249,\n" + + " \"label\": \"Late Bird\",\n" + + " \"tax_amount\": 52.29,\n" + + " \"total_amount\": 301.29,\n" + + " \"tax_rate\": 0.21,\n" + + " \"order_item_id\": null,\n" + + " \"quantity\": 1,\n" + + " \"unit\": \"PIECE\",\n" + + " \"type\": \"UNKNOWN\"\n" + + " }\n" + + " ],\n" + + " \"currency\": \"EUR\",\n" + + " \"merchant_urls\": {\n" + + " \"terms\": \"https://2020.paymentconf.com/terms-and-conditions/\",\n" + + " \"verification\": \"https://tickets.paymentconf.com/api/public/order\",\n" + + " \"confirmation\": \"https://tickets.paymentconf.com/api/public/purchase\",\n" + + " \"place_order\": \"https://2020.paymentconf.com\"\n" + + " },\n" + + " \"shipping\": {\n" + + " \"address\": {\n" + + " \"city\": \"Rīga\",\n" + + " \"country\": \"Latvia\",\n" + + " \"latitude\": 20.32,\n" + + " \"longitude\": 43.98,\n" + + " \"postal_code\": \"LV-1013\",\n" + + " \"street\": \"Mukusalas iela\"\n" + + " },\n" + + " \"contact_phone\": \"37123456789\",\n" + + " \"date\": \"01.11.2019\",\n" + + " \"method\": {\n" + + " \"id\": \"32\",\n" + + " \"name\": \"Truck\" \n" + + " },\n" + + " \"pickup_point\": {\n" + + " \"external_id\": \"9111\",\n" + + " \"name\": \"Rīgas Briāna ielas Rimi pakomāts\",\n" + + " \"comments\": \"Pa labi no galvenās ieejas\",\n" + + " \"service_hours\": \"P-Pk.piegāde 10:00, izņemšana 17:00 Sestdienās piegāde 14:00,izņemšana 14:00\"\n" + + " },\n" + + " \"type\": \"Truck\"\n" + + " }\n" + + "}"; + + ObjectMapper mapper = klixClient.getObjectMapper(); + + try { + GetOrderResponse orderResponse = mapper.readValue(exampleJson, GetOrderResponse.class); + + log.info(orderResponse.toString()); + + assertEquals("7728d220-5723-4e20-a800-454418f31739", orderResponse.getId(), "Id should be set"); + assertEquals(orderResponse.getStatus(), KlixOrderStatus.VERIFIED, "Order status should be set"); + assertEquals("Name", orderResponse.getCustomer().getFirst_name(), "First name should be set"); + assertEquals("Surname", orderResponse.getCustomer().getLast_name(), "Last name should be set"); + assertEquals("email@email.com", orderResponse.getCustomer().getEmail(), "Email should be set"); + assertEquals("3712945600", orderResponse.getCustomer().getPhone_number(), "Phone number should be set"); + + //KlixCompany + assertEquals("Example address", orderResponse.getCompany().getAddress(), "Company address should be set"); + assertEquals("Great Company", orderResponse.getCompany().getName(), "Company name should be set"); + assertEquals("REG.NUM.2333", orderResponse.getCompany().getRegistration_number(), "Company registration number should be set"); + assertEquals("VAT23232324", orderResponse.getCompany().getVat_number(), "Company VAT number should be set"); + + assertEquals("37616d8b-8ed0-4cd7-88d7-13b320ae14dc", orderResponse.getOrder_id(), "Order Id should be set"); + assertNull(orderResponse.getAmount(), "Order amount should be null"); + assertEquals(0, orderResponse.getTax_amount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(115.19).setScale(2, RoundingMode.HALF_UP)), "Order tax amount should be set"); + assertEquals(0, orderResponse.getTotal_amount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(663.69).setScale(2, RoundingMode.HALF_UP)), "Order total amount should be set"); + + //KlixOrderItem + KlixOrderItem[] orderItems = orderResponse.getItems(); + assertEquals(3, orderItems.length, "Order items length should be 3"); + assertEquals(0, orderItems[0].getAmount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(100).setScale(2, RoundingMode.HALF_UP)), "Item[0] amount should be set"); + assertEquals("Dummy Ticket", orderItems[0].getLabel(), "Order Id should be set"); + assertEquals(0, orderItems[0].getTax_amount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(21).setScale(2, RoundingMode.HALF_UP)), "Item[0] tax amount should be set"); + assertEquals(0, orderItems[0].getTotal_amount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(121).setScale(2, RoundingMode.HALF_UP)), "Item[0] total amount should be set"); + assertEquals(0, orderItems[0].getTax_rate().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(0.21).setScale(2, RoundingMode.HALF_UP)), "Item[0] tax rate should be set"); + assertNull(orderItems[0].getOrder_item_id(), "Item[0] id should be null"); + assertEquals(0, orderItems[0].getQuantity().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(1).setScale(2, RoundingMode.HALF_UP)), "Item[0] quantity should be set"); + assertEquals("PIECE", orderItems[0].getUnit(), "Item[0] unit should be set"); + assertEquals("UNKNOWN", orderItems[0].getType(), "Item[0] type should be set"); + assertNull(orderItems[0].getReference(), "Item[0] reference should be null"); + + assertEquals("EUR", orderResponse.getCurrency(), "Item[0] currency should be set"); + + //KlixMerchantUrls + KlixMerchantUrls merchantUrls = orderResponse.getMerchant_urls(); + assertEquals("https://tickets.paymentconf.com/api/public/purchase", merchantUrls.getConfirmation(), "MerchantUrls confirmation link Id should be set"); + assertEquals("https://2020.paymentconf.com", merchantUrls.getPlace_order(), "MerchantUrls place order link should be set"); + assertEquals("https://2020.paymentconf.com/terms-and-conditions/", merchantUrls.getTerms(), "MerchantUrls terms link should be set"); + assertEquals("https://tickets.paymentconf.com/api/public/order", merchantUrls.getVerification(), "MerchantUrls verification link should be set"); + + //KlixShipping + assertEquals(orderResponse.getShipping().getAddress().getCity(), "Rīga", "Address city should be set"); + assertEquals(orderResponse.getShipping().getAddress().getCountry(), "Latvia", "Address country should be set"); + assertEquals(orderResponse.getShipping().getAddress().getLatitude(), 20.32, "Address latitude should be set"); + assertEquals(orderResponse.getShipping().getAddress().getLongitude(), 43.98, "Address longitude should be set"); + assertEquals(orderResponse.getShipping().getAddress().getPostal_code(), "LV-1013", "Address postal code should be set"); + assertEquals(orderResponse.getShipping().getAddress().getStreet(), "Mukusalas iela", "Address street should be set"); + assertEquals(orderResponse.getShipping().getContact_phone(), "37123456789", "Shipping contact phone should be set"); + assertEquals(orderResponse.getShipping().getDate(), "01.11.2019", "Shipping date should be set"); + assertEquals(orderResponse.getShipping().getMethod().getId(), "32", "ShippingMethod Id should be set"); + assertEquals(orderResponse.getShipping().getMethod().getName(), "Truck", "ShippingMethod name should be set"); + assertEquals(orderResponse.getShipping().getPickup_point().getComments(), "Pa labi no galvenās ieejas", "Pickup point comments should be set"); + assertEquals(orderResponse.getShipping().getPickup_point().getExternal_id(), "9111", "Pickup point external id should be set"); + assertEquals(orderResponse.getShipping().getPickup_point().getName(), "Rīgas Briāna ielas Rimi pakomāts", "Pickup point name should be set"); + assertEquals(orderResponse.getShipping().getPickup_point().getService_hours(), + "P-Pk.piegāde 10:00, izņemšana 17:00 Sestdienās piegāde 14:00,izņemšana 14:00", "Pickup point service hours should be set"); + assertEquals(orderResponse.getShipping().getType(), "Truck", "Shipping type should be set"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + void OrderVerificationCallbackMapping() { + KlixClient klixClient = new KlixClient(); + + String exampleJson = "{\n" + + " \"orderId\": \"7728d220-5723-4e20-a800-454418f31739\",\n" + + " \"externalOrderId\": \"37616d8b-8ed0-4cd7-88d7-13b320ae14dc\"\n" + + "}"; + + ObjectMapper mapper = klixClient.getObjectMapper(); + + try { + OrderVerificationCallback orderVerificationCallback = mapper.readValue(exampleJson, OrderVerificationCallback.class); + + log.info(orderVerificationCallback.toString()); + + assertEquals("7728d220-5723-4e20-a800-454418f31739", orderVerificationCallback.getOrderId(), "Order Id should be set"); + assertEquals("37616d8b-8ed0-4cd7-88d7-13b320ae14dc", orderVerificationCallback.getExternalOrderId(), "External order Id should be set"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + void PurchaseConfirmationCallbackMapping() { + KlixClient klixClient = new KlixClient(); + + String exampleJson = "{\n" + + " \"status\": \"PAYMENT_APPROVED\",\n" + + " \"orderId\": \"7728d220-5723-4e20-a800-454418f31739\",\n" + + " \"externalOrderId\": \"37616d8b-8ed0-4cd7-88d7-13b320ae14dc\"\n" + + "}"; + + ObjectMapper mapper = klixClient.getObjectMapper(); + + try { + PurchaseConfirmationCallback purchaseConfirmationCallback = mapper.readValue(exampleJson, PurchaseConfirmationCallback.class); + + log.info(purchaseConfirmationCallback.toString()); + + assertEquals(purchaseConfirmationCallback.getStatus(), KlixPaymentStatus.PAYMENT_APPROVED, "Order status should be set"); + assertEquals("7728d220-5723-4e20-a800-454418f31739", purchaseConfirmationCallback.getOrderId(), "Order Id should be set"); + assertEquals("37616d8b-8ed0-4cd7-88d7-13b320ae14dc", purchaseConfirmationCallback.getExternalOrderId(), "External order Id should be set"); + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Test + void ShippingCostCalculationCallbackMapping() { + KlixClient klixClient = new KlixClient(); + + String exampleJson = "{ \n" + + " \"order_id\": \"05957e7f-803f-46f0-9100-1c3e10199b43\",\n" + + " \"items\": [\n" + + " {\n" + + " \"amount\": 100,\n" + + " \"label\": \"Dummy Ticket\",\n" + + " \"tax_amount\": 21,\n" + + " \"total_amount\": 121,\n" + + " \"tax_rate\": 0.21,\n" + + " \"order_item_id\": null,\n" + + " \"quantity\": 1,\n" + + " \"unit\": \"PIECE\",\n" + + " \"type\": \"UNKNOWN\"\n" + + " },\n" + + " {\n" + + " \"amount\": 199.5,\n" + + " \"label\": \"Early Bird Ticket\",\n" + + " \"tax_amount\": 41.895,\n" + + " \"total_amount\": 241.395,\n" + + " \"tax_rate\": 0.21,\n" + + " \"order_item_id\": null,\n" + + " \"quantity\": 1,\n" + + " \"unit\": \"PIECE\",\n" + + " \"type\": \"UNKNOWN\"\n" + + " }\n" + + " ],\n" + + " \"shipping_method_id\": \"omniva\",\n" + + " \"address\": {\n" + + " \"city\": \"Rīga\",\n" + + " \"country\": \"Latvia\",\n" + + " \"postal_code\": \"LV-1013\"\n" + + " },\n" + + " \"pickup_point\": {\n" + + " \"external_id\": \"9111\",\n" + + " \"name\": \"Rīgas Briāna ielas Rimi pakomāts\",\n" + + " \"comments\": \"Pa labi no galvenās ieejas\",\n" + + " \"service_hours\": \"P-Pk.piegāde 10:00, izņemšana 17:00 Sestdienās piegāde 14:00,izņemšana 14:00\"\n" + + " }\n" + + "}"; + + ObjectMapper mapper = klixClient.getObjectMapper(); + + try { + ShippingCostCalculationCallback shippingCostCalculationCallback = mapper.readValue(exampleJson, ShippingCostCalculationCallback.class); + + log.info(shippingCostCalculationCallback.toString()); + + assertEquals("05957e7f-803f-46f0-9100-1c3e10199b43", shippingCostCalculationCallback.getOrder_id(), "Order Id should be set"); + + //KlixOrderItems + KlixOrderItem[] orderItems = shippingCostCalculationCallback.getItems(); + assertEquals(2, orderItems.length, "Order items length should be 2"); + assertEquals(0, orderItems[1].getAmount().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(199.5).setScale(2, RoundingMode.HALF_UP)), "Item[1] amount should be set"); + assertEquals("Early Bird Ticket", orderItems[1].getLabel(), "Order Id should be set"); + assertEquals(0, orderItems[1].getTax_amount().setScale(3, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(41.895).setScale(3, RoundingMode.HALF_UP)), "Item[1] tax amount should be set"); + assertEquals(0, orderItems[1].getTotal_amount().setScale(3, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(241.395).setScale(3, RoundingMode.HALF_UP)), "Item[1] total amount should be set"); + assertEquals(0, orderItems[1].getTax_rate().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(0.21).setScale(2, RoundingMode.HALF_UP)), "Item[1] tax rate should be set"); + assertNull(orderItems[1].getOrder_item_id(), "Item[1] id should be null"); + assertEquals(0, orderItems[1].getQuantity().setScale(2, RoundingMode.HALF_UP) + .compareTo(new BigDecimal(1).setScale(2, RoundingMode.HALF_UP)), "Item[1] quantity should be set"); + assertEquals("PIECE", orderItems[1].getUnit(), "Item[1] unit should be set"); + assertEquals("UNKNOWN", orderItems[1].getType(), "Item[1] type should be set"); + assertNull(orderItems[1].getReference(), "Item[1] reference should be null"); + + assertEquals("omniva", shippingCostCalculationCallback.getShipping_method_id(), "Order shipping method id should be set"); + + //KlixAddress + assertEquals("Rīga", shippingCostCalculationCallback.getAddress().getCity(), "Address city should be set"); + assertEquals("Latvia", shippingCostCalculationCallback.getAddress().getCountry(), "Address country should be set"); + assertNull(shippingCostCalculationCallback.getAddress().getLatitude(), "Address latitude should be null"); + assertNull(shippingCostCalculationCallback.getAddress().getLongitude(), "Address longitude should be null"); + assertEquals("LV-1013", shippingCostCalculationCallback.getAddress().getPostal_code(), "Address postal code should be set"); + assertNull(shippingCostCalculationCallback.getAddress().getStreet(), "Address street should be null"); + + //KlixPickupPoint + assertEquals("Pa labi no galvenās ieejas", shippingCostCalculationCallback.getPickup_point().getComments(), "PickupPoint comments should be set"); + assertEquals("9111", shippingCostCalculationCallback.getPickup_point().getExternal_id(), "PickupPoint external id should be set"); + assertEquals("Rīgas Briāna ielas Rimi pakomāts", shippingCostCalculationCallback.getPickup_point().getName(), "PickupPoint name should be set"); + assertEquals("P-Pk.piegāde 10:00, izņemšana 17:00 Sestdienās piegāde 14:00,izņemšana 14:00", shippingCostCalculationCallback.getPickup_point().getService_hours(), "PickupPoint service hours should be set"); + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Test + void KlixClientSetup() { + KlixClient klixClient = new KlixClient(); + klixClient.setApiKey("Test Api Key"); + klixClient.setEnvironment(KlixEnvironment.STAGING); + klixClient.setMerchantCertificateId("Test Certificate Id"); + klixClient.setPrivateKey("Test Private Key".getBytes()); + + log.info(klixClient.toString()); + + assertEquals("Test Api Key", klixClient.getApiKey(), "Api key should be set"); + assertEquals(klixClient.getEnvironment(), KlixEnvironment.STAGING, "Klix environment should be set"); + assertEquals("Test Certificate Id", klixClient.getMerchantCertificateId(), "Merchant certificate id should be set"); + assertArrayEquals(klixClient.getPrivateKey(), ("Test Private Key".getBytes()), "Private key should be set"); } + + @Test + void ApproveOrderRequestSetup() { + KlixClient klixClient = new KlixClient(); + klixClient.setApiKey("Test Api Key"); + klixClient.setEnvironment(KlixEnvironment.STAGING); + klixClient.setMerchantCertificateId("Test Certificate Id"); + klixClient.setPrivateKey("Test Private Key".getBytes()); + + KlixConnector klixConnector = new KlixConnector(klixClient, new JwsEncoder()); + + ApproveOrderRequest approveOrderRequest = klixClient.approveOrder(klixConnector, "Test Merchant Id", "Test Order Id"); + + log.info(approveOrderRequest.toString()); + + assertEquals(approveOrderRequest.getMerchantId(), "Test Merchant Id"); + assertEquals(approveOrderRequest.getOrderId(), "Test Order Id"); + } + + @Test + void GetOrderRequestSetup() { + KlixClient klixClient = new KlixClient(); + klixClient.setApiKey("Test Api Key"); + klixClient.setEnvironment(KlixEnvironment.STAGING); + klixClient.setMerchantCertificateId("Test Certificate Id"); + klixClient.setPrivateKey("Test Private Key".getBytes()); + + KlixConnector klixConnector = new KlixConnector(klixClient, new JwsEncoder()); + + GetOrderRequest getOrderRequest = klixClient.retrieveOrderDetails(klixConnector, "Test Merchant Id", "Test Order Id"); + + log.info(getOrderRequest.toString()); + + assertEquals(getOrderRequest.getMerchantId(), "Test Merchant Id"); + assertEquals(getOrderRequest.getOrderId(), "Test Order Id"); + } + + @Test + void RejectOrderRequestSetup() { + KlixClient klixClient = new KlixClient(); + klixClient.setApiKey("Test Api Key"); + klixClient.setEnvironment(KlixEnvironment.STAGING); + klixClient.setMerchantCertificateId("Test Certificate Id"); + klixClient.setPrivateKey("Test Private Key".getBytes()); + + KlixConnector klixConnector = new KlixConnector(klixClient, new JwsEncoder()); + + RejectOrderRequest rejectOrderRequest = klixClient.rejectOrder(klixConnector, "Test Merchant Id", "Test Order Id", KlixRejectReason.PRICE_CHANGED); + + log.info(rejectOrderRequest.toString()); + + assertEquals(rejectOrderRequest.getMerchantId(), "Test Merchant Id"); + assertEquals(rejectOrderRequest.getOrderId(), "Test Order Id"); + assertEquals(rejectOrderRequest.getReasonCode(), KlixRejectReason.PRICE_CHANGED); + } + }