From c099915f9116b2ad455965f3d9857b6f4f0c164f Mon Sep 17 00:00:00 2001 From: Przemyslaw Poznienski Date: Tue, 19 May 2026 09:15:16 +0200 Subject: [PATCH 1/4] [COREP-5113] dupport pass card --- CHANGELOG.md | 3 + build.gradle | 2 +- readme.md | 2 +- .../com/pensio/api/PensioMerchantAPI.java | 11 ++++ .../api/request/AcquirerTransactionData.java | 21 +++++++ .../java/com/pensio/api/request/PassCard.java | 9 +++ .../pensio/api/request/PaymentRequest.java | 7 +++ src/main/resources/xsd/APIResponse.xsd | 22 +++++++ ..._AcquirerTransactionDataEmissionTests.java | 57 +++++++++++++++++ .../api/PensioAPIResponseParserTest.java | 63 +++++++++++++++++++ .../request/AcquirerTransactionDataTest.java | 39 ++++++++++++ 11 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/pensio/api/request/AcquirerTransactionData.java create mode 100644 src/main/java/com/pensio/api/request/PassCard.java create mode 100644 src/test/java/com/pensio/api/MerchantApi_AcquirerTransactionDataEmissionTests.java create mode 100644 src/test/java/com/pensio/api/PensioAPIResponseParserTest.java create mode 100644 src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 431bf87..c8dd9d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog All notable changes to this project will be documented in this file. +## [3.1.14] +- Added `AcquirerTransactionData` + ## [3.1.13] - Added `terminal' field to `ChargeSubscriptionRequest` and `ReserveSubscriptionChargeRequest` in `MerchantAPI` to support charging subscription with specific terminal diff --git a/build.gradle b/build.gradle index 2ae8980..0f40a70 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { } group = 'com.altapay' -version = '3.1.13' +version = '3.1.14' repositories { mavenCentral() diff --git a/readme.md b/readme.md index f9a118c..0359798 100644 --- a/readme.md +++ b/readme.md @@ -49,7 +49,7 @@ For integrating Java projects with the AltaPay gateway. com.altapay sdk-java - 3.1.13 + 3.1.14 ### Gradle diff --git a/src/main/java/com/pensio/api/PensioMerchantAPI.java b/src/main/java/com/pensio/api/PensioMerchantAPI.java index d8da2f1..db0e207 100644 --- a/src/main/java/com/pensio/api/PensioMerchantAPI.java +++ b/src/main/java/com/pensio/api/PensioMerchantAPI.java @@ -665,6 +665,17 @@ private void addPaymentInfo(PaymentRequest paymentRequest, HashMap> g + : paymentRequest.getAcquirerTransactionData().getAll().entrySet()) { + for (Map.Entry kv : g.getValue().entrySet()) { + addParam(params, + "acquirerTransactionData[" + g.getKey() + "][" + kv.getKey() + "]", + kv.getValue()); + } + } + } } private void addAuthType(PaymentRequest request, HashMap params) diff --git a/src/main/java/com/pensio/api/request/AcquirerTransactionData.java b/src/main/java/com/pensio/api/request/AcquirerTransactionData.java new file mode 100644 index 0000000..ba14405 --- /dev/null +++ b/src/main/java/com/pensio/api/request/AcquirerTransactionData.java @@ -0,0 +1,21 @@ +package com.pensio.api.request; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class AcquirerTransactionData { + private final Map> groups = new LinkedHashMap<>(); + + public AcquirerTransactionData add(String group, String key, String value) { + groups.computeIfAbsent(group, g -> new LinkedHashMap<>()).put(key, value); + return this; + } + + public Map> getAll() { + return groups; + } + + public boolean isEmpty() { + return groups.isEmpty(); + } +} diff --git a/src/main/java/com/pensio/api/request/PassCard.java b/src/main/java/com/pensio/api/request/PassCard.java new file mode 100644 index 0000000..cd5fc00 --- /dev/null +++ b/src/main/java/com/pensio/api/request/PassCard.java @@ -0,0 +1,9 @@ +package com.pensio.api.request; + +public final class PassCard { + private PassCard() {} + + public static final String GROUP = "passcard"; + public static final String CREDITCODE = "creditcode"; + public static final String PAYMENTOCCURRENCE = "paymentoccurrence"; +} diff --git a/src/main/java/com/pensio/api/request/PaymentRequest.java b/src/main/java/com/pensio/api/request/PaymentRequest.java index 7c6b157..096b6bc 100644 --- a/src/main/java/com/pensio/api/request/PaymentRequest.java +++ b/src/main/java/com/pensio/api/request/PaymentRequest.java @@ -27,6 +27,7 @@ public class PaymentRequest> protected CustomerInfo recipientInfo; private final PaymentInfos paymentInfos; private final List orderLines; + private final AcquirerTransactionData acquirerTransactionData; /** * @@ -38,6 +39,7 @@ public class PaymentRequest> { paymentInfos = new PaymentInfos(); orderLines = new ArrayList<>(); + acquirerTransactionData = new AcquirerTransactionData(); } public PaymentRequest() @@ -199,6 +201,11 @@ public PaymentInfos getPaymentInfos() return paymentInfos; } + public AcquirerTransactionData getAcquirerTransactionData() + { + return acquirerTransactionData; + } + @SuppressWarnings("unchecked") public T addPaymentInfo(String key, String value) { diff --git a/src/main/resources/xsd/APIResponse.xsd b/src/main/resources/xsd/APIResponse.xsd index 9484c98..c1d01c5 100644 --- a/src/main/resources/xsd/APIResponse.xsd +++ b/src/main/resources/xsd/APIResponse.xsd @@ -597,6 +597,7 @@ + @@ -827,6 +828,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/pensio/api/MerchantApi_AcquirerTransactionDataEmissionTests.java b/src/test/java/com/pensio/api/MerchantApi_AcquirerTransactionDataEmissionTests.java new file mode 100644 index 0000000..f4643fe --- /dev/null +++ b/src/test/java/com/pensio/api/MerchantApi_AcquirerTransactionDataEmissionTests.java @@ -0,0 +1,57 @@ +package com.pensio.api; + +import com.pensio.Amount; +import com.pensio.Currency; +import com.pensio.api.generated.APIResponse; +import com.pensio.api.request.PassCard; +import com.pensio.api.request.PaymentReservationRequest; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +class MerchantApi_AcquirerTransactionDataEmissionTests { + + @Test + void reservationEmitsAcquirerTransactionDataParams() throws PensioAPIException { + final var api = new PensioMerchantAPI("http://base", "user", "pass") { + @Override + protected APIResponse getAPIResponse(String method, HttpMethod httpMethod, Map requestVars) { + assertEquals("reservation", method); + assertEquals(HttpMethod.POST, httpMethod); + assertEquals("32", requestVars.get("acquirerTransactionData[passcard][creditcode]")); + assertEquals("001", requestVars.get("acquirerTransactionData[passcard][paymentoccurrence]")); + return null; + } + }; + + PaymentReservationRequest request = new PaymentReservationRequest("order123", "Terminal", Amount.get(100, Currency.DKK)); + request.getAcquirerTransactionData() + .add(PassCard.GROUP, PassCard.CREDITCODE, "32") + .add(PassCard.GROUP, PassCard.PAYMENTOCCURRENCE, "001"); + + api.reservation(request); + } + + @Test + void reservationOmitsAcquirerTransactionDataParamsWhenEmpty() throws PensioAPIException { + final var api = new PensioMerchantAPI("http://base", "user", "pass") { + @Override + protected APIResponse getAPIResponse(String method, HttpMethod httpMethod, Map requestVars) { + assertEquals("reservation", method); + assertEquals(HttpMethod.POST, httpMethod); + assertFalse( + requestVars.keySet().stream().anyMatch(k -> k.startsWith("acquirerTransactionData[")), + "no key starting with acquirerTransactionData[ should be emitted when empty" + ); + return null; + } + }; + + PaymentReservationRequest request = new PaymentReservationRequest("order123", "Terminal", Amount.get(100, Currency.DKK)); + + api.reservation(request); + } +} diff --git a/src/test/java/com/pensio/api/PensioAPIResponseParserTest.java b/src/test/java/com/pensio/api/PensioAPIResponseParserTest.java new file mode 100644 index 0000000..b444b33 --- /dev/null +++ b/src/test/java/com/pensio/api/PensioAPIResponseParserTest.java @@ -0,0 +1,63 @@ +package com.pensio.api; + +import com.pensio.api.generated.APIResponse; +import com.pensio.api.generated.AcquirerTransactionData; +import com.pensio.api.generated.AcquirerTransactionDataEntry; +import com.pensio.api.generated.AcquirerTransactionDataGroup; +import com.pensio.api.generated.Transaction; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class PensioAPIResponseParserTest { + + @Test + void parsesAcquirerTransactionDataOnTransaction() throws Exception { + String xml = + "" + + "" + + "
" + + " 2026-05-14T11:50:45+02:00" + + " API/reservation" + + " 0" + + " Success" + + "
" + + " " + + " Success" + + " " + + " " + + " 1" + + " " + + " " + + " 32" + + " 001" + + " " + + " " + + " " + + " " + + " " + + "
"; + + PensioMerchantAPI api = new PensioMerchantAPI("url", "username", "password"); + APIResponse parsed = api.parsePostBackXMLParameter(xml); + + Transaction t = parsed.getBody().getTransactions().getTransaction().get(0); + + AcquirerTransactionData atd = t.getAcquirerTransactionData(); + assertNotNull(atd); + assertEquals(1, atd.getGroup().size()); + + AcquirerTransactionDataGroup group = atd.getGroup().get(0); + assertEquals("passcard", group.getName()); + assertEquals(2, group.getEntry().size()); + + AcquirerTransactionDataEntry e0 = group.getEntry().get(0); + assertEquals("creditcode", e0.getKey()); + assertEquals("32", e0.getValue()); + + AcquirerTransactionDataEntry e1 = group.getEntry().get(1); + assertEquals("paymentoccurrence", e1.getKey()); + assertEquals("001", e1.getValue()); + } +} diff --git a/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java b/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java new file mode 100644 index 0000000..b1731b6 --- /dev/null +++ b/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java @@ -0,0 +1,39 @@ +package com.pensio.api.request; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class AcquirerTransactionDataTest { + + @Test + public void emptyByDefault() { + AcquirerTransactionData d = new AcquirerTransactionData(); + assertTrue(d.isEmpty()); + assertTrue(d.getAll().isEmpty()); + } + + @Test + public void addChainsAndStoresByGroup() { + AcquirerTransactionData d = new AcquirerTransactionData() + .add(PassCard.GROUP, PassCard.CREDITCODE, "32") + .add(PassCard.GROUP, PassCard.PAYMENTOCCURRENCE, "001"); + + assertFalse(d.isEmpty()); + Map passcard = d.getAll().get(PassCard.GROUP); + assertEquals(2, passcard.size()); + assertEquals("32", passcard.get(PassCard.CREDITCODE)); + assertEquals("001", passcard.get(PassCard.PAYMENTOCCURRENCE)); + } + + @Test + public void addOverwritesSameKey() { + AcquirerTransactionData d = new AcquirerTransactionData() + .add("g", "k", "v1") + .add("g", "k", "v2"); + + assertEquals("v2", d.getAll().get("g").get("k")); + } +} From 0bff5d7546cfe8d829071671baea155fe6f4686a Mon Sep 17 00:00:00 2001 From: Przemyslaw Poznienski Date: Thu, 21 May 2026 09:08:51 +0200 Subject: [PATCH 2/4] [COREP-5113] fix test --- .../api/request/AcquirerTransactionDataTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java b/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java index b1731b6..d179289 100644 --- a/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java +++ b/src/test/java/com/pensio/api/request/AcquirerTransactionDataTest.java @@ -4,19 +4,21 @@ import java.util.Map; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class AcquirerTransactionDataTest { +class AcquirerTransactionDataTest { @Test - public void emptyByDefault() { + void emptyByDefault() { AcquirerTransactionData d = new AcquirerTransactionData(); assertTrue(d.isEmpty()); assertTrue(d.getAll().isEmpty()); } @Test - public void addChainsAndStoresByGroup() { + void addChainsAndStoresByGroup() { AcquirerTransactionData d = new AcquirerTransactionData() .add(PassCard.GROUP, PassCard.CREDITCODE, "32") .add(PassCard.GROUP, PassCard.PAYMENTOCCURRENCE, "001"); @@ -29,7 +31,7 @@ public void addChainsAndStoresByGroup() { } @Test - public void addOverwritesSameKey() { + void addOverwritesSameKey() { AcquirerTransactionData d = new AcquirerTransactionData() .add("g", "k", "v1") .add("g", "k", "v2"); From 6e259de433a95057f3407f9bb4a676d05a392ca8 Mon Sep 17 00:00:00 2001 From: Przemyslaw Poznienski Date: Thu, 21 May 2026 10:46:39 +0200 Subject: [PATCH 3/4] [COREP-5113] fix generated --- .../generated/AcquirerTransactionData.java | 73 ++++++++++++ .../AcquirerTransactionDataGroup.java | 104 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionData.java create mode 100644 build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionDataGroup.java diff --git a/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionData.java b/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionData.java new file mode 100644 index 0000000..2c5d6f6 --- /dev/null +++ b/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionData.java @@ -0,0 +1,73 @@ + +package com.pensio.api.generated; + +import java.util.ArrayList; +import java.util.List; +import jakarta.annotation.Generated; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for AcquirerTransactionData complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="AcquirerTransactionData">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="Group" type="{}AcquirerTransactionDataGroup" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "AcquirerTransactionData", propOrder = { + "group" +}) +@Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") +public class AcquirerTransactionData { + + @XmlElement(name = "Group") + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + protected List group; + + /** + * Gets the value of the group property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the group property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getGroup().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link AcquirerTransactionDataGroup } + * + * + */ + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + public List getGroup() { + if (group == null) { + group = new ArrayList<>(); + } + return this.group; + } + +} diff --git a/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionDataGroup.java b/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionDataGroup.java new file mode 100644 index 0000000..535480d --- /dev/null +++ b/build/generated/sources/xjc/java/com/pensio/api/generated/AcquirerTransactionDataGroup.java @@ -0,0 +1,104 @@ + +package com.pensio.api.generated; + +import java.util.ArrayList; +import java.util.List; +import jakarta.annotation.Generated; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; + + +/** + *

Java class for AcquirerTransactionDataGroup complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="AcquirerTransactionDataGroup">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="Entry" type="{}AcquirerTransactionDataEntry" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *       <attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "AcquirerTransactionDataGroup", propOrder = { + "entry" +}) +@Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") +public class AcquirerTransactionDataGroup { + + @XmlElement(name = "Entry") + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + protected List entry; + @XmlAttribute(name = "name", required = true) + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + protected String name; + + /** + * Gets the value of the entry property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the Jakarta XML Binding object. + * This is why there is not a set method for the entry property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getEntry().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link AcquirerTransactionDataEntry } + * + * + */ + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + public List getEntry() { + if (entry == null) { + entry = new ArrayList<>(); + } + return this.entry; + } + + /** + * Gets the value of the name property. + * + * @return + * possible object is + * {@link String } + * + */ + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + public String getName() { + return name; + } + + /** + * Sets the value of the name property. + * + * @param value + * allowed object is + * {@link String } + * + */ + @Generated(value = "com.sun.tools.xjc.Driver", comments = "JAXB RI v3.0.2", date = "2026-05-21T09:05:56+02:00") + public void setName(String value) { + this.name = value; + } + +} From 2e33be33dd2a911a62b0617c3c3af8afd23a0c92 Mon Sep 17 00:00:00 2001 From: Shahbaz Date: Thu, 21 May 2026 16:35:06 +0500 Subject: [PATCH 4/4] Exclude generated code from sonarqube analysis --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 0f40a70..0790c35 100644 --- a/build.gradle +++ b/build.gradle @@ -83,6 +83,8 @@ sonar { property "sonar.coverage.exclusions", "**/*" property "sonar.java.coveragePlugin", "none" + property "sonar.exclusions", "**/com/pensio/api/generated/**" + // Fail build if Quality Gate fails property "sonar.qualitygate.wait", "true" }