Skip to content

Commit 526b986

Browse files
committed
Generate ACS webhooks
1 parent ce12858 commit 526b986

File tree

9 files changed

+328
-3
lines changed

9 files changed

+328
-3
lines changed

src/__tests__/notification.spec.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,79 @@ describe("Notification Test", function (): void {
311311
expect(negativeBalanceCompensationWarningNotificationRequest.data.id).toBe("BA00000000000000000001");
312312
expect(negativeBalanceCompensationWarningNotificationRequest.data.creationDate?.toISOString()).toBe(new Date("2024-07-02T02:01:08+02:00").toISOString());
313313
});
314+
315+
it("should deserialize AcsWebhook AuthenticationNotificationRequest", function (): void {
316+
const json = {
317+
"data": {
318+
"authentication": {
319+
"acsTransId": "6a4c1709-a42e-4c7f-96c7-1043adacfc97",
320+
"challenge": {
321+
"flow": "OOB",
322+
"lastInteraction": "2022-12-22T15:49:03+01:00"
323+
},
324+
"challengeIndicator": "01",
325+
"createdAt": "2022-12-22T15:45:03+01:00",
326+
"deviceChannel": "app",
327+
"dsTransID": "a3b86754-444d-46ca-95a2-ada351d3f42c",
328+
"exemptionIndicator": "lowValue",
329+
"inPSD2Scope": true,
330+
"messageCategory": "payment",
331+
"messageVersion": "2.2.0",
332+
"riskScore": 0,
333+
"threeDSServerTransID": "6edcc246-23ee-4e94-ac5d-8ae620bea7d9",
334+
"transStatus": "Y",
335+
"type": "challenge"
336+
},
337+
"balancePlatform": "YOUR_BALANCE_PLATFORM",
338+
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
339+
"paymentInstrumentId": "PI3227C223222B5BPCMFXD2XG",
340+
"purchase": {
341+
"date": "2022-12-22T15:49:03+01:00",
342+
"merchantName": "TeaShop_NL",
343+
"originalAmount": {
344+
"currency": "EUR",
345+
"value": 1000
346+
}
347+
},
348+
"status": "authenticated"
349+
},
350+
"environment": "test",
351+
"timestamp": "2022-12-22T15:42:03+01:00",
352+
"type": "balancePlatform.authentication.created"
353+
};
354+
const jsonString = JSON.stringify(json);
355+
const bankingWebhookHandler = new BankingWebhookHandler(jsonString);
356+
const authenticationNotificationRequest = bankingWebhookHandler.getAuthenticationNotificationRequest();
357+
expect(authenticationNotificationRequest).toBeTruthy();
358+
expect(authenticationNotificationRequest.type).toBe(AuthenticationNotificationRequest
359+
.TypeEnum.BalancePlatformAuthenticationCreated
360+
);
361+
expect(authenticationNotificationRequest.environment).toBe("test");
362+
expect(authenticationNotificationRequest.timestamp?.toISOString()).toBe(new Date("2022-12-22T15:42:03+01:00").toISOString());
363+
expect(authenticationNotificationRequest.data).toBeDefined();
364+
expect(authenticationNotificationRequest.data.balancePlatform).toBe("YOUR_BALANCE_PLATFORM");
365+
expect(authenticationNotificationRequest.data.id).toBe("497f6eca-6276-4993-bfeb-53cbbbba6f08");
366+
});
367+
368+
it("should deserialize AcsWebhook RelayedAuthenticationRequest", function (): void {
369+
const json = {
370+
"id": "1ea64f8e-d1e1-4b9d-a3a2-3953e385b2c8",
371+
"paymentInstrumentId": "PI123ABCDEFGHIJKLMN45678",
372+
"purchase": {
373+
"date": "2025-03-06T15:17:55Z",
374+
"merchantName": "widgetsInc",
375+
"originalAmount": {
376+
"currency": "EUR",
377+
"value": 14548
378+
}
379+
}
380+
};
381+
const jsonString = JSON.stringify(json);
382+
const bankingWebhookHandler = new BankingWebhookHandler(jsonString);
383+
const relayedAuthenticationRequest = bankingWebhookHandler.getRelayedAuthenticationRequest();
384+
expect(relayedAuthenticationRequest).toBeTruthy();
385+
expect(relayedAuthenticationRequest.id).toBe("1ea64f8e-d1e1-4b9d-a3a2-3953e385b2c8");
386+
expect(relayedAuthenticationRequest.paymentInstrumentId).toBe("PI123ABCDEFGHIJKLMN45678");
387+
expect(relayedAuthenticationRequest.purchase).toBeDefined();
388+
});
314389
});

src/notification/bankingWebhookHandler.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class BankingWebhookHandler {
1616

1717
// Return generic webhook type
1818
public getGenericWebhook(): acsWebhooks.AuthenticationNotificationRequest
19+
| acsWebhooks.RelayedAuthenticationRequest
1920
| configurationWebhooks.AccountHolderNotificationRequest
2021
| configurationWebhooks.BalanceAccountNotificationRequest
2122
| configurationWebhooks.PaymentNotificationRequest
@@ -67,13 +68,23 @@ class BankingWebhookHandler {
6768
return this.getTransactionNotificationRequest();
6869
}
6970

71+
if(!type && this.payload["paymentInstrumentId"]){
72+
// ad-hoc fix for the relayed authentication request
73+
// if type is undefined but paymentInstrumentId is present then it is a relayedAuthenticationRequest
74+
return this.getRelayedAuthenticationRequest();
75+
}
76+
7077
throw new Error("Could not parse the json payload: " + this.payload);
7178
}
7279

7380
public getAuthenticationNotificationRequest(): acsWebhooks.AuthenticationNotificationRequest {
7481
return acsWebhooks.ObjectSerializer.deserialize(this.payload, "AuthenticationNotificationRequest");
7582
}
7683

84+
public getRelayedAuthenticationRequest(): acsWebhooks.RelayedAuthenticationRequest {
85+
return acsWebhooks.ObjectSerializer.deserialize(this.payload, "RelayedAuthenticationRequest");
86+
}
87+
7788
public getAccountHolderNotificationRequest(): configurationWebhooks.AccountHolderNotificationRequest {
7889
return configurationWebhooks.ObjectSerializer.deserialize(this.payload, "AccountHolderNotificationRequest");
7990
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* The version of the OpenAPI document: v1
3+
*
4+
*
5+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
6+
* https://openapi-generator.tech
7+
* Do not edit this class manually.
8+
*/
9+
10+
11+
export class AuthenticationDecision {
12+
/**
13+
* The status of the authentication. Possible values: * **refused** * **proceed** For more information, refer to [Authenticate cardholders using the Authentication SDK](https://docs.adyen.com/issuing/3d-secure/oob-auth-sdk/authenticate-cardholders/).
14+
*/
15+
'status': AuthenticationDecision.StatusEnum;
16+
17+
static discriminator: string | undefined = undefined;
18+
19+
static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
20+
{
21+
"name": "status",
22+
"baseName": "status",
23+
"type": "AuthenticationDecision.StatusEnum"
24+
} ];
25+
26+
static getAttributeTypeMap() {
27+
return AuthenticationDecision.attributeTypeMap;
28+
}
29+
}
30+
31+
export namespace AuthenticationDecision {
32+
export enum StatusEnum {
33+
Proceed = 'proceed',
34+
Refused = 'refused'
35+
}
36+
}

src/typings/acsWebhooks/challengeInfo.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class ChallengeInfo {
1414
*/
1515
'challengeCancel'?: ChallengeInfo.ChallengeCancelEnum;
1616
/**
17-
* The flow used in the challenge. Possible values: * **OTP_SMS**: one-time password (OTP) flow * **OOB**: out-of-band (OOB) flow
17+
* The flow used in the challenge. Possible values: * **PWD_OTP_PHONE_FL**: one-time password (OTP) flow via SMS * **PWD_OTP_EMAIL_FL**: one-time password (OTP) flow via email * **OOB_TRIGGER_FL**: out-of-band (OOB) flow
1818
*/
1919
'flow': ChallengeInfo.FlowEnum;
2020
/**
@@ -86,7 +86,8 @@ export namespace ChallengeInfo {
8686
_08 = '08'
8787
}
8888
export enum FlowEnum {
89-
OtpSms = 'OTP_SMS',
90-
Oob = 'OOB'
89+
PwdOtpPhoneFl = 'PWD_OTP_PHONE_FL',
90+
PwdOtpEmailFl = 'PWD_OTP_EMAIL_FL',
91+
OobTriggerFl = 'OOB_TRIGGER_FL'
9192
}
9293
}

src/typings/acsWebhooks/models.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,33 @@
99

1010

1111
export * from './amount';
12+
export * from './authenticationDecision';
1213
export * from './authenticationInfo';
1314
export * from './authenticationNotificationData';
1415
export * from './authenticationNotificationRequest';
1516
export * from './balancePlatformNotificationResponse';
1617
export * from './challengeInfo';
18+
export * from './purchase';
1719
export * from './purchaseInfo';
20+
export * from './relayedAuthenticationRequest';
21+
export * from './relayedAuthenticationResponse';
1822
export * from './resource';
23+
export * from './serviceError';
1924

2025

2126
import { Amount } from './amount';
27+
import { AuthenticationDecision } from './authenticationDecision';
2228
import { AuthenticationInfo } from './authenticationInfo';
2329
import { AuthenticationNotificationData } from './authenticationNotificationData';
2430
import { AuthenticationNotificationRequest } from './authenticationNotificationRequest';
2531
import { BalancePlatformNotificationResponse } from './balancePlatformNotificationResponse';
2632
import { ChallengeInfo } from './challengeInfo';
33+
import { Purchase } from './purchase';
2734
import { PurchaseInfo } from './purchaseInfo';
35+
import { RelayedAuthenticationRequest } from './relayedAuthenticationRequest';
36+
import { RelayedAuthenticationResponse } from './relayedAuthenticationResponse';
2837
import { Resource } from './resource';
38+
import { ServiceError } from './serviceError';
2939

3040
/* tslint:disable:no-unused-variable */
3141
let primitives = [
@@ -40,6 +50,7 @@ let primitives = [
4050
];
4151

4252
let enumsMap: {[index: string]: any} = {
53+
"AuthenticationDecision.StatusEnum": AuthenticationDecision.StatusEnum,
4354
"AuthenticationInfo.ChallengeIndicatorEnum": AuthenticationInfo.ChallengeIndicatorEnum,
4455
"AuthenticationInfo.DeviceChannelEnum": AuthenticationInfo.DeviceChannelEnum,
4556
"AuthenticationInfo.ExemptionIndicatorEnum": AuthenticationInfo.ExemptionIndicatorEnum,
@@ -55,13 +66,18 @@ let enumsMap: {[index: string]: any} = {
5566

5667
let typeMap: {[index: string]: any} = {
5768
"Amount": Amount,
69+
"AuthenticationDecision": AuthenticationDecision,
5870
"AuthenticationInfo": AuthenticationInfo,
5971
"AuthenticationNotificationData": AuthenticationNotificationData,
6072
"AuthenticationNotificationRequest": AuthenticationNotificationRequest,
6173
"BalancePlatformNotificationResponse": BalancePlatformNotificationResponse,
6274
"ChallengeInfo": ChallengeInfo,
75+
"Purchase": Purchase,
6376
"PurchaseInfo": PurchaseInfo,
77+
"RelayedAuthenticationRequest": RelayedAuthenticationRequest,
78+
"RelayedAuthenticationResponse": RelayedAuthenticationResponse,
6479
"Resource": Resource,
80+
"ServiceError": ServiceError,
6581
}
6682

6783
export class ObjectSerializer {

src/typings/acsWebhooks/purchase.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* The version of the OpenAPI document: v1
3+
*
4+
*
5+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
6+
* https://openapi-generator.tech
7+
* Do not edit this class manually.
8+
*/
9+
10+
import { Amount } from './amount';
11+
12+
export class Purchase {
13+
/**
14+
* The time of the purchase.
15+
*/
16+
'date': Date;
17+
/**
18+
* The name of the merchant.
19+
*/
20+
'merchantName': string;
21+
'originalAmount': Amount;
22+
23+
static discriminator: string | undefined = undefined;
24+
25+
static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
26+
{
27+
"name": "date",
28+
"baseName": "date",
29+
"type": "Date"
30+
},
31+
{
32+
"name": "merchantName",
33+
"baseName": "merchantName",
34+
"type": "string"
35+
},
36+
{
37+
"name": "originalAmount",
38+
"baseName": "originalAmount",
39+
"type": "Amount"
40+
} ];
41+
42+
static getAttributeTypeMap() {
43+
return Purchase.attributeTypeMap;
44+
}
45+
}
46+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* The version of the OpenAPI document: v1
3+
*
4+
*
5+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
6+
* https://openapi-generator.tech
7+
* Do not edit this class manually.
8+
*/
9+
10+
import { Purchase } from './purchase';
11+
12+
export class RelayedAuthenticationRequest {
13+
/**
14+
* The unique identifier of the challenge.
15+
*/
16+
'id': string;
17+
/**
18+
* The unique identifier of the [payment instrument](https://docs.adyen.com/api-explorer/balanceplatform/latest/get/paymentInstruments/_id_) used for the purchase.
19+
*/
20+
'paymentInstrumentId': string;
21+
'purchase': Purchase;
22+
23+
static discriminator: string | undefined = undefined;
24+
25+
static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
26+
{
27+
"name": "id",
28+
"baseName": "id",
29+
"type": "string"
30+
},
31+
{
32+
"name": "paymentInstrumentId",
33+
"baseName": "paymentInstrumentId",
34+
"type": "string"
35+
},
36+
{
37+
"name": "purchase",
38+
"baseName": "purchase",
39+
"type": "Purchase"
40+
} ];
41+
42+
static getAttributeTypeMap() {
43+
return RelayedAuthenticationRequest.attributeTypeMap;
44+
}
45+
}
46+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* The version of the OpenAPI document: v1
3+
*
4+
*
5+
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
6+
* https://openapi-generator.tech
7+
* Do not edit this class manually.
8+
*/
9+
10+
import { AuthenticationDecision } from './authenticationDecision';
11+
12+
export class RelayedAuthenticationResponse {
13+
'authenticationDecision': AuthenticationDecision;
14+
15+
static discriminator: string | undefined = undefined;
16+
17+
static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [
18+
{
19+
"name": "authenticationDecision",
20+
"baseName": "authenticationDecision",
21+
"type": "AuthenticationDecision"
22+
} ];
23+
24+
static getAttributeTypeMap() {
25+
return RelayedAuthenticationResponse.attributeTypeMap;
26+
}
27+
}
28+

0 commit comments

Comments
 (0)