Skip to content

Commit 27e02df

Browse files
committed
Add helpers to parse specific fields
1 parent 4a7c8d4 commit 27e02df

5 files changed

Lines changed: 867 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package com.adyen.util.tapi;
2+
3+
import java.net.URLDecoder;
4+
import java.nio.charset.StandardCharsets;
5+
import java.util.Collections;
6+
import java.util.LinkedHashMap;
7+
import java.util.Map;
8+
import java.util.Optional;
9+
10+
/**
11+
* A helper class to parse and manage the key-value pairs within a PredefinedContent referenceID
12+
* string. The referenceID is expected to be in a URL query string format (e.g., {@code
13+
* key1=value1&key2=value2}).
14+
*/
15+
public final class PredefinedContentHelper {
16+
17+
private static final String KEY_EVENT = "event";
18+
private static final String KEY_TRANSACTION_ID = "TransactionID";
19+
private static final String KEY_TIME_STAMP = "TimeStamp";
20+
21+
/**
22+
* Defines the supported events for display notifications within a PredefinedContent reference ID.
23+
*/
24+
public enum DisplayNotificationEvent {
25+
TENDER_CREATED,
26+
CARD_INSERTED,
27+
CARD_PRESENTED,
28+
CARD_SWIPED,
29+
WAIT_FOR_APP_SELECTION,
30+
APPLICATION_SELECTED,
31+
ASK_SIGNATURE,
32+
CHECK_SIGNATURE,
33+
SIGNATURE_CHECKED,
34+
WAIT_FOR_PIN,
35+
PIN_ENTERED,
36+
PRINT_RECEIPT,
37+
RECEIPT_PRINTED,
38+
CARD_REMOVED,
39+
TENDER_FINAL,
40+
ASK_DCC,
41+
DCC_ACCEPTED,
42+
DCC_REJECTED,
43+
ASK_GRATUITY,
44+
GRATUITY_ENTERED,
45+
BALANCE_QUERY_STARTED,
46+
BALANCE_QUERY_COMPLETED,
47+
LOAD_STARTED,
48+
LOAD_COMPLETED,
49+
PROVIDE_CARD_DETAILS,
50+
CARD_DETAILS_PROVIDED
51+
}
52+
53+
private final Map<String, String> params;
54+
55+
/**
56+
* Constructs a helper instance by parsing the provided reference ID.
57+
*
58+
* @param referenceId The string from {@code PredefinedContent#getReferenceID()}, expected to be
59+
* in URL query string format.
60+
*/
61+
public PredefinedContentHelper(String referenceId) {
62+
this.params = parse(referenceId);
63+
}
64+
65+
/**
66+
* Extracts and validates the 'event' value from the reference ID.
67+
*
68+
* @return An {@link Optional} containing the {@link DisplayNotificationEvent} if it is present
69+
* and valid, otherwise an empty Optional.
70+
* <pre>{@code
71+
* PredefinedContentHelper helper = new PredefinedContentHelper("...&event=PIN_ENTERED");
72+
* helper.getEvent().ifPresent(event -> System.out.println(event)); // Prints PIN_ENTERED
73+
* }</pre>
74+
*/
75+
public Optional<DisplayNotificationEvent> getEvent() {
76+
return get(KEY_EVENT)
77+
.flatMap(
78+
eventValue -> {
79+
try {
80+
return Optional.of(DisplayNotificationEvent.valueOf(eventValue));
81+
} catch (IllegalArgumentException e) {
82+
return Optional.empty();
83+
}
84+
});
85+
}
86+
87+
/**
88+
* Gets the transaction ID from the reference ID.
89+
*
90+
* @return An {@link Optional} containing the TransactionID, or an empty Optional if not present.
91+
*/
92+
public Optional<String> getTransactionId() {
93+
return get(KEY_TRANSACTION_ID);
94+
}
95+
96+
/**
97+
* Gets the timestamp from the reference ID.
98+
*
99+
* @return An {@link Optional} containing the TimeStamp, or an empty Optional if not present.
100+
*/
101+
public Optional<String> getTimeStamp() {
102+
return get(KEY_TIME_STAMP);
103+
}
104+
105+
/**
106+
* Gets the value for a given key from the reference ID.
107+
*
108+
* @param key The name of the parameter to retrieve.
109+
* @return An {@link Optional} containing the parameter's value, or an empty Optional if not
110+
* present.
111+
*/
112+
public Optional<String> get(String key) {
113+
return Optional.ofNullable(params.get(key));
114+
}
115+
116+
/**
117+
* Returns an unmodifiable view of all parsed parameters.
118+
*
119+
* @return An unmodifiable {@link Map} of all key-value pairs from the reference ID.
120+
*/
121+
public Map<String, String> toMap() {
122+
return Collections.unmodifiableMap(params);
123+
}
124+
125+
/**
126+
* Parses a URL query-like string into a map.
127+
*
128+
* @param referenceId The string to parse.
129+
* @return A map of the parsed key-value pairs.
130+
*/
131+
private static Map<String, String> parse(String referenceId) {
132+
if (referenceId == null || referenceId.trim().isEmpty()) {
133+
return Collections.emptyMap();
134+
}
135+
136+
Map<String, String> queryPairs = new LinkedHashMap<>();
137+
String[] pairs = referenceId.split("&");
138+
for (String pair : pairs) {
139+
int idx = pair.indexOf("=");
140+
if (idx > 0 && idx < pair.length() - 1) {
141+
String key = URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8);
142+
String value = URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8);
143+
queryPairs.put(key, value);
144+
}
145+
}
146+
return queryPairs;
147+
}
148+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.adyen.util.tapi;
2+
3+
import com.adyen.model.tapi.SaleData;
4+
import java.util.Objects;
5+
import java.util.Optional;
6+
7+
/**
8+
* A helper class to work with {@code SaleData} from the TAPI model.
9+
*
10+
* <p>The {@code SaleToAcquirerData} field supports two formats:
11+
*
12+
* <ul>
13+
* <li><b>Base64-encoded JSON</b>: a JSON object encoded as a Base64 string.
14+
* <li><b>Key-value pairs</b>: form-encoded pairs using {@code &} as separator (e.g. {@code
15+
* shopperEmail=foo@bar.com&tenderOption=AskGratuity}).
16+
* </ul>
17+
*
18+
* This helper auto-detects the format and parses it into a {@link SaleToAcquirerData} object.
19+
*/
20+
public final class SaleDataHelper {
21+
22+
private final SaleData saleData;
23+
24+
/**
25+
* Constructs a helper instance wrapping the provided {@link SaleData}.
26+
*
27+
* @param saleData The {@link SaleData} instance to wrap.
28+
*/
29+
public SaleDataHelper(SaleData saleData) {
30+
this.saleData = Objects.requireNonNull(saleData, "saleData must not be null");
31+
}
32+
33+
/**
34+
* Parses the {@code SaleToAcquirerData} field into a {@link SaleToAcquirerData} object. Supports
35+
* both Base64-encoded JSON and form-encoded key-value pair formats.
36+
*
37+
* @return An {@link Optional} containing the parsed {@link SaleToAcquirerData}, or an empty
38+
* Optional if the field is absent or cannot be parsed.
39+
*/
40+
public Optional<SaleToAcquirerData> getSaleToAcquirerData() {
41+
String raw = saleData.getSaleToAcquirerData();
42+
if (raw == null || raw.trim().isEmpty()) {
43+
return Optional.empty();
44+
}
45+
if (SaleToAcquirerData.isBase64Json(raw)) {
46+
try {
47+
return Optional.of(SaleToAcquirerData.fromBase64(raw));
48+
} catch (Exception e) {
49+
return Optional.empty();
50+
}
51+
} else {
52+
try {
53+
return Optional.of(SaleToAcquirerData.fromKeyValuePairs(raw));
54+
} catch (Exception e) {
55+
return Optional.empty();
56+
}
57+
}
58+
}
59+
60+
}

0 commit comments

Comments
 (0)