diff --git a/.gitignore b/.gitignore
index 9c45b58..0936194 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,7 @@ ExhaustiveVerify.java
*-result.md
*test-result*
*.log
+
+apache-maven-3.9.6
+
+src/main/docker
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b7b6742..3c92a24 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,13 @@
org.mockito
mockito-core
- 5.8.0
+ 5.11.0
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.11.0
test
@@ -81,7 +87,17 @@
maven-surefire-plugin
${surefire-plugin.version}
+
+ integration
+
+ -XX:+EnableDynamicAgentLoading
+ --add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.util=ALL-UNNAMED
+ --add-opens java.base/java.io=ALL-UNNAMED
+ --add-opens java.base/java.net=ALL-UNNAMED
+
+
-
\ No newline at end of file
+
diff --git a/src/main/java/com/tapsilat/TapsilatClient.java b/src/main/java/com/tapsilat/TapsilatClient.java
index 549d339..812d786 100644
--- a/src/main/java/com/tapsilat/TapsilatClient.java
+++ b/src/main/java/com/tapsilat/TapsilatClient.java
@@ -58,7 +58,7 @@ public OrganizationService organization() {
/**
* Backward compatibility method for createOrder.
*/
- public com.tapsilat.model.order.OrderResponse createOrder(com.tapsilat.model.order.OrderRequest request)
+ public com.tapsilat.model.order.OrderResponse createOrder(com.tapsilat.model.order.OrderCreateRequest request)
throws TapsilatException {
return orders.create(request);
}
diff --git a/src/main/java/com/tapsilat/builder/OrderRequestBuilder.java b/src/main/java/com/tapsilat/builder/OrderRequestBuilder.java
index e5c1ff2..9a944ab 100644
--- a/src/main/java/com/tapsilat/builder/OrderRequestBuilder.java
+++ b/src/main/java/com/tapsilat/builder/OrderRequestBuilder.java
@@ -10,7 +10,7 @@
import java.util.List;
/**
- * Builder class for creating OrderRequest objects easily.
+ * Builder class for creating OrderCreateRequest objects easily.
*/
public class OrderRequestBuilder {
@@ -18,8 +18,6 @@ public class OrderRequestBuilder {
private String currency;
private String locale;
private Buyer buyer;
- private String description;
- private String callbackUrl;
private String conversationId;
private List metadata = new ArrayList<>();
private ShippingAddress shippingAddress;
@@ -132,29 +130,8 @@ public OrderRequestBuilder buyer(String name, String surname, String email) {
* @return This builder
*/
public OrderRequestBuilder buyer(String name, String surname, String email, String phone, String identityNumber) {
- this.buyer = new Buyer(name, surname, email, phone, identityNumber);
- return this;
- }
-
- /**
- * Set the order description.
- *
- * @param description The description
- * @return This builder
- */
- public OrderRequestBuilder description(String description) {
- this.description = description;
- return this;
- }
-
- /**
- * Set the callback URL.
- *
- * @param callbackUrl The callback URL
- * @return This builder
- */
- public OrderRequestBuilder callbackUrl(String callbackUrl) {
- this.callbackUrl = callbackUrl;
+ this.buyer = new Buyer(name, surname, email, identityNumber);
+ this.buyer.setGsmNumber(phone);
return this;
}
@@ -316,7 +293,7 @@ public OrderRequestBuilder paymentOptions(List paymentOptions) {
private CheckoutDesign checkoutDesign;
private List enabledInstallments = new ArrayList<>();
private String externalReferenceId;
- private OrderCard orderCards;
+ private List orderCards = new ArrayList<>();
private BigDecimal paidAmount;
private String paymentFailureUrl;
private String paymentMode;
@@ -348,8 +325,13 @@ public OrderRequestBuilder externalReferenceId(String externalReferenceId) {
return this;
}
- public OrderRequestBuilder orderCards(OrderCard orderCards) {
- this.orderCards = orderCards;
+ public OrderRequestBuilder orderCards(List orderCards) {
+ this.orderCards = orderCards == null ? new ArrayList<>() : new ArrayList<>(orderCards);
+ return this;
+ }
+
+ public OrderRequestBuilder addOrderCard(OrderCard orderCard) {
+ this.orderCards.add(orderCard);
return this;
}
@@ -404,18 +386,16 @@ public OrderRequestBuilder submerchants(List submerchants) {
}
/**
- * Build the OrderRequest object.
+ * Build the OrderCreateRequest object.
*
- * @return The constructed OrderRequest
+ * @return The constructed OrderCreateRequest
*/
- public OrderRequest build() {
- OrderRequest orderRequest = new OrderRequest();
+ public OrderCreateRequest build() {
+ OrderCreateRequest orderRequest = new OrderCreateRequest();
orderRequest.setAmount(amount);
orderRequest.setCurrency(currency);
orderRequest.setLocale(locale);
orderRequest.setBuyer(buyer);
- orderRequest.setDescription(description);
- orderRequest.setCallbackUrl(callbackUrl);
orderRequest.setConversationId(conversationId);
orderRequest.setMetadata(metadata.isEmpty() ? null : metadata);
orderRequest.setShippingAddress(shippingAddress);
@@ -451,4 +431,4 @@ public OrderRequest build() {
public static OrderRequestBuilder newBuilder() {
return new OrderRequestBuilder();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/tapsilat/model/common/BasketItem.java b/src/main/java/com/tapsilat/model/common/BasketItem.java
index 43104e3..6797d33 100644
--- a/src/main/java/com/tapsilat/model/common/BasketItem.java
+++ b/src/main/java/com/tapsilat/model/common/BasketItem.java
@@ -61,6 +61,9 @@ public class BasketItem {
@JsonProperty("sub_merchant_price")
private String subMerchantPrice;
+ @JsonProperty("mcc")
+ private String mcc;
+
// Default constructor for Jackson
public BasketItem() {
}
@@ -145,6 +148,70 @@ public void setQuantityUnit(String quantityUnit) {
this.quantityUnit = quantityUnit;
}
+ public Double getCommissionAmount() {
+ return commissionAmount;
+ }
+
+ public void setCommissionAmount(Double commissionAmount) {
+ this.commissionAmount = commissionAmount;
+ }
+
+ public String getCoupon() {
+ return coupon;
+ }
+
+ public void setCoupon(String coupon) {
+ this.coupon = coupon;
+ }
+
+ public Double getPaidAmount() {
+ return paidAmount;
+ }
+
+ public void setPaidAmount(Double paidAmount) {
+ this.paidAmount = paidAmount;
+ }
+
+ public BasketItemPayer getPayer() {
+ return payer;
+ }
+
+ public void setPayer(BasketItemPayer payer) {
+ this.payer = payer;
+ }
+
+ public Double getQuantityFloat() {
+ return quantityFloat;
+ }
+
+ public void setQuantityFloat(Double quantityFloat) {
+ this.quantityFloat = quantityFloat;
+ }
+
+ public String getSubMerchantKey() {
+ return subMerchantKey;
+ }
+
+ public void setSubMerchantKey(String subMerchantKey) {
+ this.subMerchantKey = subMerchantKey;
+ }
+
+ public String getSubMerchantPrice() {
+ return subMerchantPrice;
+ }
+
+ public void setSubMerchantPrice(String subMerchantPrice) {
+ this.subMerchantPrice = subMerchantPrice;
+ }
+
+ public String getMcc() {
+ return mcc;
+ }
+
+ public void setMcc(String mcc) {
+ this.mcc = mcc;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o)
@@ -161,13 +228,23 @@ public boolean equals(Object o) {
Objects.equals(itemType, that.itemType) &&
Objects.equals(couponDiscount, that.couponDiscount) &&
Objects.equals(data, that.data) &&
- Objects.equals(quantityUnit, that.quantityUnit);
+ Objects.equals(quantityUnit, that.quantityUnit) &&
+ Objects.equals(commissionAmount, that.commissionAmount) &&
+ Objects.equals(coupon, that.coupon) &&
+ Objects.equals(paidAmount, that.paidAmount) &&
+ Objects.equals(payer, that.payer) &&
+ Objects.equals(quantityFloat, that.quantityFloat) &&
+ Objects.equals(subMerchantKey, that.subMerchantKey) &&
+ Objects.equals(subMerchantPrice, that.subMerchantPrice) &&
+ Objects.equals(mcc, that.mcc);
}
@Override
public int hashCode() {
return Objects.hash(id, name, category1, category2, price, quantity,
- itemType, couponDiscount, data, quantityUnit);
+ itemType, couponDiscount, data, quantityUnit, commissionAmount,
+ coupon, paidAmount, payer, quantityFloat, subMerchantKey,
+ subMerchantPrice, mcc);
}
@Override
@@ -183,6 +260,14 @@ public String toString() {
", couponDiscount=" + couponDiscount +
", data='" + data + '\'' +
", quantityUnit='" + quantityUnit + '\'' +
+ ", commissionAmount=" + commissionAmount +
+ ", coupon='" + coupon + '\'' +
+ ", paidAmount=" + paidAmount +
+ ", payer=" + payer +
+ ", quantityFloat=" + quantityFloat +
+ ", subMerchantKey='" + subMerchantKey + '\'' +
+ ", subMerchantPrice='" + subMerchantPrice + '\'' +
+ ", mcc='" + mcc + '\'' +
'}';
}
}
diff --git a/src/main/java/com/tapsilat/model/common/BillingAddress.java b/src/main/java/com/tapsilat/model/common/BillingAddress.java
index c2f0a7d..168c99b 100644
--- a/src/main/java/com/tapsilat/model/common/BillingAddress.java
+++ b/src/main/java/com/tapsilat/model/common/BillingAddress.java
@@ -36,86 +36,166 @@ public class BillingAddress {
@JsonProperty("vat_number")
private String vatNumber;
-
+
+ @JsonProperty("citizenship")
+ private String citizenship;
+
+ @JsonProperty("title")
+ private String title;
+
+ @JsonProperty("tax_office")
+ private String taxOffice;
+
+ @JsonProperty("neighbourhood")
+ private String neighbourhood;
+
+ @JsonProperty("street1")
+ private String street1;
+
+ @JsonProperty("street2")
+ private String street2;
+
+ @JsonProperty("street3")
+ private String street3;
+
// Default constructor for Jackson
- public BillingAddress() {}
-
+ public BillingAddress() {
+ }
+
public String getAddress() {
return address;
}
-
+
public void setAddress(String address) {
this.address = address;
}
-
+
public String getCity() {
return city;
}
-
+
public void setCity(String city) {
this.city = city;
}
-
+
public String getContactName() {
return contactName;
}
-
+
public void setContactName(String contactName) {
this.contactName = contactName;
}
-
+
public String getCountry() {
return country;
}
-
+
public void setCountry(String country) {
this.country = country;
}
-
+
public String getZipCode() {
return zipCode;
}
-
+
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
-
+
public String getBillingType() {
return billingType;
}
-
+
public void setBillingType(String billingType) {
this.billingType = billingType;
}
-
+
public String getContactPhone() {
return contactPhone;
}
-
+
public void setContactPhone(String contactPhone) {
this.contactPhone = contactPhone;
}
-
+
public String getDistrict() {
return district;
}
-
+
public void setDistrict(String district) {
this.district = district;
}
-
+
public String getVatNumber() {
return vatNumber;
}
-
+
public void setVatNumber(String vatNumber) {
this.vatNumber = vatNumber;
}
-
+
+ public String getCitizenship() {
+ return citizenship;
+ }
+
+ public void setCitizenship(String citizenship) {
+ this.citizenship = citizenship;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getTaxOffice() {
+ return taxOffice;
+ }
+
+ public void setTaxOffice(String taxOffice) {
+ this.taxOffice = taxOffice;
+ }
+
+ public String getNeighbourhood() {
+ return neighbourhood;
+ }
+
+ public void setNeighbourhood(String neighbourhood) {
+ this.neighbourhood = neighbourhood;
+ }
+
+ public String getStreet1() {
+ return street1;
+ }
+
+ public void setStreet1(String street1) {
+ this.street1 = street1;
+ }
+
+ public String getStreet2() {
+ return street2;
+ }
+
+ public void setStreet2(String street2) {
+ this.street2 = street2;
+ }
+
+ public String getStreet3() {
+ return street3;
+ }
+
+ public void setStreet3(String street3) {
+ this.street3 = street3;
+ }
+
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
BillingAddress that = (BillingAddress) o;
return Objects.equals(address, that.address) &&
Objects.equals(city, that.city) &&
@@ -125,13 +205,21 @@ public boolean equals(Object o) {
Objects.equals(billingType, that.billingType) &&
Objects.equals(contactPhone, that.contactPhone) &&
Objects.equals(district, that.district) &&
- Objects.equals(vatNumber, that.vatNumber);
+ Objects.equals(vatNumber, that.vatNumber) &&
+ Objects.equals(citizenship, that.citizenship) &&
+ Objects.equals(title, that.title) &&
+ Objects.equals(taxOffice, that.taxOffice) &&
+ Objects.equals(neighbourhood, that.neighbourhood) &&
+ Objects.equals(street1, that.street1) &&
+ Objects.equals(street2, that.street2) &&
+ Objects.equals(street3, that.street3);
}
@Override
public int hashCode() {
- return Objects.hash(address, city, contactName, country, zipCode,
- billingType, contactPhone, district, vatNumber);
+ return Objects.hash(address, city, contactName, country, zipCode,
+ billingType, contactPhone, district, vatNumber, citizenship, title,
+ taxOffice, neighbourhood, street1, street2, street3);
}
@Override
@@ -146,6 +234,13 @@ public String toString() {
", contactPhone='" + contactPhone + '\'' +
", district='" + district + '\'' +
", vatNumber='" + vatNumber + '\'' +
+ ", citizenship='" + citizenship + '\'' +
+ ", title='" + title + '\'' +
+ ", taxOffice='" + taxOffice + '\'' +
+ ", neighbourhood='" + neighbourhood + '\'' +
+ ", street1='" + street1 + '\'' +
+ ", street2='" + street2 + '\'' +
+ ", street3='" + street3 + '\'' +
'}';
}
}
diff --git a/src/main/java/com/tapsilat/model/common/Buyer.java b/src/main/java/com/tapsilat/model/common/Buyer.java
index f8821ab..c6cecb6 100644
--- a/src/main/java/com/tapsilat/model/common/Buyer.java
+++ b/src/main/java/com/tapsilat/model/common/Buyer.java
@@ -21,9 +21,6 @@ public class Buyer {
@JsonProperty("email")
private String email;
- @JsonProperty("phone")
- private String phone;
-
@JsonProperty("identity_number")
private String identityNumber;
@@ -79,20 +76,19 @@ public Buyer(String name, String surname, String email) {
}
/**
- * Constructor with all fields.
- *
+ * Constructor with required fields and optional identity number.
+ *
* @param name Buyer's first name (required)
* @param surname Buyer's last name (required)
* @param email Buyer's email address (required)
- * @param phone Buyer's phone number (optional)
* @param identityNumber Buyer's identity number (optional)
* @throws NullPointerException if any required parameter is null
+ * @see #setGsmNumber(String) to set buyer GSM number when needed
*/
- public Buyer(String name, String surname, String email, String phone, String identityNumber) {
+ public Buyer(String name, String surname, String email, String identityNumber) {
this.name = Objects.requireNonNull(name, "Buyer name cannot be null");
this.surname = Objects.requireNonNull(surname, "Buyer surname cannot be null");
this.email = Objects.requireNonNull(email, "Buyer email cannot be null");
- this.phone = phone;
this.identityNumber = identityNumber;
}
@@ -121,14 +117,6 @@ public void setEmail(String email) {
this.email = Objects.requireNonNull(email, "Buyer email cannot be null");
}
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
public String getIdentityNumber() {
return identityNumber;
}
@@ -235,13 +223,12 @@ public boolean equals(Object o) {
return Objects.equals(name, buyer.name) &&
Objects.equals(surname, buyer.surname) &&
Objects.equals(email, buyer.email) &&
- Objects.equals(phone, buyer.phone) &&
Objects.equals(identityNumber, buyer.identityNumber);
}
@Override
public int hashCode() {
- return Objects.hash(name, surname, email, phone, identityNumber);
+ return Objects.hash(name, surname, email, identityNumber);
}
@Override
@@ -250,8 +237,7 @@ public String toString() {
"name='" + name + '\'' +
", surname='" + surname + '\'' +
", email='" + email + '\'' +
- ", phone='" + phone + '\'' +
", identityNumber='" + identityNumber + '\'' +
'}';
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/tapsilat/model/common/OrderPFSubMerchant.java b/src/main/java/com/tapsilat/model/common/OrderPFSubMerchant.java
index e37aaf0..1bc4977 100644
--- a/src/main/java/com/tapsilat/model/common/OrderPFSubMerchant.java
+++ b/src/main/java/com/tapsilat/model/common/OrderPFSubMerchant.java
@@ -41,6 +41,12 @@ public class OrderPFSubMerchant {
@JsonProperty("terminal_no")
private String terminalNo;
+ @JsonProperty("national_id")
+ private String nationalId;
+
+ @JsonProperty("switch_id")
+ private String switchId;
+
public String getAddress() {
return address;
}
@@ -136,4 +142,20 @@ public String getTerminalNo() {
public void setTerminalNo(String terminalNo) {
this.terminalNo = terminalNo;
}
+
+ public String getNationalId() {
+ return nationalId;
+ }
+
+ public void setNationalId(String nationalId) {
+ this.nationalId = nationalId;
+ }
+
+ public String getSwitchId() {
+ return switchId;
+ }
+
+ public void setSwitchId(String switchId) {
+ this.switchId = switchId;
+ }
}
diff --git a/src/main/java/com/tapsilat/model/common/ShippingAddress.java b/src/main/java/com/tapsilat/model/common/ShippingAddress.java
index 6d1b038..6cabdac 100644
--- a/src/main/java/com/tapsilat/model/common/ShippingAddress.java
+++ b/src/main/java/com/tapsilat/model/common/ShippingAddress.java
@@ -24,12 +24,19 @@ public class ShippingAddress {
@JsonProperty("zip_code")
private String zipCode;
-
+
+ @JsonProperty("tracking_code")
+ private String trackingCode;
+
+ @JsonProperty("shipping_date")
+ private String shippingDate;
+
// Default constructor for Jackson
- public ShippingAddress() {}
-
+ public ShippingAddress() {
+ }
+
/**
- * Constructor with all fields.
+ * Constructor with required fields.
*/
public ShippingAddress(String address, String city, String contactName, String country, String zipCode) {
this.address = address;
@@ -38,62 +45,82 @@ public ShippingAddress(String address, String city, String contactName, String c
this.country = country;
this.zipCode = zipCode;
}
-
+
public String getAddress() {
return address;
}
-
+
public void setAddress(String address) {
this.address = address;
}
-
+
public String getCity() {
return city;
}
-
+
public void setCity(String city) {
this.city = city;
}
-
+
public String getContactName() {
return contactName;
}
-
+
public void setContactName(String contactName) {
this.contactName = contactName;
}
-
+
public String getCountry() {
return country;
}
-
+
public void setCountry(String country) {
this.country = country;
}
-
+
public String getZipCode() {
return zipCode;
}
-
+
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
-
+
+ public String getTrackingCode() {
+ return trackingCode;
+ }
+
+ public void setTrackingCode(String trackingCode) {
+ this.trackingCode = trackingCode;
+ }
+
+ public String getShippingDate() {
+ return shippingDate;
+ }
+
+ public void setShippingDate(String shippingDate) {
+ this.shippingDate = shippingDate;
+ }
+
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
ShippingAddress that = (ShippingAddress) o;
return Objects.equals(address, that.address) &&
Objects.equals(city, that.city) &&
Objects.equals(contactName, that.contactName) &&
Objects.equals(country, that.country) &&
- Objects.equals(zipCode, that.zipCode);
+ Objects.equals(zipCode, that.zipCode) &&
+ Objects.equals(trackingCode, that.trackingCode) &&
+ Objects.equals(shippingDate, that.shippingDate);
}
@Override
public int hashCode() {
- return Objects.hash(address, city, contactName, country, zipCode);
+ return Objects.hash(address, city, contactName, country, zipCode, trackingCode, shippingDate);
}
@Override
@@ -104,6 +131,8 @@ public String toString() {
", contactName='" + contactName + '\'' +
", country='" + country + '\'' +
", zipCode='" + zipCode + '\'' +
+ ", trackingCode='" + trackingCode + '\'' +
+ ", shippingDate='" + shippingDate + '\'' +
'}';
}
}
diff --git a/src/main/java/com/tapsilat/model/order/OrderRequest.java b/src/main/java/com/tapsilat/model/order/OrderCreateRequest.java
similarity index 92%
rename from src/main/java/com/tapsilat/model/order/OrderRequest.java
rename to src/main/java/com/tapsilat/model/order/OrderCreateRequest.java
index 92117e4..1e0ed97 100644
--- a/src/main/java/com/tapsilat/model/order/OrderRequest.java
+++ b/src/main/java/com/tapsilat/model/order/OrderCreateRequest.java
@@ -17,7 +17,7 @@
* billingAddress, basketItems, taxAmount, threeDForce, partialPayment,
* paymentMethods, paymentOptions.
*/
-public class OrderRequest {
+public class OrderCreateRequest {
@JsonProperty("amount")
private BigDecimal amount;
@@ -31,12 +31,6 @@ public class OrderRequest {
@JsonProperty("buyer")
private Buyer buyer;
- @JsonProperty("description")
- private String description;
-
- @JsonProperty("callback_url")
- private String callbackUrl;
-
@JsonProperty("conversation_id")
private String conversationId;
@@ -77,7 +71,7 @@ public class OrderRequest {
private String externalReferenceId;
@JsonProperty("order_cards")
- private OrderCard orderCards;
+ private List orderCards;
@JsonProperty("paid_amount")
private BigDecimal paidAmount;
@@ -112,8 +106,9 @@ public class OrderRequest {
@JsonProperty("consents")
private List consents;
+
// Default constructor for Jackson deserialization
- public OrderRequest() {
+ public OrderCreateRequest() {
}
/**
@@ -125,7 +120,7 @@ public OrderRequest() {
* @param buyer Buyer information (required)
* @throws NullPointerException if any required parameter is null
*/
- public OrderRequest(BigDecimal amount, String currency, String locale, Buyer buyer) {
+ public OrderCreateRequest(BigDecimal amount, String currency, String locale, Buyer buyer) {
this.amount = Objects.requireNonNull(amount, "Amount cannot be null");
this.currency = Objects.requireNonNull(currency, "Currency cannot be null");
this.locale = Objects.requireNonNull(locale, "Locale cannot be null");
@@ -165,22 +160,6 @@ public void setBuyer(Buyer buyer) {
this.buyer = Objects.requireNonNull(buyer, "Buyer cannot be null");
}
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getCallbackUrl() {
- return callbackUrl;
- }
-
- public void setCallbackUrl(String callbackUrl) {
- this.callbackUrl = callbackUrl;
- }
-
public String getConversationId() {
return conversationId;
}
@@ -325,11 +304,11 @@ public void setExternalReferenceId(String externalReferenceId) {
this.externalReferenceId = externalReferenceId;
}
- public OrderCard getOrderCards() {
+ public List getOrderCards() {
return orderCards;
}
- public void setOrderCards(OrderCard orderCards) {
+ public void setOrderCards(List orderCards) {
this.orderCards = orderCards;
}
@@ -421,15 +400,14 @@ public void setConsents(List consents) {
this.consents = consents;
}
+
@Override
public String toString() {
- return "OrderRequest{" +
+ return "OrderCreateRequest{" +
"amount=" + amount +
", currency='" + currency + '\'' +
", locale='" + locale + '\'' +
", buyer=" + buyer +
- ", description='" + description + '\'' +
- ", callbackUrl='" + callbackUrl + '\'' +
", conversationId='" + conversationId + '\'' +
", metadata=" + metadata +
", shippingAddress=" + shippingAddress +
diff --git a/src/main/java/com/tapsilat/model/organization/GetVposRequest.java b/src/main/java/com/tapsilat/model/organization/GetVposRequest.java
index eaa2870..56ebe3e 100644
--- a/src/main/java/com/tapsilat/model/organization/GetVposRequest.java
+++ b/src/main/java/com/tapsilat/model/organization/GetVposRequest.java
@@ -3,25 +3,15 @@
import com.fasterxml.jackson.annotation.JsonProperty;
public class GetVposRequest {
- @JsonProperty("currency")
- private String currency;
-
@JsonProperty("currency_id")
private String currencyId;
public GetVposRequest() {}
- public GetVposRequest(String currency) {
- this.currency = currency;
- }
-
- public String getCurrency() {
- return currency;
+ public GetVposRequest(String currencyId) {
+ this.currencyId = currencyId;
}
- public void setCurrency(String currency) {
- this.currency = currency;
- }
public String getCurrencyId() {
return currencyId;
diff --git a/src/main/java/com/tapsilat/model/organization/OrgCreateUserReq.java b/src/main/java/com/tapsilat/model/organization/OrgCreateUserRequest.java
similarity index 96%
rename from src/main/java/com/tapsilat/model/organization/OrgCreateUserReq.java
rename to src/main/java/com/tapsilat/model/organization/OrgCreateUserRequest.java
index 07ee99e..cd03c13 100644
--- a/src/main/java/com/tapsilat/model/organization/OrgCreateUserReq.java
+++ b/src/main/java/com/tapsilat/model/organization/OrgCreateUserRequest.java
@@ -2,7 +2,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
-public class OrgCreateUserReq {
+public class OrgCreateUserRequest {
@JsonProperty("email")
private String email;
@@ -27,7 +27,7 @@ public class OrgCreateUserReq {
@JsonProperty("reference_id")
private String referenceId;
- public OrgCreateUserReq() {}
+ public OrgCreateUserRequest() {}
public String getEmail() {
return email;
diff --git a/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyReq.java b/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyReq.java
deleted file mode 100644
index 710e403..0000000
--- a/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyReq.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.tapsilat.model.organization;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class OrgUserMobileVerifyReq {
- @JsonProperty("phone")
- private String phone;
-
- @JsonProperty("code")
- private String code;
-
- public OrgUserMobileVerifyReq() {}
-
- public OrgUserMobileVerifyReq(String phone, String code) {
- this.phone = phone;
- this.code = code;
- }
-
- public String getPhone() {
- return phone;
- }
-
- public void setPhone(String phone) {
- this.phone = phone;
- }
-
- public String getCode() {
- return code;
- }
-
- public void setCode(String code) {
- this.code = code;
- }
-}
diff --git a/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyRequest.java b/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyRequest.java
new file mode 100644
index 0000000..3def82e
--- /dev/null
+++ b/src/main/java/com/tapsilat/model/organization/OrgUserMobileVerifyRequest.java
@@ -0,0 +1,22 @@
+package com.tapsilat.model.organization;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OrgUserMobileVerifyRequest {
+ @JsonProperty("user_id")
+ private String userId;
+
+ public OrgUserMobileVerifyRequest() {}
+
+ public OrgUserMobileVerifyRequest(String userId) {
+ this.userId = userId;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+}
diff --git a/src/main/java/com/tapsilat/model/organization/OrgUserVerifyReq.java b/src/main/java/com/tapsilat/model/organization/OrgUserVerifyReq.java
deleted file mode 100644
index c854280..0000000
--- a/src/main/java/com/tapsilat/model/organization/OrgUserVerifyReq.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.tapsilat.model.organization;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public class OrgUserVerifyReq {
- @JsonProperty("email")
- private String email;
-
- @JsonProperty("code")
- private String code;
-
- public OrgUserVerifyReq() {}
-
- public OrgUserVerifyReq(String email, String code) {
- this.email = email;
- this.code = code;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public String getCode() {
- return code;
- }
-
- public void setCode(String code) {
- this.code = code;
- }
-}
diff --git a/src/main/java/com/tapsilat/model/organization/OrgUserVerifyRequest.java b/src/main/java/com/tapsilat/model/organization/OrgUserVerifyRequest.java
new file mode 100644
index 0000000..cb91caf
--- /dev/null
+++ b/src/main/java/com/tapsilat/model/organization/OrgUserVerifyRequest.java
@@ -0,0 +1,22 @@
+package com.tapsilat.model.organization;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OrgUserVerifyRequest {
+ @JsonProperty("user_id")
+ private String userId;
+
+ public OrgUserVerifyRequest() {}
+
+ public OrgUserVerifyRequest(String userId) {
+ this.userId = userId;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+}
diff --git a/src/main/java/com/tapsilat/model/organization/SetLimitUserRequest.java b/src/main/java/com/tapsilat/model/organization/SetLimitUserRequest.java
index 7e933ea..16ffcde 100644
--- a/src/main/java/com/tapsilat/model/organization/SetLimitUserRequest.java
+++ b/src/main/java/com/tapsilat/model/organization/SetLimitUserRequest.java
@@ -7,17 +7,14 @@ public class SetLimitUserRequest {
@JsonProperty("user_id")
private String userId;
- @JsonProperty("limit")
- private BigDecimal limit;
-
@JsonProperty("limit_id")
private String limitId;
public SetLimitUserRequest() {}
- public SetLimitUserRequest(String userId, BigDecimal limit) {
+ public SetLimitUserRequest(String userId, String limitId) {
this.userId = userId;
- this.limit = limit;
+ this.limitId = limitId;
}
public String getUserId() {
@@ -28,13 +25,6 @@ public void setUserId(String userId) {
this.userId = userId;
}
- public BigDecimal getLimit() {
- return limit;
- }
-
- public void setLimit(BigDecimal limit) {
- this.limit = limit;
- }
public String getLimitId() {
return limitId;
diff --git a/src/main/java/com/tapsilat/model/subscription/SubscriptionCreateRequest.java b/src/main/java/com/tapsilat/model/subscription/SubscriptionCreateRequest.java
index 3d9d5c6..a5a5a9f 100644
--- a/src/main/java/com/tapsilat/model/subscription/SubscriptionCreateRequest.java
+++ b/src/main/java/com/tapsilat/model/subscription/SubscriptionCreateRequest.java
@@ -34,6 +34,13 @@ public class SubscriptionCreateRequest {
@JsonProperty("period")
private Integer period;
+ /**
+ * @deprecated Use amount/cycle/period fields according to latest API shape.
+ */
+ @Deprecated
+ @JsonProperty("price_option")
+ private SubscriptionPriceOption priceOption;
+
@JsonProperty("success_url")
private String successUrl;
@@ -43,9 +50,6 @@ public class SubscriptionCreateRequest {
@JsonProperty("user")
private SubscriptionUser user;
- @JsonProperty("price_option")
- private SubscriptionPriceOption priceOption;
-
public BigDecimal getAmount() {
return amount;
}
@@ -118,6 +122,22 @@ public void setPeriod(Integer period) {
this.period = period;
}
+ /**
+ * @deprecated Use amount/cycle/period fields according to latest API shape.
+ */
+ @Deprecated
+ public SubscriptionPriceOption getPriceOption() {
+ return priceOption;
+ }
+
+ /**
+ * @deprecated Use amount/cycle/period fields according to latest API shape.
+ */
+ @Deprecated
+ public void setPriceOption(SubscriptionPriceOption priceOption) {
+ this.priceOption = priceOption;
+ }
+
public String getSuccessUrl() {
return successUrl;
}
@@ -141,12 +161,4 @@ public SubscriptionUser getUser() {
public void setUser(SubscriptionUser user) {
this.user = user;
}
-
- public SubscriptionPriceOption getPriceOption() {
- return priceOption;
- }
-
- public void setPriceOption(SubscriptionPriceOption priceOption) {
- this.priceOption = priceOption;
- }
}
diff --git a/src/main/java/com/tapsilat/model/subscription/SubscriptionPriceOption.java b/src/main/java/com/tapsilat/model/subscription/SubscriptionPriceOption.java
index 1db0d63..0e3cca5 100644
--- a/src/main/java/com/tapsilat/model/subscription/SubscriptionPriceOption.java
+++ b/src/main/java/com/tapsilat/model/subscription/SubscriptionPriceOption.java
@@ -4,8 +4,9 @@
import java.math.BigDecimal;
/**
- * Represents a price option for a subscription.
+ * @deprecated Use amount/cycle/period fields in SubscriptionCreateRequest.
*/
+@Deprecated
public class SubscriptionPriceOption {
@JsonProperty("count")
private Integer count;
@@ -36,12 +37,4 @@ public BigDecimal getPrice() {
public void setPrice(BigDecimal price) {
this.price = price;
}
-
- @Override
- public String toString() {
- return "SubscriptionPriceOption{" +
- "count=" + count +
- ", price=" + price +
- '}';
- }
}
diff --git a/src/main/java/com/tapsilat/service/OrderService.java b/src/main/java/com/tapsilat/service/OrderService.java
index 48c2289..0c2c5cf 100644
--- a/src/main/java/com/tapsilat/service/OrderService.java
+++ b/src/main/java/com/tapsilat/service/OrderService.java
@@ -20,7 +20,7 @@ public OrderService(CloseableHttpClient httpClient, TapsilatConfig config, Objec
super(httpClient, config, objectMapper);
}
- public OrderResponse create(OrderRequest orderRequest) throws TapsilatException {
+ public OrderResponse create(OrderCreateRequest orderRequest) throws TapsilatException {
try {
OrderRequestValidator.validateOrThrow(orderRequest);
OrderResponse response = executeRequest(
diff --git a/src/main/java/com/tapsilat/service/OrganizationService.java b/src/main/java/com/tapsilat/service/OrganizationService.java
index f29e865..6ccfbf9 100644
--- a/src/main/java/com/tapsilat/service/OrganizationService.java
+++ b/src/main/java/com/tapsilat/service/OrganizationService.java
@@ -124,7 +124,7 @@ public Map getSuborganizations(Integer page, Integer perPage) th
}
@SuppressWarnings("unchecked")
- public Map createUser(OrgCreateUserReq request) throws TapsilatException {
+ public Map createUser(OrgCreateUserRequest request) throws TapsilatException {
try {
return executeRequest(buildRequest("POST", TapsilatConstants.ENDPOINT_ORGANIZATION_USER_CREATE, request, null), Map.class);
} catch (IOException | ParseException e) {
@@ -133,7 +133,7 @@ public Map createUser(OrgCreateUserReq request) throws TapsilatE
}
@SuppressWarnings("unchecked")
- public Map verifyUser(OrgUserVerifyReq request) throws TapsilatException {
+ public Map verifyUser(OrgUserVerifyRequest request) throws TapsilatException {
try {
return executeRequest(buildRequest("POST", TapsilatConstants.ENDPOINT_ORGANIZATION_USER_VERIFY, request, null), Map.class);
} catch (IOException | ParseException e) {
@@ -142,7 +142,7 @@ public Map verifyUser(OrgUserVerifyReq request) throws TapsilatE
}
@SuppressWarnings("unchecked")
- public Map verifyUserMobile(OrgUserMobileVerifyReq request) throws TapsilatException {
+ public Map verifyUserMobile(OrgUserMobileVerifyRequest request) throws TapsilatException {
try {
return executeRequest(buildRequest("POST", TapsilatConstants.ENDPOINT_ORGANIZATION_USER_VERIFY_MOBILE, request, null), Map.class);
} catch (IOException | ParseException e) {
diff --git a/src/main/java/com/tapsilat/service/SubscriptionService.java b/src/main/java/com/tapsilat/service/SubscriptionService.java
index f1f8fa0..d3d2a26 100644
--- a/src/main/java/com/tapsilat/service/SubscriptionService.java
+++ b/src/main/java/com/tapsilat/service/SubscriptionService.java
@@ -9,6 +9,8 @@
import org.apache.hc.core5.http.ParseException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,6 +40,25 @@ public SubscriptionDetail get(SubscriptionGetRequest request) throws TapsilatExc
}
public List list(Integer page, Integer perPage) throws TapsilatException {
+ Map response = listResponse(page, perPage);
+ if (response == null) {
+ return Collections.emptyList();
+ }
+
+ Object itemsObject = response.get("items");
+ if (!(itemsObject instanceof List> items)) {
+ return Collections.emptyList();
+ }
+
+ List results = new ArrayList<>(items.size());
+ for (Object item : items) {
+ results.add(objectMapper.convertValue(item, SubscriptionListItem.class));
+ }
+ return results;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map listResponse(Integer page, Integer perPage) throws TapsilatException {
try {
Map params = new HashMap<>();
if (page != null)
@@ -45,10 +66,9 @@ public List list(Integer page, Integer perPage) throws Tap
if (perPage != null)
params.put("per_page", String.valueOf(perPage));
- SubscriptionListItem[] items = executeRequest(
+ return executeRequest(
buildRequest("GET", TapsilatConstants.ENDPOINT_SUBSCRIPTION_LIST, null, params),
- SubscriptionListItem[].class);
- return items != null ? java.util.Arrays.asList(items) : java.util.Collections.emptyList();
+ Map.class);
} catch (IOException | ParseException e) {
throw new TapsilatException("Failed to list subscriptions", e);
}
diff --git a/src/main/java/com/tapsilat/validation/OrderRequestValidator.java b/src/main/java/com/tapsilat/validation/OrderRequestValidator.java
index f6c2132..0adfd49 100644
--- a/src/main/java/com/tapsilat/validation/OrderRequestValidator.java
+++ b/src/main/java/com/tapsilat/validation/OrderRequestValidator.java
@@ -2,14 +2,14 @@
import com.tapsilat.constants.TapsilatConstants;
import com.tapsilat.model.common.Buyer;
-import com.tapsilat.model.order.OrderRequest;
+import com.tapsilat.model.order.OrderCreateRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
- * Validates OrderRequest objects before sending to Tapsilat API.
+ * Validates OrderCreateRequest objects before sending to Tapsilat API.
* Provides detailed validation error messages with field context.
*/
public final class OrderRequestValidator {
@@ -21,16 +21,16 @@ private OrderRequestValidator() {
}
/**
- * Validates an OrderRequest and returns list of validation errors.
+ * Validates an OrderCreateRequest and returns list of validation errors.
* Empty list indicates valid request.
*
* @param request The order request to validate
* @return List of validation error messages, empty if valid
* @throws ValidationException if request is null
*/
- public static List validate(OrderRequest request) {
+ public static List validate(OrderCreateRequest request) {
if (request == null) {
- throw new ValidationException("OrderRequest cannot be null");
+ throw new ValidationException("OrderCreateRequest cannot be null");
}
List errors = new ArrayList<>();
@@ -39,8 +39,6 @@ public static List validate(OrderRequest request) {
validateCurrency(request.getCurrency(), errors);
validateLocale(request.getLocale(), errors);
validateBuyer(request.getBuyer(), errors);
- validateDescription(request.getDescription(), errors);
- validateCallbackUrl(request.getCallbackUrl(), errors);
validateConversationId(request.getConversationId(), errors);
// Parity with Python: Clean GSM number if present
@@ -51,7 +49,7 @@ public static List validate(OrderRequest request) {
}
}
- // Parity with Python: Validate installments
+ // Parity with other SDKs: Validate installments
if (request.getEnabledInstallments() != null) {
validateInstallmentsList(request.getEnabledInstallments(), errors);
}
@@ -60,12 +58,12 @@ public static List validate(OrderRequest request) {
}
/**
- * Validates an OrderRequest and throws ValidationException if invalid.
+ * Validates an OrderCreateRequest and throws ValidationException if invalid.
*
* @param request The order request to validate
* @throws ValidationException if validation fails
*/
- public static void validateOrThrow(OrderRequest request) {
+ public static void validateOrThrow(OrderCreateRequest request) {
List errors = validate(request);
if (!errors.isEmpty()) {
throw new ValidationException("Order request validation failed", errors);
@@ -94,7 +92,6 @@ private static void validateLocale(String locale, List errors) {
private static void validateBuyer(Buyer buyer, List errors) {
if (buyer == null) {
- errors.add("Buyer information is required and cannot be null");
return;
}
@@ -113,21 +110,6 @@ private static void validateBuyer(Buyer buyer, List errors) {
}
}
- private static void validateDescription(String description, List errors) {
- if (description != null && description.length() > TapsilatConstants.MAX_DESCRIPTION_LENGTH) {
- errors.add("Description exceeds maximum length of " + TapsilatConstants.MAX_DESCRIPTION_LENGTH
- + " characters, got: " + description.length());
- }
- }
-
- private static void validateCallbackUrl(String callbackUrl, List errors) {
- if (callbackUrl != null && !callbackUrl.trim().isEmpty()) {
- if (!isValidUrl(callbackUrl)) {
- errors.add("Callback URL format is invalid: " + callbackUrl);
- }
- }
- }
-
private static void validateConversationId(String conversationId, List errors) {
if (conversationId != null && conversationId.length() > TapsilatConstants.MAX_CONVERSATION_ID_LENGTH) {
errors.add("Conversation ID exceeds maximum length of " + TapsilatConstants.MAX_CONVERSATION_ID_LENGTH
@@ -140,34 +122,29 @@ private static boolean isValidEmail(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
}
- private static boolean isValidUrl(String url) {
- // Basic URL validation - checks for http/https scheme
- return url != null && (url.startsWith("http://") || url.startsWith("https://"));
- }
-
+ /**
+ * Cleans GSM number by removing non-digit characters and enforcing minimum
+ * length.
+ * Universal logic: keeps only digits (and + prefix if present), min length 5.
+ */
public static String cleanGsmNumber(String phone, List errors) {
if (phone == null || phone.isEmpty())
return phone;
- String cleanPhone = phone.replaceAll("[\\s\\-\\(\\)]", "");
-
- if (!cleanPhone.replaceAll("\\+", "").matches("\\d+")) {
- errors.add("Invalid phone number format: " + phone);
- return cleanPhone;
+ boolean hasPlusPrefix = phone.trim().startsWith("+");
+ StringBuilder digitsBuilder = new StringBuilder();
+ for (char c : phone.toCharArray()) {
+ if (Character.isDigit(c)) {
+ digitsBuilder.append(c);
+ }
}
+ String digitsOnly = digitsBuilder.toString();
+ String cleanPhone = hasPlusPrefix ? "+" + digitsOnly : digitsOnly;
- if (cleanPhone.startsWith("+")) {
- if (cleanPhone.length() < 8)
- errors.add("International phone number too short: " + phone);
- } else if (cleanPhone.startsWith("00")) {
- if (cleanPhone.length() < 9)
- errors.add("International phone number (00 format) too short: " + phone);
- } else if (cleanPhone.startsWith("0")) {
- if (cleanPhone.length() < 7)
- errors.add("National phone number too short: " + phone);
- } else {
- if (cleanPhone.length() < 6)
- errors.add("Local phone number too short: " + phone);
+ // Enforce minimum length of 5 digits
+ if (digitsOnly.length() < 5) {
+ errors.add("Phone number too short (minimum 5 digits required): " + phone);
+ return cleanPhone;
}
return cleanPhone;
diff --git a/src/test/java/com/tapsilat/EnumTest.java b/src/test/java/com/tapsilat/EnumTest.java
deleted file mode 100644
index 5f3464b..0000000
--- a/src/test/java/com/tapsilat/EnumTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.tapsilat;
-
-import com.tapsilat.enums.BillingType;
-import com.tapsilat.enums.ItemType;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * Unit tests for enum classes.
- */
-class EnumTest {
-
- @Test
- void testBillingTypeEnum() {
- // Test enum values
- assertEquals("PERSONAL", BillingType.PERSONAL.getCode());
- assertEquals("BUSINESS", BillingType.BUSINESS.getCode());
-
- // Test fromCode
- assertEquals(BillingType.PERSONAL, BillingType.fromCode("PERSONAL"));
- assertEquals(BillingType.PERSONAL, BillingType.fromCode("personal"));
- assertEquals(BillingType.BUSINESS, BillingType.fromCode("BUSINESS"));
- assertEquals(BillingType.BUSINESS, BillingType.fromCode("business"));
-
- // Test invalid codes
- assertNull(BillingType.fromCode("INVALID"));
- assertNull(BillingType.fromCode(null));
- assertNull(BillingType.fromCode(""));
-
- // Test toString
- assertEquals("PERSONAL", BillingType.PERSONAL.toString());
- assertEquals("BUSINESS", BillingType.BUSINESS.toString());
- }
-
- @Test
- void testItemTypeEnum() {
- // Test enum values
- assertEquals("PHYSICAL", ItemType.PHYSICAL.getCode());
- assertEquals("DIGITAL", ItemType.DIGITAL.getCode());
- assertEquals("SERVICE", ItemType.SERVICE.getCode());
-
- // Test fromCode
- assertEquals(ItemType.PHYSICAL, ItemType.fromCode("PHYSICAL"));
- assertEquals(ItemType.PHYSICAL, ItemType.fromCode("physical"));
- assertEquals(ItemType.DIGITAL, ItemType.fromCode("DIGITAL"));
- assertEquals(ItemType.DIGITAL, ItemType.fromCode("digital"));
- assertEquals(ItemType.SERVICE, ItemType.fromCode("SERVICE"));
- assertEquals(ItemType.SERVICE, ItemType.fromCode("service"));
-
- // Test invalid codes
- assertNull(ItemType.fromCode("INVALID"));
- assertNull(ItemType.fromCode(null));
- assertNull(ItemType.fromCode(""));
-
- // Test toString
- assertEquals("PHYSICAL", ItemType.PHYSICAL.toString());
- assertEquals("DIGITAL", ItemType.DIGITAL.toString());
- assertEquals("SERVICE", ItemType.SERVICE.toString());
- }
-}
diff --git a/src/test/java/com/tapsilat/IntegrationTest.java b/src/test/java/com/tapsilat/IntegrationTest.java
deleted file mode 100644
index 80b98aa..0000000
--- a/src/test/java/com/tapsilat/IntegrationTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.tapsilat;
-
-import com.tapsilat.config.TapsilatConfig;
-import com.tapsilat.model.order.OrderRequest;
-import com.tapsilat.model.order.OrderResponse;
-import com.tapsilat.model.common.Buyer;
-import com.tapsilat.model.common.BillingAddress;
-import com.tapsilat.model.common.BasketItem;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.List;
-import static org.junit.jupiter.api.Assertions.*;
-
-public class IntegrationTest {
-
- private TapsilatClient client;
- private String token;
-
- @BeforeEach
- public void setUp() {
- token = System.getenv("TAPSILAT_API_KEY");
- if (token == null) {
- System.out.println("Warning: TAPSILAT_API_KEY not set. Skipping setup.");
- return;
- }
-
- TapsilatConfig config = new TapsilatConfig();
- config.setBaseUrl("https://panel.tapsilat.dev/api/v1");
- config.setBearerToken(token);
-
- client = new TapsilatClient(config);
- }
-
- @Test
- public void testCreateOrder() throws Exception {
- if (token == null) {
- System.out.println("Skipping testCreateOrder: No API Key");
- return;
- }
-
- OrderRequest order = new OrderRequest();
- order.setAmount(BigDecimal.valueOf(10.0));
- order.setCurrency("TRY");
- order.setLocale("tr");
-
- Buyer buyer = new Buyer();
- buyer.setId("123456");
- buyer.setName("John");
- buyer.setSurname("Doe");
- buyer.setEmail("john@doe.com");
- buyer.setGsmNumber("5321234567");
- buyer.setIdentityNumber("12345678901");
- buyer.setCity("Istanbul");
- buyer.setCountry("Turkey");
- buyer.setZipCode("34732");
- buyer.setIp("127.0.0.1");
- order.setBuyer(buyer);
-
- BillingAddress billing = new BillingAddress();
- billing.setContactName("John Doe");
- billing.setCity("Istanbul");
- billing.setCountry("Turkey");
- billing.setAddress("Test Address");
- billing.setZipCode("34732");
- order.setBillingAddress(billing);
-
- List items = new ArrayList<>();
- BasketItem item1 = new BasketItem();
- item1.setId("1");
- item1.setName("Item 1");
- item1.setPrice(5.0);
- item1.setItemType("PHYSICAL");
- items.add(item1);
-
- BasketItem item2 = new BasketItem();
- item2.setId("2");
- item2.setName("Item 2");
- item2.setPrice(5.0);
- item2.setItemType("PHYSICAL");
- items.add(item2);
-
- order.setBasketItems(items);
-
- OrderResponse response = client.orders().create(order);
- assertNotNull(response);
- assertNotNull(response.getReferenceId());
- System.out.println("Order Created: " + response.getReferenceId());
- }
-}
diff --git a/src/test/java/com/tapsilat/integration/IntegrationTest.java b/src/test/java/com/tapsilat/integration/IntegrationTest.java
new file mode 100644
index 0000000..db04053
--- /dev/null
+++ b/src/test/java/com/tapsilat/integration/IntegrationTest.java
@@ -0,0 +1,363 @@
+package com.tapsilat.integration;
+
+import com.tapsilat.TapsilatClient;
+import com.tapsilat.config.TapsilatConfig;
+import com.tapsilat.exception.TapsilatException;
+import com.tapsilat.model.order.*;
+import com.tapsilat.model.common.*;
+import com.tapsilat.model.subscription.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Integration tests that run against the real Tapsilat API.
+ * Reads TAPSILAT_API_KEY from environment variable or .env file.
+ * Mirrors tapsilat-py/tests/integration/integration_test.py
+ */
+@Tag("integration")
+public class IntegrationTest {
+
+ private TapsilatClient client;
+ private String token;
+
+ @BeforeEach
+ public void setUp() {
+ token = System.getenv("TAPSILAT_API_KEY");
+
+ if (token == null) {
+ token = loadFromEnvFile("TAPSILAT_API_KEY");
+ }
+
+ if (token == null) {
+ System.out.println("Warning: TAPSILAT_API_KEY not set. Skipping setup.");
+ return;
+ }
+
+ TapsilatConfig config = new TapsilatConfig();
+ config.setBaseUrl("https://panel.tapsilat.dev/api/v1");
+ config.setBearerToken(token);
+
+ client = new TapsilatClient(config);
+ }
+
+ /**
+ * Reads a key from the .env file in the project root.
+ * Supports KEY=VALUE format (no quotes handling needed for simple values).
+ */
+ private String loadFromEnvFile(String key) {
+ // Try multiple possible .env locations
+ String[] paths = { ".env", "../.env" };
+ for (String path : paths) {
+ File envFile = new File(path);
+ if (envFile.exists()) {
+ try (BufferedReader reader = new BufferedReader(new FileReader(envFile))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty() || line.startsWith("#"))
+ continue;
+ int eqIndex = line.indexOf('=');
+ if (eqIndex > 0) {
+ String k = line.substring(0, eqIndex).trim();
+ String v = line.substring(eqIndex + 1).trim();
+ if (k.equals(key)) {
+ return v;
+ }
+ }
+ }
+ } catch (IOException e) {
+ // Silently ignore read errors
+ }
+ }
+ }
+ return null;
+ }
+
+ // ==================== Scenario 1: Basic Order ====================
+
+ @Test
+ public void testScenario1_BasicOrder() throws Exception {
+ if (token == null) {
+ skipTest("testScenario1_BasicOrder");
+ return;
+ }
+
+ OrderCreateRequest order = new OrderCreateRequest();
+ order.setAmount(BigDecimal.valueOf(100.00));
+ order.setCurrency("TRY");
+ order.setLocale("tr");
+ order.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ OrderResponse response = client.orders().create(order);
+ assertNotNull(response);
+ assertNotNull(response.getReferenceId());
+ }
+
+ // ==================== Scenario 2: Order with Basket Items ====================
+
+ @Test
+ public void testScenario2_OrderWithBasketItems() throws Exception {
+ if (token == null) {
+ skipTest("testScenario2_OrderWithBasketItems");
+ return;
+ }
+
+ OrderCreateRequest order = new OrderCreateRequest();
+ order.setAmount(BigDecimal.valueOf(30.49));
+ order.setCurrency("TRY");
+ order.setLocale("tr");
+ order.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ BasketItem item1 = new BasketItem();
+ item1.setId("B001");
+ item1.setName("Item 1");
+ item1.setPrice(10.00);
+ item1.setItemType("PHYSICAL");
+
+ BasketItem item2 = new BasketItem();
+ item2.setId("B002");
+ item2.setName("Item 2");
+ item2.setPrice(20.49);
+ item2.setItemType("PHYSICAL");
+
+ order.setBasketItems(Arrays.asList(item1, item2));
+
+ OrderResponse response = client.orders().create(order);
+ assertNotNull(response);
+ assertNotNull(response.getReferenceId());
+ }
+
+ // ==================== Scenario 3: Order with Addresses ====================
+
+ @Test
+ public void testScenario3_OrderWithAddresses() throws Exception {
+ if (token == null) {
+ skipTest("testScenario3_OrderWithAddresses");
+ return;
+ }
+
+ OrderCreateRequest order = new OrderCreateRequest();
+ order.setAmount(BigDecimal.valueOf(25.00));
+ order.setCurrency("TRY");
+ order.setLocale("tr");
+ order.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ BillingAddress billing = new BillingAddress();
+ billing.setAddress("123 Main St");
+ billing.setCity("Istanbul");
+ billing.setCountry("TR");
+ billing.setContactName("John Doe");
+ billing.setZipCode("34000");
+ order.setBillingAddress(billing);
+
+ ShippingAddress shipping = new ShippingAddress();
+ shipping.setAddress("456 Oak Ave");
+ shipping.setCity("Istanbul");
+ shipping.setCountry("TR");
+ shipping.setContactName("Jane Doe");
+ shipping.setZipCode("34001");
+ order.setShippingAddress(shipping);
+
+ OrderResponse response = client.orders().create(order);
+ assertNotNull(response);
+ assertNotNull(response.getReferenceId());
+ }
+
+ // ==================== Scenario 4: Installments ====================
+
+ @Test
+ public void testScenario4_Installments() throws Exception {
+ if (token == null) {
+ skipTest("testScenario4_Installments");
+ return;
+ }
+
+ OrderCreateRequest order = new OrderCreateRequest();
+ order.setAmount(BigDecimal.valueOf(1200.00));
+ order.setCurrency("TRY");
+ order.setLocale("tr");
+ order.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+ order.setEnabledInstallments(Arrays.asList(2, 3, 6, 9));
+ order.setPaymentMethods(true);
+ order.setPaymentOptions(Arrays.asList("credit_card", "cash"));
+
+ OrderResponse response = client.orders().create(order);
+ assertNotNull(response);
+ assertNotNull(response.getReferenceId());
+ }
+
+ // ==================== Scenario 5: Order List ====================
+
+ @Test
+ public void testScenario5_GetOrders() throws Exception {
+ if (token == null) {
+ skipTest("testScenario5_GetOrders");
+ return;
+ }
+
+ try {
+ Map response = client.orders().list(1, 10, null, null, null, null, null);
+ assertNotNull(response);
+ } catch (TapsilatException e) {
+ fail("API Error: " + e.getMessage());
+ }
+ }
+
+ // ==================== Scenario 6: Organization Settings ====================
+
+ @Test
+ public void testScenario6_OrganizationSettings() throws Exception {
+ if (token == null) {
+ skipTest("testScenario6_OrganizationSettings");
+ return;
+ }
+
+ Map settings = client.orders().getOrganizationSettings();
+ assertNotNull(settings);
+ }
+
+ // ==================== Scenario 7: Subscription Lifecycle ====================
+
+ @Test
+ public void testScenario7_SubscriptionLifecycle() throws Exception {
+ if (token == null) {
+ skipTest("testScenario7_SubscriptionLifecycle");
+ return;
+ }
+
+ // Create Subscription
+ SubscriptionCreateRequest subRequest = new SubscriptionCreateRequest();
+ subRequest.setAmount(new BigDecimal("100.0"));
+ subRequest.setCurrency("TRY");
+ subRequest.setCycle(12);
+ subRequest.setPeriod(1);
+ subRequest.setPaymentDate(1);
+ subRequest.setTitle("Monthly Subscription");
+ subRequest.setExternalReferenceId("ext_ref_java_test");
+ subRequest.setSuccessUrl("https://example.com/success");
+ subRequest.setFailureUrl("https://example.com/failure");
+
+ SubscriptionUser user = new SubscriptionUser();
+ user.setFirstName("John");
+ user.setLastName("Doe");
+ user.setEmail("john.doe@example.com");
+ user.setPhone("+905551234567");
+ user.setIdentityNumber("12345678901");
+ subRequest.setUser(user);
+
+ SubscriptionCreateResponse createResponse = client.subscriptions().create(subRequest);
+ assertNotNull(createResponse);
+ assertNotNull(createResponse.getReferenceId());
+
+ String subRefId = createResponse.getReferenceId();
+
+ // Get Subscription
+ SubscriptionGetRequest getRequest = new SubscriptionGetRequest();
+ getRequest.setReferenceId(subRefId);
+ SubscriptionDetail subscription = client.subscriptions().get(getRequest);
+ assertNotNull(subscription);
+
+ // List Subscriptions
+ List subscriptions = client.subscriptions().list(1, 10);
+ assertNotNull(subscriptions);
+
+ // Redirect Subscription
+ SubscriptionRedirectRequest redirectRequest = new SubscriptionRedirectRequest();
+ redirectRequest.setSubscriptionId(subRefId);
+ SubscriptionRedirectResponse redirectResponse = client.subscriptions().redirect(redirectRequest);
+ assertNotNull(redirectResponse);
+ assertNotNull(redirectResponse.getUrl());
+
+ // Cancel Subscription
+ SubscriptionCancelRequest cancelRequest = new SubscriptionCancelRequest();
+ cancelRequest.setReferenceId(subRefId);
+ Map cancelResponse = client.subscriptions().cancel(cancelRequest);
+ assertNotNull(cancelResponse);
+ }
+
+ // ==================== Scenario 8: Order Operations Smoke Test
+ // ====================
+
+ @Test
+ public void testScenario8_OrderOperationsSmoke() throws Exception {
+ if (token == null) {
+ skipTest("testScenario8_OrderOperationsSmoke");
+ return;
+ }
+
+ // get_system_order_statuses
+ try {
+ Map statuses = client.orders().getSystemOrderStatuses();
+ assertNotNull(statuses);
+ } catch (TapsilatException ignored) {
+ }
+
+ // get_order_submerchants
+ try {
+ Map merchants = client.orders().getSubmerchants(1, 10);
+ assertNotNull(merchants);
+ } catch (TapsilatException ignored) {
+ }
+
+ String dummyRef = "dummy_ref_123";
+
+ // get_order_by_conversation_id
+ try {
+ client.orders().getByConversationId(dummyRef);
+ } catch (TapsilatException ignored) {
+ }
+
+ // order_accounting
+ try {
+ client.orders().accounting(new OrderAccountingRequest(dummyRef));
+ } catch (TapsilatException ignored) {
+ }
+
+ // order_postauth
+ try {
+ client.orders().postAuth(new OrderPostAuthRequest(new BigDecimal("10.0"), dummyRef));
+ } catch (TapsilatException ignored) {
+ }
+
+ // get_order_status
+ try {
+ client.orders().getStatus(dummyRef);
+ } catch (TapsilatException ignored) {
+ }
+
+ // get_order_transactions
+ try {
+ client.orders().getTransactions(dummyRef);
+ } catch (TapsilatException ignored) {
+ }
+
+ // cancel_order
+ try {
+ client.orders().cancel(dummyRef);
+ } catch (TapsilatException ignored) {
+ }
+
+ // get_order_payment_details
+ try {
+ client.orders().getPaymentDetails(dummyRef, null);
+ } catch (TapsilatException ignored) {
+ }
+ }
+
+ private void skipTest(String testName) {
+ System.out.println("Skipping " + testName + ": No API Key");
+ }
+}
diff --git a/src/test/java/com/tapsilat/TapsilatClientComprehensiveExample.java b/src/test/java/com/tapsilat/integration/TapsilatClientComprehensiveExample.java
similarity index 91%
rename from src/test/java/com/tapsilat/TapsilatClientComprehensiveExample.java
rename to src/test/java/com/tapsilat/integration/TapsilatClientComprehensiveExample.java
index 7b10fde..6815447 100644
--- a/src/test/java/com/tapsilat/TapsilatClientComprehensiveExample.java
+++ b/src/test/java/com/tapsilat/integration/TapsilatClientComprehensiveExample.java
@@ -1,9 +1,11 @@
-package com.tapsilat;
+package com.tapsilat.integration;
+
+import com.tapsilat.TapsilatClient;
import com.tapsilat.config.TapsilatConfig;
import com.tapsilat.enums.Currency;
import com.tapsilat.enums.Locale;
-import com.tapsilat.model.common.*; import com.tapsilat.model.order.*; import com.tapsilat.model.subscription.*;
+import com.tapsilat.model.common.*; import com.tapsilat.model.order.*;
import java.math.BigDecimal;
import java.util.Arrays;
@@ -25,7 +27,7 @@ public static void main(String[] args) {
// Create buyer information
Buyer buyer = new Buyer("John", "Doe", "johndoe@example.com");
- buyer.setPhone("+905321234567");
+ buyer.setGsmNumber("+905321234567");
buyer.setIdentityNumber("12345678901");
// Create shipping address
@@ -79,15 +81,13 @@ public static void main(String[] args) {
Metadata metadata2 = new Metadata("customer_segment", "premium");
// Build the complete order request
- OrderRequest orderRequest = new OrderRequest(
+ OrderCreateRequest orderRequest = new OrderCreateRequest(
new BigDecimal("150.00"),
Currency.TRY.getCode(),
Locale.TR.getCode(),
buyer
);
- orderRequest.setDescription("Test Order with Complete Information");
- orderRequest.setCallbackUrl("https://example.com/callback");
orderRequest.setConversationId("test-order-" + System.currentTimeMillis());
orderRequest.setShippingAddress(shippingAddress);
orderRequest.setBillingAddress(billingAddress);
@@ -96,7 +96,7 @@ public static void main(String[] args) {
orderRequest.setTaxAmount(0.0);
orderRequest.setThreeDForce(true);
orderRequest.setPartialPayment(false);
- orderRequest.setPaymentMethods(Arrays.asList("CREDIT_CARD", "DEBIT_CARD"));
+ orderRequest.setPaymentMethods(true);
orderRequest.setPaymentOptions(Arrays.asList("INSTALLMENT"));
// Create the order
diff --git a/src/test/java/com/tapsilat/TapsilatClientExample.java b/src/test/java/com/tapsilat/integration/TapsilatClientExample.java
similarity index 87%
rename from src/test/java/com/tapsilat/TapsilatClientExample.java
rename to src/test/java/com/tapsilat/integration/TapsilatClientExample.java
index 072e0f0..a49730a 100644
--- a/src/test/java/com/tapsilat/TapsilatClientExample.java
+++ b/src/test/java/com/tapsilat/integration/TapsilatClientExample.java
@@ -1,4 +1,6 @@
-package com.tapsilat;
+package com.tapsilat.integration;
+
+import com.tapsilat.TapsilatClient;
import com.tapsilat.builder.OrderRequestBuilder;
import com.tapsilat.config.TapsilatConfig;
@@ -7,11 +9,10 @@
import com.tapsilat.exception.TapsilatException;
import com.tapsilat.model.common.Buyer;
import com.tapsilat.model.common.Metadata;
-import com.tapsilat.model.order.OrderRequest;
+import com.tapsilat.model.order.OrderCreateRequest;
import com.tapsilat.model.order.OrderResponse;
import java.math.BigDecimal;
-import java.util.Arrays;
/**
* Example usage of the Tapsilat Java SDK.
@@ -48,16 +49,15 @@ private static void basicExample() {
TapsilatClient client = new TapsilatClient(config);
// Create buyer
- Buyer buyer = new Buyer("John", "Doe", "john.doe@example.com", "+9099999999", "11111111111");
+ Buyer buyer = new Buyer("John", "Doe", "john.doe@example.com", "11111111111");
+ buyer.setGsmNumber("+9099999999");
// Create order request
- OrderRequest orderRequest = new OrderRequest();
+ OrderCreateRequest orderRequest = new OrderCreateRequest();
orderRequest.setAmount(new BigDecimal("150.75"));
orderRequest.setCurrency("TRY");
orderRequest.setLocale("tr");
orderRequest.setBuyer(buyer);
- orderRequest.setDescription("Premium Subscription - Annual Plan");
- orderRequest.setCallbackUrl("https://your-website.com/payment-complete");
orderRequest.setConversationId("order-" + System.currentTimeMillis());
// Create order
@@ -90,13 +90,11 @@ private static void builderExample() {
TapsilatClient client = new TapsilatClient(config);
// Create order request using builder
- OrderRequest orderRequest = OrderRequestBuilder.newBuilder()
+ OrderCreateRequest orderRequest = OrderRequestBuilder.newBuilder()
.amount(150.75)
.currency("TRY")
.locale("tr")
.buyer("John", "Doe", "john.doe@example.com", "+9099999999", "11111111111")
- .description("Premium Subscription - Annual Plan")
- .callbackUrl("https://your-website.com/payment-complete")
.conversationId("order-" + System.currentTimeMillis())
.build();
@@ -130,13 +128,11 @@ private static void enumExample() {
TapsilatClient client = new TapsilatClient(config);
// Create order request using enums
- OrderRequest orderRequest = OrderRequestBuilder.newBuilder()
+ OrderCreateRequest orderRequest = OrderRequestBuilder.newBuilder()
.amount(new BigDecimal("99.99"))
.currency(Currency.USD)
.locale(Locale.EN)
.buyer("Jane", "Smith", "jane.smith@example.com")
- .description("Monthly Subscription")
- .callbackUrl("https://your-website.com/payment-complete")
.conversationId("order-" + System.currentTimeMillis())
.build();
@@ -175,13 +171,11 @@ private static void metadataExample() {
Metadata campaignMetadata = new Metadata("campaign", "summer2025");
// Create order request with metadata
- OrderRequest orderRequest = OrderRequestBuilder.newBuilder()
+ OrderCreateRequest orderRequest = OrderRequestBuilder.newBuilder()
.amount(150.75)
.currency(Currency.TRY)
.locale(Locale.TR)
.buyer("John", "Doe", "john.doe@example.com", "+9099999999", "11111111111")
- .description("Premium Subscription - Annual Plan")
- .callbackUrl("https://your-website.com/payment-complete")
.conversationId("order-" + System.currentTimeMillis())
.metadata(productMetadata)
.metadata(customerMetadata)
diff --git a/src/test/java/com/tapsilat/TapsilatClientLiveExample.java b/src/test/java/com/tapsilat/integration/TapsilatClientLiveExample.java
similarity index 89%
rename from src/test/java/com/tapsilat/TapsilatClientLiveExample.java
rename to src/test/java/com/tapsilat/integration/TapsilatClientLiveExample.java
index d3bbce9..115f70c 100644
--- a/src/test/java/com/tapsilat/TapsilatClientLiveExample.java
+++ b/src/test/java/com/tapsilat/integration/TapsilatClientLiveExample.java
@@ -1,11 +1,13 @@
-package com.tapsilat;
+package com.tapsilat.integration;
+
+import com.tapsilat.TapsilatClient;
import com.tapsilat.builder.OrderRequestBuilder;
import com.tapsilat.config.TapsilatConfig;
import com.tapsilat.enums.Currency;
import com.tapsilat.enums.Locale;
import com.tapsilat.exception.TapsilatException;
-import com.tapsilat.model.order.OrderRequest;
+import com.tapsilat.model.order.OrderCreateRequest;
import com.tapsilat.model.order.OrderResponse;
import java.math.BigDecimal;
@@ -36,13 +38,11 @@ public static void main(String[] args) {
TapsilatClient client = new TapsilatClient(config);
try {
- OrderRequest orderRequest = OrderRequestBuilder.newBuilder()
+ OrderCreateRequest orderRequest = OrderRequestBuilder.newBuilder()
.amount(new BigDecimal("10.00"))
.currency(Currency.TRY)
.locale(Locale.TR)
.buyer("John", "Doe", "john.doe@example.com", "+9099999999", "11111111111")
- .description("Live API test order")
- .callbackUrl("https://example.com/payment-complete")
.conversationId("order-" + System.currentTimeMillis())
.build();
diff --git a/src/test/java/com/tapsilat/TapsilatClientTest.java b/src/test/java/com/tapsilat/unit/TapsilatClientTest.java
similarity index 87%
rename from src/test/java/com/tapsilat/TapsilatClientTest.java
rename to src/test/java/com/tapsilat/unit/TapsilatClientTest.java
index 5b9d144..d9bb377 100644
--- a/src/test/java/com/tapsilat/TapsilatClientTest.java
+++ b/src/test/java/com/tapsilat/unit/TapsilatClientTest.java
@@ -1,4 +1,6 @@
-package com.tapsilat;
+package com.tapsilat.unit;
+
+import com.tapsilat.TapsilatClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tapsilat.builder.OrderRequestBuilder;
@@ -8,8 +10,9 @@
import com.tapsilat.exception.TapsilatException;
import com.tapsilat.model.common.Buyer;
import com.tapsilat.model.common.Metadata;
-import com.tapsilat.model.order.OrderRequest;
+import com.tapsilat.model.order.OrderCreateRequest;
import com.tapsilat.model.order.OrderResponse;
+import com.tapsilat.validation.OrderRequestValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -36,13 +39,11 @@ void setUp() {
@Test
void testOrderRequestBuilder() {
// Test builder pattern
- OrderRequest orderRequest = OrderRequestBuilder.newBuilder()
+ OrderCreateRequest orderRequest = OrderRequestBuilder.newBuilder()
.amount(150.75)
.currency(Currency.TRY)
.locale(Locale.TR)
.buyer("John", "Doe", "john.doe@example.com")
- .description("Test Order")
- .callbackUrl("https://example.com/callback")
.conversationId("test-order-123")
.metadata("testKey", "testValue")
.build();
@@ -57,8 +58,6 @@ void testOrderRequestBuilder() {
assertEquals("john.doe@example.com", orderRequest.getBuyer().getEmail());
// Verify optional fields
- assertEquals("Test Order", orderRequest.getDescription());
- assertEquals("https://example.com/callback", orderRequest.getCallbackUrl());
assertEquals("test-order-123", orderRequest.getConversationId());
assertNotNull(orderRequest.getMetadata());
assertEquals(1, orderRequest.getMetadata().size());
@@ -73,15 +72,16 @@ void testBuyerConstructor() {
assertEquals("John", buyer1.getName());
assertEquals("Doe", buyer1.getSurname());
assertEquals("john.doe@example.com", buyer1.getEmail());
- assertNull(buyer1.getPhone());
+ assertNull(buyer1.getGsmNumber());
assertNull(buyer1.getIdentityNumber());
// Test buyer with all fields
- Buyer buyer2 = new Buyer("Jane", "Smith", "jane.smith@example.com", "+1234567890", "123456789");
+ Buyer buyer2 = new Buyer("Jane", "Smith", "jane.smith@example.com", "123456789");
+ buyer2.setGsmNumber("+1234567890");
assertEquals("Jane", buyer2.getName());
assertEquals("Smith", buyer2.getSurname());
assertEquals("jane.smith@example.com", buyer2.getEmail());
- assertEquals("+1234567890", buyer2.getPhone());
+ assertEquals("+1234567890", buyer2.getGsmNumber());
assertEquals("123456789", buyer2.getIdentityNumber());
}
@@ -163,7 +163,7 @@ void testOrderRequestValidation() {
});
// Test order request with null amount
- OrderRequest invalidRequest1 = new OrderRequest();
+ OrderCreateRequest invalidRequest1 = new OrderCreateRequest();
invalidRequest1.setCurrency("TRY");
invalidRequest1.setLocale("tr");
invalidRequest1.setBuyer(new Buyer("John", "Doe", "john@example.com"));
@@ -172,15 +172,17 @@ void testOrderRequestValidation() {
client.createOrder(invalidRequest1);
});
- // Test order request with null buyer
- OrderRequest invalidRequest2 = new OrderRequest();
- invalidRequest2.setAmount(new BigDecimal("100"));
- invalidRequest2.setCurrency("TRY");
- invalidRequest2.setLocale("tr");
-
- assertThrows(TapsilatException.class, () -> {
- client.createOrder(invalidRequest2);
- });
+ // Buyer is optional for order validation
+ OrderCreateRequest requestWithoutBuyer = new OrderCreateRequest();
+ requestWithoutBuyer.setAmount(new BigDecimal("100"));
+ requestWithoutBuyer.setCurrency("TRY");
+ requestWithoutBuyer.setLocale("tr");
+ assertTrue(OrderRequestValidator.validate(requestWithoutBuyer).isEmpty());
+ }
+
+ @Test
+ void testOrderBuilderAcceptsNullOrderCardsList() {
+ assertDoesNotThrow(() -> OrderRequestBuilder.newBuilder().orderCards(null));
}
@Test
@@ -208,4 +210,4 @@ void testOrderResponseMapping() throws Exception {
assertEquals("0d058ce3-3e55-47d8-8ea1-2b42a60b362c", response.getOrderId());
assertEquals("497b0212-3a51-4c52-aa75-0f93f613dd7c", response.getReferenceId());
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/tapsilat/ModelTest.java b/src/test/java/com/tapsilat/unit/model/CommonModelTest.java
similarity index 97%
rename from src/test/java/com/tapsilat/ModelTest.java
rename to src/test/java/com/tapsilat/unit/model/CommonModelTest.java
index 56e1442..8590e58 100644
--- a/src/test/java/com/tapsilat/ModelTest.java
+++ b/src/test/java/com/tapsilat/unit/model/CommonModelTest.java
@@ -1,4 +1,4 @@
-package com.tapsilat;
+package com.tapsilat.unit.model;
import com.tapsilat.model.common.*; import com.tapsilat.model.order.*; import com.tapsilat.model.subscription.*;
import org.junit.jupiter.api.Test;
@@ -27,8 +27,10 @@ void testBuyerEqualsAndHashCode() {
assertNotEquals(buyer1.hashCode(), buyer3.hashCode());
// Test with optional fields
- Buyer buyer4 = new Buyer("John", "Doe", "john@example.com", "+123", "123");
- Buyer buyer5 = new Buyer("John", "Doe", "john@example.com", "+123", "123");
+ Buyer buyer4 = new Buyer("John", "Doe", "john@example.com", "123");
+ buyer4.setGsmNumber("+123");
+ Buyer buyer5 = new Buyer("John", "Doe", "john@example.com", "123");
+ buyer5.setGsmNumber("+123");
assertEquals(buyer4, buyer5);
assertEquals(buyer4.hashCode(), buyer5.hashCode());
}
diff --git a/src/test/java/com/tapsilat/unit/model/OrderDTOSerializationTest.java b/src/test/java/com/tapsilat/unit/model/OrderDTOSerializationTest.java
new file mode 100644
index 0000000..a5531c9
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/model/OrderDTOSerializationTest.java
@@ -0,0 +1,354 @@
+package com.tapsilat.unit.model;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.model.common.*;
+import com.tapsilat.model.order.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests JSON serialization of all order-related request and response DTOs.
+ * Mirrors test_order.py DTO serialization tests.
+ */
+class OrderDTOSerializationTest {
+
+ private ObjectMapper objectMapper;
+
+ @BeforeEach
+ void setUp() {
+ objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ }
+
+ // ==================== OrderCreateRequest ====================
+
+ @Test
+ void testOrderCreateRequest_BasicSerialization() throws Exception {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ String json = objectMapper.writeValueAsString(request);
+ @SuppressWarnings("unchecked")
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals(100, ((Number) map.get("amount")).intValue());
+ assertEquals("TRY", map.get("currency"));
+ assertEquals("tr", map.get("locale"));
+
+ @SuppressWarnings("unchecked")
+ Map buyer = (Map) map.get("buyer");
+ assertEquals("John", buyer.get("name"));
+ assertEquals("Doe", buyer.get("surname"));
+ assertEquals("test@example.com", buyer.get("email"));
+ }
+
+ @Test
+ void testOrderCreateRequest_OmitsNullFields() throws Exception {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("50"));
+ request.setCurrency("USD");
+ request.setLocale("en");
+ request.setBuyer(new Buyer("Jane", "Smith", "jane@test.com"));
+
+ String json = objectMapper.writeValueAsString(request);
+
+ assertFalse(json.contains("conversation_id"));
+ assertFalse(json.contains("metadata"));
+ assertFalse(json.contains("basket_items"));
+ assertFalse(json.contains("shipping_address"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderCreateRequest_FullSerialization() throws Exception {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("150"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+ request.setConversationId("conv-id-123");
+ request.setEnabledInstallments(Arrays.asList(3, 6));
+ request.setExternalReferenceId("ext-ref");
+ request.setPaymentMode("card");
+ request.setRedirectSuccessUrl("https://example.com/success");
+ request.setRedirectFailureUrl("https://example.com/fail");
+ request.setThreeDForce(true);
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("conv-id-123", map.get("conversation_id"));
+ assertEquals("ext-ref", map.get("external_reference_id"));
+ assertEquals("card", map.get("payment_mode"));
+ assertEquals("https://example.com/success", map.get("redirect_success_url"));
+ assertEquals("https://example.com/fail", map.get("redirect_failure_url"));
+ assertTrue((Boolean) map.get("three_d_force"));
+ }
+
+ // ==================== OrderResponse ====================
+
+ @Test
+ void testOrderResponse_Deserialization() throws Exception {
+ String json = "{\"order_id\":\"o123\", \"reference_id\":\"r456\", \"checkout_url\":\"https://checkout.test\"}";
+ OrderResponse response = objectMapper.readValue(json, OrderResponse.class);
+
+ assertEquals("o123", response.getOrderId());
+ assertEquals("r456", response.getReferenceId());
+ assertEquals("https://checkout.test", response.getCheckoutUrl());
+ }
+
+ @Test
+ void testOrderResponse_IgnoresUnknownFields() throws Exception {
+ String json = "{\"order_id\":\"o123\", \"unknown_field\":\"value\"}";
+ OrderResponse response = objectMapper.readValue(json, OrderResponse.class);
+ assertEquals("o123", response.getOrderId());
+ }
+
+ // ==================== BasketItem ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testBasketItem_Serialization() throws Exception {
+ BasketItem item = new BasketItem();
+ item.setId("BI101");
+ item.setName("Binocular");
+ item.setPrice(19.99);
+ item.setItemType("PHYSICAL");
+
+ String json = objectMapper.writeValueAsString(item);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("BI101", map.get("id"));
+ assertEquals("Binocular", map.get("name"));
+ assertEquals(19.99, ((Number) map.get("price")).doubleValue());
+ assertEquals("PHYSICAL", map.get("item_type"));
+ assertNull(map.get("category1"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testBasketItem_AllFields() throws Exception {
+ BasketItemPayer payer = new BasketItemPayer();
+ payer.setTitle("Company");
+
+ BasketItem item = new BasketItem();
+ item.setId("item1");
+ item.setPrice(100.0);
+ item.setName("Product");
+ item.setCategory1("C1");
+ item.setCategory2("C2");
+ item.setItemType("PHYSICAL");
+ item.setSubMerchantKey("key1");
+ item.setSubMerchantPrice("100.0");
+ item.setCoupon("SAVE10");
+ item.setCouponDiscount(10.0);
+ item.setQuantity(1);
+ item.setQuantityFloat(1.0);
+ item.setQuantityUnit("pcs");
+ item.setPaidAmount(90.0);
+ item.setData("extra");
+ item.setPayer(payer);
+ item.setCommissionAmount(5.0);
+ item.setMcc("1234");
+
+ String json = objectMapper.writeValueAsString(item);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("item1", map.get("id"));
+ assertEquals(5.0, ((Number) map.get("commission_amount")).doubleValue());
+ assertEquals(1.0, ((Number) map.get("quantity_float")).doubleValue());
+ assertEquals("Company", ((Map) map.get("payer")).get("title"));
+ }
+
+ // ==================== BillingAddress ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testBillingAddress_Serialization() throws Exception {
+ BillingAddress billing = new BillingAddress();
+ billing.setAddress("uskudar");
+ billing.setCity("Istanbul");
+ billing.setCountry("TR");
+ billing.setContactName("Jane Doe");
+
+ String json = objectMapper.writeValueAsString(billing);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("uskudar", map.get("address"));
+ assertEquals("Istanbul", map.get("city"));
+ assertEquals("TR", map.get("country"));
+ assertEquals("Jane Doe", map.get("contact_name"));
+ assertNull(map.get("zip_code"));
+ }
+
+ // ==================== Metadata ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testMetadata_Serialization() throws Exception {
+ Metadata meta = new Metadata("key", "value");
+
+ String json = objectMapper.writeValueAsString(meta);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("key", map.get("key"));
+ assertEquals("value", map.get("value"));
+ }
+
+ // ==================== OrderCard ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderCard_Serialization() throws Exception {
+ OrderCard card = new OrderCard();
+ card.setCardId("123456789");
+ card.setCardSequence(1);
+
+ String json = objectMapper.writeValueAsString(card);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("123456789", map.get("card_id"));
+ assertEquals(1, ((Number) map.get("card_sequence")).intValue());
+ }
+
+ // ==================== RefundOrderRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testRefundOrderRequest_Serialization() throws Exception {
+ RefundOrderRequest request = new RefundOrderRequest();
+ request.setAmount(new BigDecimal("50.0"));
+ request.setReferenceId("ref123");
+ request.setOrderItemId("item001");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("ref123", map.get("reference_id"));
+ assertEquals("item001", map.get("order_item_id"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testRefundOrderRequest_FullSerialization() throws Exception {
+ RefundOrderRequest request = new RefundOrderRequest();
+ request.setAmount(new BigDecimal("100.0"));
+ request.setReferenceId("ref456");
+ request.setOrderItemId("item002");
+ request.setOrderItemPaymentId("payment002");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("ref456", map.get("reference_id"));
+ assertEquals("item002", map.get("order_item_id"));
+ assertEquals("payment002", map.get("order_item_payment_id"));
+ }
+
+ // ==================== OrderAccountingRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderAccountingRequest_Serialization() throws Exception {
+ OrderAccountingRequest request = new OrderAccountingRequest("ref123");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("ref123", map.get("order_reference_id"));
+ }
+
+ // ==================== OrderPostAuthRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderPostAuthRequest_Serialization() throws Exception {
+ OrderPostAuthRequest request = new OrderPostAuthRequest(new BigDecimal("50.0"), "ref123");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("ref123", map.get("reference_id"));
+ }
+
+ // ==================== OrderPaymentTermCreateRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderPaymentTermCreateRequest_Serialization() throws Exception {
+ OrderPaymentTermCreateRequest request = new OrderPaymentTermCreateRequest();
+ request.setOrderId("order123");
+ request.setTermReferenceId("term-ref-create");
+ request.setAmount(new BigDecimal("200"));
+ request.setDueDate("2025-10-10 00:00:00");
+ request.setTermSequence(2);
+ request.setRequired(false);
+ request.setStatus("active");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("order123", map.get("order_id"));
+ assertEquals("term-ref-create", map.get("term_reference_id"));
+ assertEquals(2, ((Number) map.get("term_sequence")).intValue());
+ assertEquals(false, map.get("required"));
+ assertEquals("active", map.get("status"));
+ }
+
+ // ==================== OrderManualCallbackRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testOrderManualCallbackRequest_Serialization() throws Exception {
+ OrderManualCallbackRequest request = new OrderManualCallbackRequest("ref-id");
+ request.setConversationId("conv-id");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("ref-id", map.get("reference_id"));
+ assertEquals("conv-id", map.get("conversation_id"));
+ }
+
+ // ==================== AddBasketItemRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testAddBasketItemRequest_Serialization() throws Exception {
+ BasketItem item = new BasketItem();
+ item.setId("item-1");
+ item.setName("Widget");
+ item.setPrice(9.99);
+
+ AddBasketItemRequest request = new AddBasketItemRequest("order-ref", item);
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("order-ref", map.get("order_reference_id"));
+ assertNotNull(map.get("basket_item"));
+ }
+
+ // ==================== RemoveBasketItemRequest ====================
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testRemoveBasketItemRequest_Serialization() throws Exception {
+ RemoveBasketItemRequest request = new RemoveBasketItemRequest("order-ref", "item-1");
+
+ String json = objectMapper.writeValueAsString(request);
+ Map map = objectMapper.readValue(json, Map.class);
+
+ assertEquals("order-ref", map.get("order_reference_id"));
+ assertEquals("item-1", map.get("basket_item_id"));
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/model/OrderModelTest.java b/src/test/java/com/tapsilat/unit/model/OrderModelTest.java
new file mode 100644
index 0000000..9d8775b
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/model/OrderModelTest.java
@@ -0,0 +1,144 @@
+package com.tapsilat.unit.model;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.enums.Currency;
+import com.tapsilat.enums.Locale;
+import com.tapsilat.model.common.*;
+import com.tapsilat.model.order.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OrderModelTest {
+ private ObjectMapper mapper;
+
+ @BeforeEach
+ void setUp() {
+ mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ }
+
+ @Test
+ void testOrderCreateRequestSerializationFull() throws JsonProcessingException {
+ Buyer buyer = new Buyer("John", "Doe", "test@example.com");
+ buyer.setGsmNumber("+905551234567");
+ buyer.setIdentityNumber("11111111111");
+ buyer.setCity("Istanbul");
+ buyer.setCountry("TR");
+ buyer.setRegistrationAddress("Reg Address");
+ buyer.setIp("127.0.0.1");
+
+ BasketItemPayer payer = new BasketItemPayer();
+ payer.setAddress("Payer Addr");
+ payer.setReferenceId("payer-ref");
+ payer.setTaxOffice("TaxOfc");
+ payer.setType("PERSONAL");
+ BasketItem item = new BasketItem();
+ item.setId("itm-1");
+ item.setName("Laptop");
+ item.setItemType("PHYSICAL");
+ item.setPrice(100.0);
+ item.setQuantity(1);
+ item.setPayer(payer);
+
+ BillingAddress billing = new BillingAddress();
+ billing.setAddress("Bill Addr");
+ billing.setBillingType("PERSONAL");
+ billing.setCountry("TR");
+ billing.setCity("Istanbul");
+ billing.setContactName("Contact");
+
+ OrderConsent consent = new OrderConsent("Consent Title", "http://consent");
+
+ OrderCard card = new OrderCard();
+ card.setCardId("card-1");
+ card.setCardSequence(1);
+
+ PaymentTerm term = new PaymentTerm();
+ term.setAmount(new BigDecimal("50.0"));
+ term.setTermReferenceId("term-ref");
+ term.setTermSequence(1);
+ term.setStatus("PENDING");
+
+ OrderPFSubMerchant pfSub = new OrderPFSubMerchant();
+ pfSub.setId("pf-1");
+ pfSub.setName("PF Name");
+ pfSub.setMcc("1234");
+
+ ShippingAddress shipping = new ShippingAddress("Ship Addr", "City", "Contact Name", "TR", "34000");
+
+ SubOrganization subOrg = new SubOrganization();
+ subOrg.setOrganizationName("Org Name");
+ subOrg.setLegalCompanyTitle("Legal Title");
+ subOrg.setSubMerchantKey("sm-key");
+ subOrg.setAcquirer("ACQ");
+
+ Submerchant subMerch = new Submerchant();
+ subMerch.setAmount(new BigDecimal("40.0"));
+ subMerch.setOrderBasketItemId("itm-1");
+
+ CheckoutDesign design = new CheckoutDesign();
+ design.setInputBackgroundColor("#FFF");
+
+ OrderCreateRequest order = new OrderCreateRequest();
+ order.setAmount(new BigDecimal("150.0"));
+ order.setCurrency(Currency.TRY.getCode());
+ order.setLocale(Locale.TR.getCode());
+ order.setBuyer(buyer);
+ order.setBasketItems(Collections.singletonList(item));
+ order.setBillingAddress(billing);
+ order.setCheckoutDesign(design);
+ order.setConsents(Collections.singletonList(consent));
+ order.setOrderCards(Collections.singletonList(card));
+ order.setPaymentTerms(Collections.singletonList(term));
+ order.setPfSubMerchant(pfSub);
+ order.setShippingAddress(shipping);
+ order.setSubOrganization(subOrg);
+ order.setSubmerchants(Collections.singletonList(subMerch));
+ order.setThreeDForce(true);
+
+ String json = mapper.writeValueAsString(order);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals(150.0, node.get("amount").asDouble());
+ assertEquals("TRY", node.get("currency").asText());
+ assertEquals("tr", node.get("locale").asText());
+ assertEquals("John", node.get("buyer").get("name").asText());
+ assertEquals("TR", node.get("buyer").get("country").asText());
+ assertEquals("Laptop", node.get("basket_items").get(0).get("name").asText());
+ assertEquals("Payer Addr", node.get("basket_items").get(0).get("payer").get("address").asText());
+ assertEquals("Bill Addr", node.get("billing_address").get("address").asText());
+ assertEquals("#FFF", node.get("checkout_design").get("input_background_color").asText());
+ assertEquals("Consent Title", node.get("consents").get(0).get("title").asText());
+ assertEquals("card-1", node.get("order_cards").get(0).get("card_id").asText());
+ assertEquals(50.0, node.get("payment_terms").get(0).get("amount").asDouble());
+ assertEquals("PF Name", node.get("pf_sub_merchant").get("name").asText());
+ assertEquals("Ship Addr", node.get("shipping_address").get("address").asText());
+ assertEquals("ACQ", node.get("sub_organization").get("acquirer").asText());
+ assertEquals(40.0, node.get("submerchants").get(0).get("amount").asDouble());
+ assertTrue(node.get("three_d_force").asBoolean());
+ }
+
+ @Test
+ void testRefundOrderRequestSerialization() throws JsonProcessingException {
+ RefundOrderRequest refund = new RefundOrderRequest();
+ refund.setReferenceId("ref123");
+ refund.setAmount(new BigDecimal("50.0"));
+ refund.setOrderItemId("item001");
+
+ String json = mapper.writeValueAsString(refund);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals("50.0", node.get("amount").asText());
+ assertEquals("ref123", node.get("reference_id").asText());
+ assertEquals("item001", node.get("order_item_id").asText());
+ assertFalse(node.has("order_item_payment_id"));
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/model/OrganizationModelTest.java b/src/test/java/com/tapsilat/unit/model/OrganizationModelTest.java
new file mode 100644
index 0000000..ef4210e
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/model/OrganizationModelTest.java
@@ -0,0 +1,46 @@
+package com.tapsilat.unit.model;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.model.organization.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OrganizationModelTest {
+ private ObjectMapper mapper;
+
+ @BeforeEach
+ void setUp() {
+ mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ }
+
+ @Test
+ void testCallbackURLDTOSerialization() throws JsonProcessingException {
+ CallbackURLDTO dto = new CallbackURLDTO();
+ dto.setCallbackUrl("http://success.url");
+ dto.setFailCallbackUrl("http://fail.url");
+
+ String json = mapper.writeValueAsString(dto);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals("http://success.url", node.get("callback_url").asText());
+ assertEquals("http://fail.url", node.get("fail_callback_url").asText());
+ }
+
+ @Test
+ void testSetLimitUserRequestSerialization() throws JsonProcessingException {
+ SetLimitUserRequest request = new SetLimitUserRequest();
+ request.setUserId("user-1");
+ request.setLimitId("limit-1");
+
+ String json = mapper.writeValueAsString(request);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals("user-1", node.get("user_id").asText());
+ assertEquals("limit-1", node.get("limit_id").asText());
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/model/SubscriptionModelTest.java b/src/test/java/com/tapsilat/unit/model/SubscriptionModelTest.java
new file mode 100644
index 0000000..42f7ca1
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/model/SubscriptionModelTest.java
@@ -0,0 +1,64 @@
+package com.tapsilat.unit.model;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.model.subscription.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SubscriptionModelTest {
+ private ObjectMapper mapper;
+
+ @BeforeEach
+ void setUp() {
+ mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ }
+
+ @Test
+ void testSubscriptionCreateRequestSerialization() throws JsonProcessingException {
+ SubscriptionUser buyer = new SubscriptionUser();
+ buyer.setFirstName("Sub");
+ buyer.setLastName("User");
+ buyer.setEmail("subuser@example.com");
+
+ SubscriptionCreateRequest request = new SubscriptionCreateRequest();
+ request.setAmount(new BigDecimal("99.99"));
+ request.setCurrency("TRY");
+ request.setUser(buyer);
+ request.setCycle(1);
+ request.setPeriod(1);
+ request.setExternalReferenceId("sub-ref-123");
+ request.setSuccessUrl("http://callback.test");
+ request.setPriceOption(new SubscriptionPriceOption(2, new BigDecimal("49.99")));
+
+ String json = mapper.writeValueAsString(request);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals(99.99, node.get("amount").asDouble());
+ assertEquals("TRY", node.get("currency").asText());
+ assertEquals(1, node.get("cycle").asInt());
+ assertEquals(1, node.get("period").asInt());
+ assertEquals("sub-ref-123", node.get("external_reference_id").asText());
+ assertEquals("http://callback.test", node.get("success_url").asText());
+ assertEquals("Sub", node.get("user").get("first_name").asText());
+ assertEquals(2, node.get("price_option").get("count").asInt());
+ assertEquals(49.99, node.get("price_option").get("price").asDouble());
+ }
+
+ @Test
+ void testSubscriptionCancelRequestSerialization() throws JsonProcessingException {
+ SubscriptionCancelRequest request = new SubscriptionCancelRequest();
+ request.setReferenceId("ref123");
+
+ String json = mapper.writeValueAsString(request);
+ JsonNode node = mapper.readTree(json);
+
+ assertEquals("ref123", node.get("reference_id").asText());
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/service/OrderServiceTest.java b/src/test/java/com/tapsilat/unit/service/OrderServiceTest.java
new file mode 100644
index 0000000..7c16a64
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/service/OrderServiceTest.java
@@ -0,0 +1,704 @@
+package com.tapsilat.unit.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.config.TapsilatConfig;
+import com.tapsilat.exception.TapsilatException;
+import com.tapsilat.model.common.BasketItem;
+import com.tapsilat.model.common.Buyer;
+import com.tapsilat.model.order.*;
+import com.tapsilat.service.OrderService;
+import com.tapsilat.unit.utils.MockHttpClient;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OrderServiceTest {
+
+ private MockHttpClient httpClient;
+ private OrderService orderService;
+ private ObjectMapper objectMapper;
+
+ @BeforeEach
+ void setUp() {
+ TapsilatConfig config = new TapsilatConfig();
+ config.setBaseUrl("https://api.test");
+ config.setBearerToken("test-token");
+
+ objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ httpClient = new MockHttpClient();
+ orderService = new OrderService(httpClient, config, objectMapper);
+ }
+
+ @Test
+ void testCreateOrderSuccess() throws Exception {
+ String jsonResponse = "{\"order_id\":\"mock-order-123\", \"reference_id\":\"mock-ref-123\", \"checkout_url\":\"https://checkout.test\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@test.com"));
+
+ OrderResponse response = orderService.create(request);
+
+ assertNotNull(response);
+ assertEquals("mock-order-123", response.getOrderId());
+ assertEquals("mock-ref-123", response.getReferenceId());
+ assertEquals("https://checkout.test", response.getCheckoutUrl());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/create"));
+ }
+
+ @Test
+ void testCreateOrderWithBasketItems() throws Exception {
+ String jsonResponse = "{\"order_id\":\"order_basket\", \"reference_id\":\"ref_basket\", \"checkout_url\":\"https://checkout.test\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("30.49"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("Test", "User", "test@test.com"));
+
+ BasketItem item1 = new BasketItem();
+ item1.setId("B001");
+ item1.setName("Item 1");
+ item1.setPrice(10.0);
+ item1.setItemType("PHYSICAL");
+
+ BasketItem item2 = new BasketItem();
+ item2.setId("B002");
+ item2.setName("Item 2");
+ item2.setPrice(20.49);
+
+ request.setBasketItems(Arrays.asList(item1, item2));
+
+ OrderResponse response = orderService.create(request);
+
+ assertNotNull(response);
+ assertEquals("order_basket", response.getOrderId());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/create"));
+ }
+
+ // ==================== get ====================
+
+ @Test
+ void testGetOrderSuccess() throws Exception {
+ String jsonResponse = "{\"status\":8, \"reference_id\":\"mock-ref-123\", \"checkout_url\":\"https://checkout.test\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ OrderResponse response = orderService.get("mock-ref-123");
+
+ assertNotNull(response);
+ assertEquals("https://checkout.test", response.getCheckoutUrl());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/mock-ref-123"));
+ }
+
+ @Test
+ void testGetOrderFailure() throws Exception {
+ String errorJson = "{\"code\": 101160, \"error\": \"ORDER_ORDER_DETAIL_ORDER_NOT_FOUND\"}";
+ httpClient.setResponse(400, errorJson);
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.get("mock-failed-ref");
+ });
+
+ assertTrue(exception.getMessage().contains("ORDER_ORDER_DETAIL_ORDER_NOT_FOUND"));
+ }
+
+ // ==================== getByConversationId ====================
+
+ @Test
+ void testGetOrderByConversationIdSuccess() throws Exception {
+ String jsonResponse = "{\"checkout_url\":\"https://checkout.test\", \"status\":8}";
+ httpClient.setResponse(200, jsonResponse);
+
+ OrderResponse response = orderService.getByConversationId("mock-conv-id");
+
+ assertNotNull(response);
+ assertEquals("https://checkout.test", response.getCheckoutUrl());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/conversation/mock-conv-id"));
+ }
+
+ @Test
+ void testGetOrderByConversationIdFailure() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 101160, \"error\": \"ORDER_ORDER_DETAIL_ORDER_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.getByConversationId("mock-conv-fail");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== list ====================
+
+ @Test
+ void testGetOrderList() throws Exception {
+ String jsonResponse = "{\"page\":1, \"per_page\":3, \"rows\":[{},{},{}], \"total\":24, \"total_page\":8}";
+ httpClient.setResponse(200, jsonResponse);
+
+ Map response = orderService.list(1, 3, null, null, null, null, null);
+
+ assertNotNull(response);
+ assertEquals(1, ((Number) response.get("page")).intValue());
+ assertEquals(3, ((Number) response.get("per_page")).intValue());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/list"));
+ assertTrue(captured.getUri().toString().contains("page=1"));
+ assertTrue(captured.getUri().toString().contains("per_page=3"));
+ }
+
+ // ==================== getSubmerchants ====================
+
+ @Test
+ void testGetSubmerchants() throws Exception {
+ String jsonResponse = "{\"page\":1, \"per_page\":2, \"row\":[{},{}], \"total\":10, \"total_pages\":5}";
+ httpClient.setResponse(200, jsonResponse);
+
+ Map response = orderService.getSubmerchants(1, 2);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/submerchants"));
+ }
+
+ // ==================== cancel ====================
+
+ @Test
+ void testCancelOrderSuccess() throws Exception {
+ String jsonResponse = "{\"is_success\":true, \"error\":\"ORDER_CANCEL_SUCCESS\", \"status\":\"101645\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ Map response = orderService.cancel("mock-ref-123");
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("is_success"));
+ assertEquals("ORDER_CANCEL_SUCCESS", response.get("error"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/cancel"));
+ }
+
+ @Test
+ void testCancelOrderNotFound() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 101550, \"error\": \"ORDER_CANCEL_ORDER_GET_ORDER_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.cancel("mock-not-found");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== refund ====================
+
+ @Test
+ void testRefundOrderSuccess() throws Exception {
+ String jsonResponse = "{\"is_success\":true, \"error\":\"REFUND_SUCCESSFUL\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ RefundOrderRequest request = new RefundOrderRequest();
+ request.setReferenceId("mock-ref-123");
+ request.setAmount(new BigDecimal("50.0"));
+
+ Map response = orderService.refund(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("is_success"));
+ assertEquals("REFUND_SUCCESSFUL", response.get("error"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/refund"));
+ }
+
+ @Test
+ void testRefundOrderWithOptionalFields() throws Exception {
+ httpClient.setResponse(200, "{\"is_success\":true}");
+
+ RefundOrderRequest request = new RefundOrderRequest();
+ request.setReferenceId("ref456");
+ request.setAmount(new BigDecimal("100.0"));
+ request.setOrderItemId("item002");
+ request.setOrderItemPaymentId("payment002");
+
+ Map response = orderService.refund(request);
+ assertNotNull(response);
+ }
+
+ @Test
+ void testRefundOrderFailure() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 201010, \"error\": \"REFUND_VALIDATION_ERROR\"}");
+
+ RefundOrderRequest request = new RefundOrderRequest();
+ request.setReferenceId("order_ref_invalid");
+ request.setAmount(BigDecimal.ZERO);
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.refund(request);
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== refundAll ====================
+
+ @Test
+ void testRefundAllSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"is_success\":true, \"error\":\"REFUND_ALL_SUCCESSFUL\"}");
+
+ Map response = orderService.refundAll("order_ref_xyz");
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("is_success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/refund-all"));
+ }
+
+ @Test
+ void testRefundAllFailure() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 201020, \"error\": \"ORDER_NOT_FOUND_FOR_REFUND_ALL\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.refundAll("order_ref_nonexistent");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== getPaymentDetails ====================
+
+ @Test
+ void testGetPaymentDetailsById() throws Exception {
+ httpClient.setResponse(200, "{\"id\":\"mock-payment-details-id\"}");
+
+ Map response = orderService.getPaymentDetails("mock-ref-id", null);
+
+ assertNotNull(response);
+ assertEquals("mock-payment-details-id", response.get("id"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/mock-ref-id/payment-details"));
+ }
+
+ @Test
+ void testGetPaymentDetailsWithConversationId() throws Exception {
+ httpClient.setResponse(200, "{\"id\":\"mock-payment-details-id-conv\"}");
+
+ Map response = orderService.getPaymentDetails("mock-ref-id", "mock-conv-id");
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/payment-details"));
+ }
+
+ @Test
+ void testGetPaymentDetailsNotFound() throws Exception {
+ httpClient.setResponse(400,
+ "{\"code\": 101230, \"error\": \"ORDER_ORDER_PAYMENT_DETAIL_ORDER_DETAIL_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.getPaymentDetails("mock-ref-id", null);
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== getStatus ====================
+
+ @Test
+ void testGetStatusSuccess() throws Exception {
+ String jsonResponse = "{\"status\":\"Refunded\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ Map response = orderService.getStatus("mock-ref-123");
+
+ assertNotNull(response);
+ assertEquals("Refunded", response.get("status"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/mock-ref-123/status"));
+ }
+
+ @Test
+ void testGetStatusNotFound() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 100810, \"error\": \"ORDER_GET_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.getStatus("mock-ref-123");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== getTransactions ====================
+
+ @Test
+ void testGetTransactionsSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"transactions\":[{\"id\":\"mock-transaction-1\"}]}");
+
+ Map response = orderService.getTransactions("mock-ref-123");
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/mock-ref-123/transactions"));
+ }
+
+ @Test
+ void testGetTransactionsNotFound() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 101260, \"error\": \"ORDER_GET_ORDER_TXS_GET_ORDER_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.getTransactions("mock-ref-123");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== accounting ====================
+
+ @Test
+ void testAccountingSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrderAccountingRequest request = new OrderAccountingRequest("mock-ref-123");
+ Map response = orderService.accounting(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/accounting"));
+ }
+
+ // ==================== postAuth ====================
+
+ @Test
+ void testPostAuthSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrderPostAuthRequest request = new OrderPostAuthRequest(new BigDecimal("50.0"), "mock-ref-123");
+ Map response = orderService.postAuth(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/postauth"));
+ }
+
+ // ==================== systemOrderStatuses ====================
+
+ @Test
+ void testGetSystemOrderStatuses() throws Exception {
+ httpClient.setResponse(200, "{\"statuses\":[]}");
+
+ Map response = orderService.getSystemOrderStatuses();
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/system/order-statuses"));
+ }
+
+ // ==================== Term Operations ====================
+
+ @Test
+ void testGetTerm() throws Exception {
+ httpClient.setResponse(200, "{\"term_reference_id\":\"abc\"}");
+
+ Map response = orderService.getTerm("abc");
+
+ assertNotNull(response);
+ assertEquals("abc", response.get("term_reference_id"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term"));
+ assertTrue(captured.getUri().toString().contains("term_reference_id=abc"));
+ }
+
+ @Test
+ void testCreateTermSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"message\":\"ORDER_ADD_PAYMENT_TERM_SUCCESS\", \"code\":156050}");
+
+ OrderPaymentTermCreateRequest request = new OrderPaymentTermCreateRequest();
+ request.setOrderId("order123");
+ request.setTermReferenceId("term-ref-create");
+ request.setAmount(new BigDecimal("200"));
+ request.setDueDate("2025-10-10 00:00:00");
+ request.setTermSequence(2);
+ request.setRequired(false);
+ request.setStatus("active");
+
+ Map response = orderService.createTerm(request);
+
+ assertNotNull(response);
+ assertEquals("ORDER_ADD_PAYMENT_TERM_SUCCESS", response.get("message"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term"));
+ }
+
+ @Test
+ void testCreateTermFailure_ExceedsOrderAmount() throws Exception {
+ httpClient.setResponse(400,
+ "{\"code\": 156025, \"error\": \"ORDER_ADD_PAYMENT_TERM_AMOUNT_EXCEEDS_ORDER_AMOUNT\"}");
+
+ OrderPaymentTermCreateRequest request = new OrderPaymentTermCreateRequest();
+ request.setOrderId("order123");
+ request.setAmount(new BigDecimal("600"));
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.createTerm(request);
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ @Test
+ void testDeleteTerm() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = orderService.deleteTerm("order123", "term-ref-abc");
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("DELETE", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term"));
+ }
+
+ @Test
+ void testUpdateTerm() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrderPaymentTermUpdateRequest request = new OrderPaymentTermUpdateRequest();
+ request.setTermReferenceId("abc");
+ request.setAmount(new BigDecimal("100.0"));
+ request.setDueDate("2023-12-31");
+ request.setRequired(true);
+ request.setStatus("pending");
+ request.setTermSequence(1);
+
+ Map response = orderService.updateTerm(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("PATCH", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term"));
+ }
+
+ @Test
+ void testRefundTerm() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrderTermRefundRequest request = new OrderTermRefundRequest();
+ request.setTermId("term-id");
+ request.setAmount(new BigDecimal("25"));
+ request.setReferenceId("ref-123");
+
+ Map response = orderService.refundTerm(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term/refund"));
+ }
+
+ @Test
+ void testTerminateTerm() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = orderService.terminateTerm("term-ref-id", "Cancelled by user");
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/term/terminate"));
+ }
+
+ // ==================== terminate ====================
+
+ @Test
+ void testTerminateOrderSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"message\":\"ORDER_TERMINATE_ORDER_SUCCESS\", \"code\":338100}");
+
+ Map response = orderService.terminate("mock-ref-id");
+
+ assertNotNull(response);
+ assertEquals("ORDER_TERMINATE_ORDER_SUCCESS", response.get("message"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/terminate"));
+ }
+
+ @Test
+ void testTerminateOrderNotFound() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 338000, \"error\": \"ORDER_TERMINATE_ORDER_NOT_FOUND\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.terminate("mock-ref-id");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== manualCallback ====================
+
+ @Test
+ void testManualCallbackSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"message\":\"ORDER_MANUAL_CALLBACK_SUCCESS\", \"code\":337100}");
+
+ OrderManualCallbackRequest request = new OrderManualCallbackRequest("mock-ref-id");
+ request.setConversationId("mock-conv-id");
+
+ Map response = orderService.manualCallback(request);
+
+ assertNotNull(response);
+ assertEquals("ORDER_MANUAL_CALLBACK_SUCCESS", response.get("message"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/callback"));
+ }
+
+ @Test
+ void testManualCallbackFailed() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 12000, \"error\": \"ACTION_FAILED\"}");
+
+ OrderManualCallbackRequest request = new OrderManualCallbackRequest("mock-ref-id");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.manualCallback(request);
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== relatedUpdate ====================
+
+ @Test
+ void testRelatedUpdateSuccess() throws Exception {
+ httpClient.setResponse(200,
+ "{\"message\":\"ORDER_UPDATE_ORDER_SUCCESS\", \"code\":156170, \"is_success\":true}");
+
+ Map response = orderService.relatedUpdate("mock-ref-id", "mock-related-ref-id");
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("is_success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("PATCH", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/releated"));
+ }
+
+ @Test
+ void testRelatedUpdateNotFound() throws Exception {
+ httpClient.setResponse(400, "{\"code\": 12000, \"error\": \"ACTION_FAILED\"}");
+
+ TapsilatException exception = assertThrows(TapsilatException.class, () -> {
+ orderService.relatedUpdate("mock-ref-id", "mock-related-ref-id");
+ });
+ assertNotNull(exception.getMessage());
+ }
+
+ // ==================== Basket Item Operations ====================
+
+ @Test
+ void testAddBasketItem() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ BasketItem item = new BasketItem();
+ item.setId("BI101");
+ item.setName("Binocular");
+ item.setPrice(19.99);
+
+ AddBasketItemRequest request = new AddBasketItemRequest("order-ref-123", item);
+ Map response = orderService.addBasketItem(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/basket-item"));
+ }
+
+ @Test
+ void testRemoveBasketItem() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ RemoveBasketItemRequest request = new RemoveBasketItemRequest("order-ref-123", "BI101");
+ Map response = orderService.removeBasketItem(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("DELETE", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/basket-item"));
+ }
+
+ @Test
+ void testUpdateBasketItem() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ BasketItem item = new BasketItem();
+ item.setId("BI101");
+ item.setName("Updated Binocular");
+ item.setPrice(24.99);
+
+ UpdateBasketItemRequest request = new UpdateBasketItemRequest("order-ref-123", item);
+ Map response = orderService.updateBasketItem(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("PATCH", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/order/basket-item"));
+ }
+
+ // ==================== getOrganizationSettings ====================
+
+ @Test
+ void testGetOrganizationSettings() throws Exception {
+ httpClient.setResponse(200, "{\"settings\":{}}");
+
+ Map response = orderService.getOrganizationSettings();
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/settings"));
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/service/OrganizationServiceTest.java b/src/test/java/com/tapsilat/unit/service/OrganizationServiceTest.java
new file mode 100644
index 0000000..40bb07c
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/service/OrganizationServiceTest.java
@@ -0,0 +1,279 @@
+package com.tapsilat.unit.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.config.TapsilatConfig;
+import com.tapsilat.exception.TapsilatException;
+import com.tapsilat.model.organization.*;
+import com.tapsilat.service.OrganizationService;
+import com.tapsilat.unit.utils.MockHttpClient;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OrganizationServiceTest {
+
+ private MockHttpClient httpClient;
+ private OrganizationService organizationService;
+ private ObjectMapper objectMapper;
+
+ @BeforeEach
+ void setUp() {
+ TapsilatConfig config = new TapsilatConfig();
+ config.setBaseUrl("https://api.test");
+ config.setBearerToken("test-token");
+
+ objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ httpClient = new MockHttpClient();
+ organizationService = new OrganizationService(httpClient, config, objectMapper);
+ }
+
+ // ==================== getCallback ====================
+
+ @Test
+ void testGetCallbackSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"callback_url\":\"http://success.url\"}");
+
+ Map response = organizationService.getCallback();
+
+ assertNotNull(response);
+ assertEquals("http://success.url", response.get("callback_url"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/callback"));
+ }
+
+ // ==================== updateCallback ====================
+
+ @Test
+ void testUpdateCallbackSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ CallbackURLDTO request = new CallbackURLDTO();
+ request.setCallbackUrl("http://newsuccess.test");
+ Map response = organizationService.updateCallback(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("PATCH", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/callback"));
+ }
+
+ // ==================== createBusiness ====================
+
+ @Test
+ void testCreateBusinessSuccess() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrgCreateBusinessRequest request = new OrgCreateBusinessRequest();
+ request.setAddress("Test Address");
+ request.setBusinessName("Test Business");
+ request.setBusinessType(0);
+ request.setEmail("test@test.com");
+ request.setFirstName("John");
+ request.setLastName("Doe");
+ request.setPhone("+905551234567");
+ request.setIdentityNumber("12345678901");
+ request.setTaxNumber("1234567890");
+ request.setTaxOffice("Test Office");
+ request.setZipCode("34000");
+
+ Map response = organizationService.createBusiness(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/business/create"));
+ }
+
+ // ==================== getCurrencies ====================
+
+ @Test
+ void testGetCurrencies() throws Exception {
+ httpClient.setResponse(200, "{\"currencies\":[]}");
+
+ Map response = organizationService.getCurrencies();
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/currencies"));
+ }
+
+ // ==================== getLimitUser ====================
+
+ @Test
+ void testGetLimitUser() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = organizationService.getLimitUser("user-123");
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/limit/user"));
+ assertTrue(captured.getUri().toString().contains("user_id=user-123"));
+ }
+
+ // ==================== setLimitUser ====================
+
+ @Test
+ void testSetLimitUser() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ SetLimitUserRequest request = new SetLimitUserRequest("user-123", "limit-1");
+ Map response = organizationService.setLimitUser(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/limit/user"));
+ }
+
+ // ==================== getLimits ====================
+
+ @Test
+ void testGetLimits() throws Exception {
+ httpClient.setResponse(200, "{\"limits\":[]}");
+
+ Map response = organizationService.getLimits();
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/limits"));
+ }
+
+ // ==================== listVpos ====================
+
+ @Test
+ void testListVpos() throws Exception {
+ httpClient.setResponse(200, "{\"vpos\":[]}");
+
+ GetVposRequest request = new GetVposRequest("TRY");
+ Map response = organizationService.listVpos(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/list-vpos"));
+ }
+
+ // ==================== getMeta ====================
+
+ @Test
+ void testGetMeta() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = organizationService.getMeta("meta_name");
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/meta/meta_name"));
+ }
+
+ // ==================== getScopes ====================
+
+ @Test
+ void testGetScopes() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = organizationService.getScopes();
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/scopes"));
+ }
+
+ // ==================== getSuborganizations ====================
+
+ @Test
+ void testGetSuborganizations() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ Map response = organizationService.getSuborganizations(1, 10);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/suborganizations"));
+ assertTrue(captured.getUri().toString().contains("page=1"));
+ assertTrue(captured.getUri().toString().contains("per_page=10"));
+ }
+
+ // ==================== createUser ====================
+
+ @Test
+ void testCreateUser() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrgCreateUserRequest request = new OrgCreateUserRequest();
+ request.setEmail("test@test.com");
+ request.setFirstName("John");
+ request.setLastName("Doe");
+ request.setPhone("+905551234567");
+ request.setConversationId("conv-1");
+ request.setIdentityNumber("12345678901");
+ request.setIsMailVerified(true);
+ request.setReferenceId("ref-1");
+
+ Map response = organizationService.createUser(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/user/create"));
+ }
+
+ // ==================== verifyUser ====================
+
+ @Test
+ void testVerifyUser() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrgUserVerifyRequest request = new OrgUserVerifyRequest("user-123");
+ Map response = organizationService.verifyUser(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/user/verify"));
+ }
+
+ // ==================== verifyUserMobile ====================
+
+ @Test
+ void testVerifyUserMobile() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ OrgUserMobileVerifyRequest request = new OrgUserMobileVerifyRequest("user-123");
+ Map response = organizationService.verifyUserMobile(request);
+
+ assertNotNull(response);
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/organization/user/verify-mobile"));
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/service/SubscriptionServiceTest.java b/src/test/java/com/tapsilat/unit/service/SubscriptionServiceTest.java
new file mode 100644
index 0000000..b108276
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/service/SubscriptionServiceTest.java
@@ -0,0 +1,144 @@
+package com.tapsilat.unit.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tapsilat.config.TapsilatConfig;
+import com.tapsilat.model.subscription.*;
+import com.tapsilat.service.SubscriptionService;
+import com.tapsilat.unit.utils.MockHttpClient;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SubscriptionServiceTest {
+
+ private MockHttpClient httpClient;
+ private SubscriptionService subscriptionService;
+ private ObjectMapper objectMapper;
+
+ @BeforeEach
+ void setUp() {
+ TapsilatConfig config = new TapsilatConfig();
+ config.setBaseUrl("https://api.test");
+ config.setBearerToken("test-token");
+
+ objectMapper = new ObjectMapper();
+ objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
+ httpClient = new MockHttpClient();
+ subscriptionService = new SubscriptionService(httpClient, config, objectMapper);
+ }
+
+ // ==================== create ====================
+
+ @Test
+ void testCreateSubscriptionSuccess() throws Exception {
+ String jsonResponse = "{\"reference_id\":\"sub-ref-new\", \"code\":100, \"message\":\"Success\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ SubscriptionCreateRequest request = new SubscriptionCreateRequest();
+ request.setTitle("Monthly Plan");
+ request.setAmount(new BigDecimal("100.0"));
+ request.setCurrency("TRY");
+ request.setPeriod(30);
+
+ SubscriptionUser user = new SubscriptionUser();
+ user.setFirstName("John");
+ user.setLastName("Doe");
+ user.setEmail("test@test.com");
+ request.setUser(user);
+
+ SubscriptionCreateResponse response = subscriptionService.create(request);
+
+ assertNotNull(response);
+ assertEquals("sub-ref-new", response.getReferenceId());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/subscription/create"));
+ }
+
+ // ==================== get ====================
+
+ @Test
+ void testGetSubscriptionSuccess() throws Exception {
+ String jsonResponse = "{\"external_reference_id\":\"ext-ref-123\", \"title\":\"My Subscription\"}";
+ httpClient.setResponse(200, jsonResponse);
+
+ SubscriptionGetRequest request = new SubscriptionGetRequest();
+ request.setReferenceId("sub-ref-123");
+ SubscriptionDetail response = subscriptionService.get(request);
+
+ assertNotNull(response);
+ assertEquals("ext-ref-123", response.getExternalReferenceId());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/subscription"));
+ }
+
+ // ==================== list ====================
+
+ @Test
+ void testListSubscriptionsSuccess() throws Exception {
+ String jsonResponse = "{\"page\":1, \"per_page\":10, \"items\":[], \"total\":0}";
+ httpClient.setResponse(200, jsonResponse);
+
+ List response = subscriptionService.list(1, 10);
+ assertNotNull(response);
+ assertTrue(response.isEmpty());
+
+ Map rawResponse = subscriptionService.listResponse(1, 10);
+
+ assertNotNull(rawResponse);
+ assertEquals(1, ((Number) rawResponse.get("page")).intValue());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("GET", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/subscription/list"));
+ assertTrue(captured.getUri().toString().contains("page=1"));
+ assertTrue(captured.getUri().toString().contains("per_page=10"));
+ }
+
+ // ==================== cancel ====================
+
+ @Test
+ void testCancelSubscription() throws Exception {
+ httpClient.setResponse(200, "{\"success\":true}");
+
+ SubscriptionCancelRequest request = new SubscriptionCancelRequest();
+ request.setReferenceId("sub-ref-123");
+
+ Map response = subscriptionService.cancel(request);
+
+ assertNotNull(response);
+ assertTrue((Boolean) response.get("success"));
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/subscription/cancel"));
+ }
+
+ // ==================== redirect ====================
+
+ @Test
+ void testRedirectSubscription() throws Exception {
+ httpClient.setResponse(200, "{\"url\":\"https://redirect.example.com\"}");
+
+ SubscriptionRedirectRequest request = new SubscriptionRedirectRequest();
+ request.setSubscriptionId("sub-ref-123");
+
+ SubscriptionRedirectResponse response = subscriptionService.redirect(request);
+
+ assertNotNull(response);
+ assertEquals("https://redirect.example.com", response.getUrl());
+
+ ClassicHttpRequest captured = httpClient.getCapturedRequest();
+ assertEquals("POST", captured.getMethod());
+ assertTrue(captured.getUri().toString().contains("/subscription/redirect"));
+ }
+}
diff --git a/src/test/java/com/tapsilat/unit/utils/MockHttpClient.java b/src/test/java/com/tapsilat/unit/utils/MockHttpClient.java
new file mode 100644
index 0000000..db2d25e
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/utils/MockHttpClient.java
@@ -0,0 +1,50 @@
+package com.tapsilat.unit.utils;
+
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponseAdapter;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+
+import java.io.IOException;
+
+/**
+ * A manual MockHttpClient that avoids Mockito instrumentation issues.
+ * Uses CloseableHttpResponseAdapter to create real instances of the final CloseableHttpResponse class.
+ */
+public class MockHttpClient extends CloseableHttpClient {
+ private ClassicHttpRequest capturedRequest;
+ private int stubStatusCode = 200;
+ private String stubResponseBody = "{}";
+
+ public void setResponse(int statusCode, String body) {
+ this.stubStatusCode = statusCode;
+ this.stubResponseBody = body;
+ }
+
+ public ClassicHttpRequest getCapturedRequest() {
+ return capturedRequest;
+ }
+
+ @Override
+ protected CloseableHttpResponse doExecute(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException {
+ this.capturedRequest = request;
+
+ // Create a real ClassicHttpResponse
+ BasicClassicHttpResponse response = new BasicClassicHttpResponse(stubStatusCode);
+ response.setEntity(new StringEntity(stubResponseBody));
+
+ // Adapt it to CloseableHttpResponse using our package-private adapter trick
+ return CloseableHttpResponseAdapter.adapt(response);
+ }
+
+ @Override
+ public void close() throws IOException {}
+
+ @Override
+ public void close(CloseMode closeMode) {}
+}
diff --git a/src/test/java/com/tapsilat/unit/validation/ValidatorTest.java b/src/test/java/com/tapsilat/unit/validation/ValidatorTest.java
new file mode 100644
index 0000000..3734df9
--- /dev/null
+++ b/src/test/java/com/tapsilat/unit/validation/ValidatorTest.java
@@ -0,0 +1,215 @@
+package com.tapsilat.unit.validation;
+
+import com.tapsilat.model.common.Buyer;
+import com.tapsilat.model.order.OrderCreateRequest;
+import com.tapsilat.validation.OrderRequestValidator;
+import com.tapsilat.validation.ValidationException;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ValidatorTest {
+
+ // ==================== GSM Number Cleaning ====================
+
+ @Test
+ void testCleanGsmNumber_InternationalPlusFormat() {
+ List errors = new ArrayList<>();
+ String result = OrderRequestValidator.cleanGsmNumber("+905551234567", errors);
+ assertEquals("+905551234567", result);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testCleanGsmNumber_InternationalDoubleZeroFormat() {
+ List errors = new ArrayList<>();
+ String result = OrderRequestValidator.cleanGsmNumber("00905551234567", errors);
+ assertEquals("00905551234567", result);
+ assertTrue(errors.isEmpty());
+ }
+
+
+
+ @Test
+ void testCleanGsmNumber_RemovesFormattingCharacters() {
+ List errors = new ArrayList<>();
+ String result = OrderRequestValidator.cleanGsmNumber("+90 555 123-45(67)", errors);
+ assertEquals("+905551234567", result);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testCleanGsmNumber_StripsEmbeddedPlusSigns() {
+ List errors = new ArrayList<>();
+ String result = OrderRequestValidator.cleanGsmNumber("12+34+567", errors);
+ assertEquals("1234567", result);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testCleanGsmNumber_TooShort() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.cleanGsmNumber("123", errors);
+ assertFalse(errors.isEmpty());
+ assertTrue(errors.get(0).contains("too short"));
+ }
+
+ @Test
+ void testCleanGsmNumber_NullReturnsNull() {
+ List errors = new ArrayList<>();
+ assertNull(OrderRequestValidator.cleanGsmNumber(null, errors));
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testCleanGsmNumber_EmptyReturnsEmpty() {
+ List errors = new ArrayList<>();
+ assertEquals("", OrderRequestValidator.cleanGsmNumber("", errors));
+ assertTrue(errors.isEmpty());
+ }
+
+ // ==================== Installment Validation ====================
+
+ @Test
+ void testValidateInstallmentsList_ValidValues() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(Arrays.asList(1, 3, 6, 12), errors);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testValidateInstallmentsList_SingleValid() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(Arrays.asList(1), errors);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testValidateInstallmentsList_ValueTooLow() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(Arrays.asList(0, 2, 3), errors);
+ assertEquals(1, errors.size());
+ assertTrue(errors.get(0).contains("invalid"));
+ }
+
+ @Test
+ void testValidateInstallmentsList_ValueTooHigh() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(Arrays.asList(1, 13, 3), errors);
+ assertEquals(1, errors.size());
+ assertTrue(errors.get(0).contains("invalid"));
+ }
+
+ @Test
+ void testValidateInstallmentsList_MultipleBadValues() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(Arrays.asList(0, 13), errors);
+ assertEquals(2, errors.size());
+ }
+
+ @Test
+ void testValidateInstallmentsList_NullIsNoop() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(null, errors);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testValidateInstallmentsList_EmptyIsNoop() {
+ List errors = new ArrayList<>();
+ OrderRequestValidator.validateInstallmentsList(new ArrayList<>(), errors);
+ assertTrue(errors.isEmpty());
+ }
+
+ // ==================== Full Validate ====================
+
+ @Test
+ void testValidate_NullRequestThrows() {
+ assertThrows(ValidationException.class, () -> OrderRequestValidator.validate(null));
+ }
+
+ @Test
+ void testValidate_ValidRequest() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ List errors = OrderRequestValidator.validate(request);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testValidate_MissingAmount() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ List errors = OrderRequestValidator.validate(request);
+ assertFalse(errors.isEmpty());
+ assertTrue(errors.stream().anyMatch(e -> e.contains("Amount")));
+ }
+
+ @Test
+ void testValidate_ZeroAmount() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(BigDecimal.ZERO);
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ List errors = OrderRequestValidator.validate(request);
+ assertFalse(errors.isEmpty());
+ assertTrue(errors.stream().anyMatch(e -> e.contains("greater than zero")));
+ }
+
+ @Test
+ void testValidate_MissingCurrency() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "test@example.com"));
+
+ List errors = OrderRequestValidator.validate(request);
+ assertFalse(errors.isEmpty());
+ assertTrue(errors.stream().anyMatch(e -> e.contains("Currency")));
+ }
+
+ @Test
+ void testValidate_MissingBuyer() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+
+ List errors = OrderRequestValidator.validate(request);
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ void testValidate_InvalidEmail() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ request.setAmount(new BigDecimal("100"));
+ request.setCurrency("TRY");
+ request.setLocale("tr");
+ request.setBuyer(new Buyer("John", "Doe", "bad-email"));
+
+ List errors = OrderRequestValidator.validate(request);
+ assertFalse(errors.isEmpty());
+ assertTrue(errors.stream().anyMatch(e -> e.contains("email")));
+ }
+
+ @Test
+ void testValidateOrThrow_InvalidThrows() {
+ OrderCreateRequest request = new OrderCreateRequest();
+ // Missing all required fields
+ assertThrows(ValidationException.class, () -> OrderRequestValidator.validateOrThrow(request));
+ }
+}
diff --git a/src/test/java/org/apache/hc/client5/http/impl/classic/CloseableHttpResponseAdapter.java b/src/test/java/org/apache/hc/client5/http/impl/classic/CloseableHttpResponseAdapter.java
new file mode 100644
index 0000000..bec25a1
--- /dev/null
+++ b/src/test/java/org/apache/hc/client5/http/impl/classic/CloseableHttpResponseAdapter.java
@@ -0,0 +1,12 @@
+package org.apache.hc.client5.http.impl.classic;
+
+import org.apache.hc.core5.http.ClassicHttpResponse;
+
+/**
+ * Adapter to access the package-private CloseableHttpResponse.adapt method.
+ */
+public class CloseableHttpResponseAdapter {
+ public static CloseableHttpResponse adapt(ClassicHttpResponse response) {
+ return CloseableHttpResponse.adapt(response);
+ }
+}