Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = 'com.altapay'
version = '3.1.13'
version = '3.1.14'

repositories {
mavenCentral()
Expand Down Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;


/**
* <p>Java class for AcquirerTransactionData complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType name="AcquirerTransactionData"&gt;
* &lt;complexContent&gt;
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
* &lt;sequence&gt;
* &lt;element name="Group" type="{}AcquirerTransactionDataGroup" maxOccurs="unbounded" minOccurs="0"/&gt;
* &lt;/sequence&gt;
* &lt;/restriction&gt;
* &lt;/complexContent&gt;
* &lt;/complexType&gt;
* </pre>
*
*
*/
@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<AcquirerTransactionDataGroup> group;

/**
* Gets the value of the group property.
*
* <p>
* 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 <CODE>set</CODE> method for the group property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getGroup().add(newItem);
* </pre>
*
*
* <p>
* 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<AcquirerTransactionDataGroup> getGroup() {
if (group == null) {
group = new ArrayList<>();
}
return this.group;
}

}
Original file line number Diff line number Diff line change
@@ -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;


/**
* <p>Java class for AcquirerTransactionDataGroup complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType name="AcquirerTransactionDataGroup"&gt;
* &lt;complexContent&gt;
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
* &lt;sequence&gt;
* &lt;element name="Entry" type="{}AcquirerTransactionDataEntry" maxOccurs="unbounded" minOccurs="0"/&gt;
* &lt;/sequence&gt;
* &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&gt;
* &lt;/restriction&gt;
* &lt;/complexContent&gt;
* &lt;/complexType&gt;
* </pre>
*
*
*/
@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<AcquirerTransactionDataEntry> 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.
*
* <p>
* 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 <CODE>set</CODE> method for the entry property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getEntry().add(newItem);
* </pre>
*
*
* <p>
* 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<AcquirerTransactionDataEntry> 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;
}

}
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ For integrating Java projects with the AltaPay gateway.
<dependency>
<groupId>com.altapay</groupId>
<artifactId>sdk-java</artifactId>
<version>3.1.13</version>
<version>3.1.14</version>
</dependency>

### Gradle
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/pensio/api/PensioMerchantAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,17 @@ private void addPaymentInfo(PaymentRequest<?> paymentRequest, HashMap<String, St
{
addParam(params, "transaction_info["+paymentInfo.getKey()+"]", paymentInfo.getValue());
}

if (!paymentRequest.getAcquirerTransactionData().isEmpty()) {
for (Map.Entry<String, Map<String, String>> g
: paymentRequest.getAcquirerTransactionData().getAll().entrySet()) {
for (Map.Entry<String, String> kv : g.getValue().entrySet()) {
addParam(params,
"acquirerTransactionData[" + g.getKey() + "][" + kv.getKey() + "]",
kv.getValue());
}
}
}
}

private void addAuthType(PaymentRequest<?> request, HashMap<String, String> params)
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/pensio/api/request/AcquirerTransactionData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.pensio.api.request;

import java.util.LinkedHashMap;
import java.util.Map;

public class AcquirerTransactionData {
private final Map<String, Map<String, String>> 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<String, Map<String, String>> getAll() {
return groups;
}

public boolean isEmpty() {
return groups.isEmpty();
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/pensio/api/request/PassCard.java
Original file line number Diff line number Diff line change
@@ -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";
}
7 changes: 7 additions & 0 deletions src/main/java/com/pensio/api/request/PaymentRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class PaymentRequest<T extends PaymentRequest<T>>
protected CustomerInfo recipientInfo;
private final PaymentInfos paymentInfos;
private final List<OrderLine> orderLines;
private final AcquirerTransactionData acquirerTransactionData;

/**
*
Expand All @@ -38,6 +39,7 @@ public class PaymentRequest<T extends PaymentRequest<T>>
{
paymentInfos = new PaymentInfos();
orderLines = new ArrayList<>();
acquirerTransactionData = new AcquirerTransactionData();
}

public PaymentRequest()
Expand Down Expand Up @@ -199,6 +201,11 @@ public PaymentInfos getPaymentInfos()
return paymentInfos;
}

public AcquirerTransactionData getAcquirerTransactionData()
{
return acquirerTransactionData;
}

@SuppressWarnings("unchecked")
public T addPaymentInfo(String key, String value)
{
Expand Down
22 changes: 22 additions & 0 deletions src/main/resources/xsd/APIResponse.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@
<xs:element name="ReconciliationIdentifiers"
type="ReconciliationIdentifiers"/>
<xs:element name="Authentication" type="Authentication" maxOccurs="1" minOccurs="0"/>
<xs:element name="AcquirerTransactionData" type="AcquirerTransactionData" maxOccurs="1" minOccurs="0"/>
</xs:all>
</xs:complexType>
<xs:complexType name="Authentication">
Expand Down Expand Up @@ -827,6 +828,27 @@
</xs:all>
</xs:complexType>

<xs:complexType name="AcquirerTransactionData">
<xs:sequence>
<xs:element name="Group" type="AcquirerTransactionDataGroup" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="AcquirerTransactionDataGroup">
<xs:sequence>
<xs:element name="Entry" type="AcquirerTransactionDataEntry" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>

<xs:complexType name="AcquirerTransactionDataEntry">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="key" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<xs:complexType name="CustomerInfoAddress">
<xs:sequence>
<xs:element name="Firstname" type="xs:string"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String, String> 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);
}
}
Loading
Loading