Skip to content

Commit 9d8767e

Browse files
authored
Merge pull request #1525 from Adyen/add-tests-serialization
Tests serialization additional data
2 parents f94c4b5 + 97b7377 commit 9d8767e

File tree

3 files changed

+326
-0
lines changed

3 files changed

+326
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { MessageCategoryType, MessageClassType, MessageType, ObjectSerializer, TerminalApiRequest } from "../typings/terminal/models";
2+
3+
4+
describe("ObjectSerializer.serialize", () => {
5+
6+
it("should serialize TerminalAPIPaymentRequest", () => {
7+
const terminalAPIPaymentRequest = newTerminalAPIPaymentRequest();
8+
9+
const json = ObjectSerializer.serialize(terminalAPIPaymentRequest, "TerminalApiRequest");
10+
11+
expect(json).toBeTruthy();
12+
expect(json).toHaveProperty("SaleToPOIRequest");
13+
expect(json.SaleToPOIRequest).toHaveProperty("MessageHeader");
14+
expect(json.SaleToPOIRequest).toHaveProperty("PaymentRequest");
15+
16+
expect(json.SaleToPOIRequest.PaymentRequest).toHaveProperty("SaleData");
17+
expect(json.SaleToPOIRequest.PaymentRequest.SaleData).toHaveProperty("SaleTransactionID");
18+
expect(json.SaleToPOIRequest.PaymentRequest.SaleData.SaleTransactionID).toHaveProperty("TimeStamp");
19+
expect(json.SaleToPOIRequest.PaymentRequest.SaleData).toHaveProperty("SaleToAcquirerData");
20+
21+
const saleToAcquirerData = json.SaleToPOIRequest.PaymentRequest.SaleData.SaleToAcquirerData;
22+
// SaleToAcquirerData should be a string
23+
expect(typeof(saleToAcquirerData)).toBe("string");
24+
// SaleToAcquirerData should be base64 encoded
25+
expect(() => Buffer.from(saleToAcquirerData, "base64").toString("utf-8")).not.toThrow();
26+
27+
// decode and test metadata
28+
const decoded = JSON.parse(Buffer.from(saleToAcquirerData, "base64").toString("utf-8"));
29+
expect(decoded).toHaveProperty("applicationInfo");
30+
31+
expect(decoded).toHaveProperty("metadata");
32+
expect(decoded.metadata).toHaveProperty("someMetaDataKey1");
33+
expect(decoded.metadata).toHaveProperty("someMetaDataKey2");
34+
expect(decoded.metadata.someMetaDataKey1).toBe("YOUR_VALUE");
35+
expect(decoded.metadata.someMetaDataKey2).toBe("YOUR_VALUE");
36+
});
37+
38+
it("should serialize SaleToAcquirerData as base64 if object", () => {
39+
const obj = { foo: "bar" };
40+
const expected = Buffer.from(JSON.stringify(obj)).toString("base64");
41+
expect(ObjectSerializer.serialize(obj, "SaleToAcquirerData")).toBe(expected);
42+
});
43+
44+
it("should serialize TerminalAPIPaymentRequest with additionalData as JSON object", () => {
45+
const terminalAPIPaymentRequest = newTerminalAPIPaymentRequestWithAdditionalDataAsJSONObject();
46+
47+
const json = ObjectSerializer.serialize(terminalAPIPaymentRequest, "TerminalApiRequest");
48+
49+
expect(json).toBeTruthy();
50+
expect(json).toHaveProperty("SaleToPOIRequest");
51+
expect(json.SaleToPOIRequest).toHaveProperty("MessageHeader");
52+
expect(json.SaleToPOIRequest).toHaveProperty("PaymentRequest");
53+
54+
expect(json.SaleToPOIRequest.PaymentRequest).toHaveProperty("SaleData");
55+
expect(json.SaleToPOIRequest.PaymentRequest.SaleData).toHaveProperty("SaleToAcquirerData");
56+
const saleToAcquirerData = json.SaleToPOIRequest.PaymentRequest.SaleData.SaleToAcquirerData;
57+
// SaleToAcquirerData should be a string
58+
expect(typeof(saleToAcquirerData)).toBe("string");
59+
// SaleToAcquirerData should be base64 encoded
60+
expect(() => Buffer.from(saleToAcquirerData, "base64").toString("utf-8")).not.toThrow();
61+
62+
// decode and test metadata
63+
const decoded = JSON.parse(Buffer.from(saleToAcquirerData, "base64").toString("utf-8"));
64+
// verify manualCapture
65+
expect(decoded).toHaveProperty("additionalData");
66+
expect(decoded.additionalData).toHaveProperty("manualCapture");
67+
expect(decoded.additionalData.manualCapture).toBe("true");
68+
});
69+
70+
it("should return undefined when data is undefined", () => {
71+
expect(ObjectSerializer.serialize(undefined, "string")).toBeUndefined();
72+
});
73+
74+
it("should serialize primitive types", () => {
75+
expect(ObjectSerializer.serialize("hello", "string")).toBe("hello");
76+
expect(ObjectSerializer.serialize(123, "number")).toBe(123);
77+
expect(ObjectSerializer.serialize(true, "boolean")).toBe(true);
78+
});
79+
80+
it("should serialize Date to ISO string", () => {
81+
const date = new Date("2024-01-01T12:00:00Z");
82+
expect(ObjectSerializer.serialize(date, "Date")).toBe(date.toISOString());
83+
});
84+
85+
it("should serialize Array of primitives", () => {
86+
expect(ObjectSerializer.serialize([1, 2, 3], "Array<number>")).toEqual([1, 2, 3]);
87+
expect(ObjectSerializer.serialize(["a", "b"], "Array<string>")).toEqual(["a", "b"]);
88+
});
89+
90+
it("should serialize SaleToAcquirerData as string if already string", () => {
91+
expect(ObjectSerializer.serialize("somestring", "SaleToAcquirerData")).toBe("somestring");
92+
});
93+
94+
it("should serialize enums as is", () => {
95+
expect(ObjectSerializer.serialize("Default", "AccountType")).toBe("Default");
96+
});
97+
98+
});
99+
100+
const newTerminalAPIPaymentRequest = (): TerminalApiRequest => {
101+
const messageHeader = {
102+
MessageCategory: MessageCategoryType.Payment,
103+
MessageClass: MessageClassType.Service,
104+
MessageType: MessageType.Request,
105+
POIID: "V400m-324688179",
106+
ProtocolVersion: "3.0",
107+
SaleID: "POSSystemID12345",
108+
ServiceID: "0207111104",
109+
};
110+
111+
const paymentRequest = {
112+
PaymentTransaction: {
113+
AmountsReq: {
114+
Currency: "EUR",
115+
RequestedAmount: 1000,
116+
},
117+
},
118+
SaleData: {
119+
SaleTransactionID: {
120+
TimeStamp: new Date().toISOString(),
121+
TransactionID: "1234567890",
122+
},
123+
SaleToAcquirerData: {
124+
applicationInfo: {
125+
merchantApplication: {
126+
version: "1.0",
127+
name: "TestApp",
128+
},
129+
},
130+
metadata: {
131+
someMetaDataKey1: "YOUR_VALUE",
132+
someMetaDataKey2: "YOUR_VALUE",
133+
},
134+
},
135+
},
136+
};
137+
138+
return { SaleToPOIRequest: {MessageHeader: messageHeader, PaymentRequest: paymentRequest} };
139+
};
140+
141+
const newTerminalAPIPaymentRequestWithAdditionalDataAsJSONObject = (): TerminalApiRequest => {
142+
const messageHeader = {
143+
MessageCategory: MessageCategoryType.Payment,
144+
MessageClass: MessageClassType.Service,
145+
MessageType: MessageType.Request,
146+
POIID: "V400m-324688179",
147+
ProtocolVersion: "3.0",
148+
SaleID: "POSSystemID12345",
149+
ServiceID: "0207111104",
150+
};
151+
152+
const paymentRequest = {
153+
PaymentTransaction: {
154+
AmountsReq: {
155+
Currency: "EUR",
156+
RequestedAmount: 1000,
157+
},
158+
},
159+
SaleData: {
160+
SaleTransactionID: {
161+
TimeStamp: new Date().toISOString(),
162+
TransactionID: "1234567890",
163+
},
164+
SaleToAcquirerData: {
165+
additionalData: {
166+
manualCapture: "true"
167+
},
168+
},
169+
},
170+
};
171+
172+
return { SaleToPOIRequest: {MessageHeader: messageHeader, PaymentRequest: paymentRequest} };
173+
};

src/__tests__/webhooks/notification.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,54 @@ describe("Notification Tests", function (): void {
9191
}
9292
});
9393

94+
it("should return notification items with additional data as key-value map", function (): void {
95+
const notificationRequestItem = new NotificationRequestItem();
96+
97+
notificationRequestItem.amount = { currency: "EUR", value: 1000 };
98+
notificationRequestItem.pspReference = "123456789";
99+
notificationRequestItem.eventCode = NotificationEnum.Authorisation;
100+
notificationRequestItem.eventDate = "2023-10-01T12:00:00+00:00";
101+
notificationRequestItem.merchantAccountCode = "TestMerchant";
102+
notificationRequestItem.merchantReference = "TestReference";
103+
notificationRequestItem.success = SuccessEnum.True;
104+
notificationRequestItem.additionalData = {
105+
orderId: "12345",
106+
customerId: "67890"
107+
};
108+
109+
expect(notificationRequestItem.amount).toBeDefined();
110+
expect(notificationRequestItem.additionalData).toBeDefined();
111+
expect(notificationRequestItem.additionalData).toHaveProperty("orderId", "12345");
112+
expect(notificationRequestItem.additionalData).toHaveProperty("customerId", "67890");
113+
114+
});
115+
116+
// test additionanDalata with medata as key-value pairs prefixed with "metadata." i.e. "metadata.myKey": "myValue"
117+
it("should return notification items with additional data as key-value object", function (): void {
118+
const notificationRequestItem = new NotificationRequestItem();
119+
120+
notificationRequestItem.amount = { currency: "EUR", value: 1000 };
121+
notificationRequestItem.pspReference = "123456789";
122+
notificationRequestItem.eventCode = NotificationEnum.Authorisation;
123+
notificationRequestItem.eventDate = "2023-10-01T12:00:00+00:00";
124+
notificationRequestItem.merchantAccountCode = "TestMerchant";
125+
notificationRequestItem.merchantReference = "TestReference";
126+
notificationRequestItem.success = SuccessEnum.True;
127+
notificationRequestItem.additionalData = {
128+
orderId: "12345",
129+
customerId: "67890",
130+
"metadata.myKey": "myValue",
131+
"metadata.anotherKey": "anotherValue"
132+
};
133+
134+
expect(notificationRequestItem.amount).toBeDefined();
135+
expect(notificationRequestItem.additionalData).toBeDefined();
136+
expect(notificationRequestItem.additionalData).toHaveProperty("orderId", "12345");
137+
expect(notificationRequestItem.additionalData).toHaveProperty("customerId", "67890");
138+
expect(notificationRequestItem.additionalData["metadata.myKey"]).toBeDefined();
139+
expect(notificationRequestItem.additionalData["metadata.anotherKey"]).toBeDefined();
140+
expect(notificationRequestItem.additionalData["metadata.myKey"]).toEqual("myValue");
141+
expect(notificationRequestItem.additionalData["metadata.anotherKey"]).toEqual("anotherValue");
142+
143+
});
94144
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* PredefinedContentHelper class to parse and manage predefined content reference IDs.
3+
*/
4+
export class PredefinedContentHelper {
5+
private params: URLSearchParams;
6+
7+
constructor(referenceId: string) {
8+
this.params = new URLSearchParams(referenceId);
9+
}
10+
11+
/**
12+
* Extracts and validates the `event` value from the ReferenceID.
13+
*
14+
* @returns A valid `DisplayNotificationEvent`, otherwise `null`.
15+
*
16+
* @example
17+
* const helper = new PredefinedContentHelper("...&event=PIN_ENTERED");
18+
* const event = helper.getEvent(); // "PIN_ENTERED" or null
19+
*/
20+
getEvent(): DisplayNotificationEvent | null {
21+
const event = this.params.get("event");
22+
23+
switch (event) {
24+
case "TENDER_CREATED":
25+
case "CARD_INSERTED":
26+
case "CARD_PRESENTED":
27+
case "CARD_SWIPED":
28+
case "WAIT_FOR_APP_SELECTION":
29+
case "APPLICATION_SELECTED":
30+
case "ASK_SIGNATURE":
31+
case "CHECK_SIGNATURE":
32+
case "SIGNATURE_CHECKED":
33+
case "WAIT_FOR_PIN":
34+
case "PIN_ENTERED":
35+
case "PRINT_RECEIPT":
36+
case "RECEIPT_PRINTED":
37+
case "CARD_REMOVED":
38+
case "TENDER_FINAL":
39+
case "ASK_DCC":
40+
case "DCC_ACCEPTED":
41+
case "DCC_REJECTED":
42+
case "ASK_GRATUITY":
43+
case "GRATUITY_ENTERED":
44+
case "BALANCE_QUERY_STARTED":
45+
case "BALANCE_QUERY_COMPLETED":
46+
case "LOAD_STARTED":
47+
case "LOAD_COMPLETED":
48+
case "PROVIDE_CARD_DETAILS":
49+
case "CARD_DETAILS_PROVIDED":
50+
return event as DisplayNotificationEvent;
51+
default:
52+
return null;
53+
}
54+
}
55+
getTransactionId(): string | null {
56+
return this.params.get("TransactionID");
57+
}
58+
59+
getTimeStamp(): string | null {
60+
return this.params.get("TimeStamp");
61+
}
62+
63+
get(key: string): string | null {
64+
return this.params.get(key);
65+
}
66+
67+
toObject(): Record<string, string> {
68+
const result: Record<string, string> = {};
69+
for (const [key, value] of this.params.entries()) {
70+
result[key] = value;
71+
}
72+
return result;
73+
}
74+
}
75+
76+
export enum DisplayNotificationEvent {
77+
TENDER_CREATED = "TENDER_CREATED",
78+
CARD_INSERTED = "CARD_INSERTED",
79+
CARD_PRESENTED = "CARD_PRESENTED",
80+
CARD_SWIPED = "CARD_SWIPED",
81+
WAIT_FOR_APP_SELECTION = "WAIT_FOR_APP_SELECTION",
82+
APPLICATION_SELECTED = "APPLICATION_SELECTED",
83+
ASK_SIGNATURE = "ASK_SIGNATURE",
84+
CHECK_SIGNATURE = "CHECK_SIGNATURE",
85+
SIGNATURE_CHECKED = "SIGNATURE_CHECKED",
86+
WAIT_FOR_PIN = "WAIT_FOR_PIN",
87+
PIN_ENTERED = "PIN_ENTERED",
88+
PRINT_RECEIPT = "PRINT_RECEIPT",
89+
RECEIPT_PRINTED = "RECEIPT_PRINTED",
90+
CARD_REMOVED = "CARD_REMOVED",
91+
TENDER_FINAL = "TENDER_FINAL",
92+
ASK_DCC = "ASK_DCC",
93+
DCC_ACCEPTED = "DCC_ACCEPTED",
94+
DCC_REJECTED = "DCC_REJECTED",
95+
ASK_GRATUITY = "ASK_GRATUITY",
96+
GRATUITY_ENTERED = "GRATUITY_ENTERED",
97+
BALANCE_QUERY_STARTED = "BALANCE_QUERY_STARTED",
98+
BALANCE_QUERY_COMPLETED = "BALANCE_QUERY_COMPLETED",
99+
LOAD_STARTED = "LOAD_STARTED",
100+
LOAD_COMPLETED = "LOAD_COMPLETED",
101+
PROVIDE_CARD_DETAILS = "PROVIDE_CARD_DETAILS",
102+
CARD_DETAILS_PROVIDED = "CARD_DETAILS_PROVIDED",
103+
}

0 commit comments

Comments
 (0)