Skip to content

Commit 7cc1838

Browse files
authored
Merge pull request #1494 from Adyen/generate-webhooks-models
Generate Configuration/ACS/Transfer/Transaction/Dispute webhooks and add Balance webhooks
2 parents 1da963f + da3ec7d commit 7cc1838

32 files changed

+935
-28
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The library supports all webhooks under the following model directories:
4545
| Webhooks | Description | Model Name | Supported Version |
4646
|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|-------------------|
4747
| [Authentication Webhooks](https://docs.adyen.com/api-explorer/acs-webhook/1/overview) | Adyen sends this webhook when the process of cardholder authentication is finalized, whether it is completed successfully, fails, or expires. | [AcsWebhooks](src/typings/acsWebhooks) | **v1** |
48+
| [Balance Webhooks](https://docs.adyen.com/api-explorer/balance-webhooks/1/overview) | Adyen sends webhooks to inform you of balance changes in your balance platform. | [balancewebhooks](src/main/java/com/adyen/model/balancewebhooks) | **v1** |
4849
| [Configuration Webhooks](https://docs.adyen.com/api-explorer/balanceplatform-webhooks/2/overview) | You can use these webhooks to build your implementation. For example, you can use this information to update internal statuses when the status of a capability is changed. | [ConfigurationNotification](src/typings/configurationWebhooks) | **v2** |
4950
| [Transfer Webhooks](https://docs.adyen.com/api-explorer/transfer-webhooks/4/overview) | You can use these webhooks to build your implementation. For example, you can use this information to update balances in your own dashboards or to keep track of incoming funds. | [TransferNotification](src/typings/transferWebhooks) | **v4** |
5051
| [Report Webhooks](https://docs.adyen.com/api-explorer/report-webhooks/1/overview) | You can download reports programmatically by making an HTTP GET request, or manually from your Balance Platform Customer Area | [ReportNotification](src/typings/reportWebhooks) | **v1** |

src/__tests__/notification.spec.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { PaymentMethodRequestRemovedNotificationRequest } from "../typings/manag
2121
import { PaymentMethodScheduledForRemovalNotificationRequest } from "../typings/managementWebhooks/paymentMethodScheduledForRemovalNotificationRequest";
2222
import { TransactionNotificationRequestV4 } from "../typings/transactionWebhooks/transactionNotificationRequestV4";
2323
import { NegativeBalanceCompensationWarningNotificationRequest } from "../typings/negativeBalanceWarningWebhooks/negativeBalanceCompensationWarningNotificationRequest";
24+
import { BalanceAccountBalanceNotificationRequest } from "../typings/balanceWebhooks/balanceAccountBalanceNotificationRequest";
2425

2526
describe("Notification Test", function (): void {
2627

@@ -311,4 +312,113 @@ describe("Notification Test", function (): void {
311312
expect(negativeBalanceCompensationWarningNotificationRequest.data.id).toBe("BA00000000000000000001");
312313
expect(negativeBalanceCompensationWarningNotificationRequest.data.creationDate?.toISOString()).toBe(new Date("2024-07-02T02:01:08+02:00").toISOString());
313314
});
315+
316+
it("should deserialize AcsWebhook AuthenticationNotificationRequest", function (): void {
317+
const json = {
318+
"data": {
319+
"authentication": {
320+
"acsTransId": "6a4c1709-a42e-4c7f-96c7-1043adacfc97",
321+
"challenge": {
322+
"flow": "OOB",
323+
"lastInteraction": "2022-12-22T15:49:03+01:00"
324+
},
325+
"challengeIndicator": "01",
326+
"createdAt": "2022-12-22T15:45:03+01:00",
327+
"deviceChannel": "app",
328+
"dsTransID": "a3b86754-444d-46ca-95a2-ada351d3f42c",
329+
"exemptionIndicator": "lowValue",
330+
"inPSD2Scope": true,
331+
"messageCategory": "payment",
332+
"messageVersion": "2.2.0",
333+
"riskScore": 0,
334+
"threeDSServerTransID": "6edcc246-23ee-4e94-ac5d-8ae620bea7d9",
335+
"transStatus": "Y",
336+
"type": "challenge"
337+
},
338+
"balancePlatform": "YOUR_BALANCE_PLATFORM",
339+
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
340+
"paymentInstrumentId": "PI3227C223222B5BPCMFXD2XG",
341+
"purchase": {
342+
"date": "2022-12-22T15:49:03+01:00",
343+
"merchantName": "TeaShop_NL",
344+
"originalAmount": {
345+
"currency": "EUR",
346+
"value": 1000
347+
}
348+
},
349+
"status": "authenticated"
350+
},
351+
"environment": "test",
352+
"timestamp": "2022-12-22T15:42:03+01:00",
353+
"type": "balancePlatform.authentication.created"
354+
};
355+
const jsonString = JSON.stringify(json);
356+
const bankingWebhookHandler = new BankingWebhookHandler(jsonString);
357+
const authenticationNotificationRequest = bankingWebhookHandler.getAuthenticationNotificationRequest();
358+
expect(authenticationNotificationRequest).toBeTruthy();
359+
expect(authenticationNotificationRequest.type).toBe(AuthenticationNotificationRequest
360+
.TypeEnum.BalancePlatformAuthenticationCreated
361+
);
362+
expect(authenticationNotificationRequest.environment).toBe("test");
363+
expect(authenticationNotificationRequest.timestamp?.toISOString()).toBe(new Date("2022-12-22T15:42:03+01:00").toISOString());
364+
expect(authenticationNotificationRequest.data).toBeDefined();
365+
expect(authenticationNotificationRequest.data.balancePlatform).toBe("YOUR_BALANCE_PLATFORM");
366+
expect(authenticationNotificationRequest.data.id).toBe("497f6eca-6276-4993-bfeb-53cbbbba6f08");
367+
});
368+
369+
it("should deserialize AcsWebhook RelayedAuthenticationRequest", function (): void {
370+
const json = {
371+
"id": "1ea64f8e-d1e1-4b9d-a3a2-3953e385b2c8",
372+
"paymentInstrumentId": "PI123ABCDEFGHIJKLMN45678",
373+
"purchase": {
374+
"date": "2025-03-06T15:17:55Z",
375+
"merchantName": "widgetsInc",
376+
"originalAmount": {
377+
"currency": "EUR",
378+
"value": 14548
379+
}
380+
}
381+
};
382+
const jsonString = JSON.stringify(json);
383+
const bankingWebhookHandler = new BankingWebhookHandler(jsonString);
384+
const relayedAuthenticationRequest = bankingWebhookHandler.getRelayedAuthenticationRequest();
385+
expect(relayedAuthenticationRequest).toBeTruthy();
386+
expect(relayedAuthenticationRequest.id).toBe("1ea64f8e-d1e1-4b9d-a3a2-3953e385b2c8");
387+
expect(relayedAuthenticationRequest.paymentInstrumentId).toBe("PI123ABCDEFGHIJKLMN45678");
388+
expect(relayedAuthenticationRequest.purchase).toBeDefined();
389+
});
390+
391+
it("should deserialize BalanceWebhooks BalanceAccountBalanceNotificationRequest", function (): void {
392+
const json = {
393+
"data": {
394+
"balanceAccountId": "BWHS00000000000000000000000001",
395+
"balancePlatform": "YOUR_BALANCE_PLATFORM",
396+
"balances": {
397+
"available": 499900,
398+
"pending": 350000,
399+
"reserved": 120000,
400+
"balance": 470000
401+
},
402+
"creationDate": "2025-01-19T13:37:38+02:00",
403+
"currency": "USD",
404+
"settingIds": ["WK1", "WK2"]
405+
},
406+
"environment": "test",
407+
"type": "balancePlatform.balanceAccount.balance.updated"
408+
};
409+
const jsonString = JSON.stringify(json);
410+
const bankingWebhookHandler = new BankingWebhookHandler(jsonString);
411+
const balanceAccountBalanceNotificationRequest = bankingWebhookHandler.getBalanceAccountBalanceNotificationRequest();
412+
expect(balanceAccountBalanceNotificationRequest).toBeTruthy();
413+
expect(balanceAccountBalanceNotificationRequest.type).toBe(BalanceAccountBalanceNotificationRequest
414+
.TypeEnum.BalancePlatformBalanceAccountBalanceUpdated
415+
);
416+
expect(balanceAccountBalanceNotificationRequest.environment).toBe("test");
417+
expect(balanceAccountBalanceNotificationRequest.data).toBeDefined();
418+
expect(balanceAccountBalanceNotificationRequest.data.settingIds).toBeTruthy();
419+
expect(balanceAccountBalanceNotificationRequest.data.settingIds.length).toBe(2);
420+
expect(balanceAccountBalanceNotificationRequest.data.balancePlatform).toBe("YOUR_BALANCE_PLATFORM");
421+
expect(balanceAccountBalanceNotificationRequest.data .currency).toBe("USD");
422+
});
423+
314424
});

src/notification/bankingWebhookHandler.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* See the LICENSE file for more info.
66
*/
77

8-
import {configurationWebhooks, acsWebhooks, reportWebhooks, transferWebhooks, transactionWebhooks, negativeBalanceWarningWebhooks} from "../typings";
8+
import {configurationWebhooks, acsWebhooks, reportWebhooks, transferWebhooks, transactionWebhooks, negativeBalanceWarningWebhooks, balanceWebhooks} from "../typings";
99

1010
class BankingWebhookHandler {
1111
private readonly payload: string;
@@ -16,6 +16,8 @@ class BankingWebhookHandler {
1616

1717
// Return generic webhook type
1818
public getGenericWebhook(): acsWebhooks.AuthenticationNotificationRequest
19+
| acsWebhooks.RelayedAuthenticationRequest
20+
| balanceWebhooks.BalanceAccountBalanceNotificationRequest
1921
| configurationWebhooks.AccountHolderNotificationRequest
2022
| configurationWebhooks.BalanceAccountNotificationRequest
2123
| configurationWebhooks.PaymentNotificationRequest
@@ -36,7 +38,7 @@ class BankingWebhookHandler {
3638
}
3739

3840
if(Object.values(configurationWebhooks.BalanceAccountNotificationRequest.TypeEnum).includes(type)){
39-
return this.getBalanceAccountNotificationRequest();
41+
return this.getBalanceAccountBalanceNotificationRequest();
4042
}
4143

4244
if(Object.values(configurationWebhooks.CardOrderNotificationRequest.TypeEnum).includes(type)){
@@ -67,19 +69,33 @@ class BankingWebhookHandler {
6769
return this.getTransactionNotificationRequest();
6870
}
6971

72+
if(Object.values(balanceWebhooks.BalanceAccountBalanceNotificationRequest.TypeEnum).includes(type)){
73+
return this.BalanceAccountBalanceNotificationRequest();
74+
}
75+
76+
if(!type && this.payload["paymentInstrumentId"]){
77+
// ad-hoc fix for the relayed authentication request
78+
// if type is undefined but paymentInstrumentId is present then it is a relayedAuthenticationRequest
79+
return this.getRelayedAuthenticationRequest();
80+
}
81+
7082
throw new Error("Could not parse the json payload: " + this.payload);
7183
}
7284

7385
public getAuthenticationNotificationRequest(): acsWebhooks.AuthenticationNotificationRequest {
7486
return acsWebhooks.ObjectSerializer.deserialize(this.payload, "AuthenticationNotificationRequest");
7587
}
7688

77-
public getAccountHolderNotificationRequest(): configurationWebhooks.AccountHolderNotificationRequest {
78-
return configurationWebhooks.ObjectSerializer.deserialize(this.payload, "AccountHolderNotificationRequest");
89+
public getRelayedAuthenticationRequest(): acsWebhooks.RelayedAuthenticationRequest {
90+
return acsWebhooks.ObjectSerializer.deserialize(this.payload, "RelayedAuthenticationRequest");
7991
}
8092

81-
public getBalanceAccountNotificationRequest(): configurationWebhooks.BalanceAccountNotificationRequest {
82-
return configurationWebhooks.ObjectSerializer.deserialize(this.payload, "BalanceAccountNotificationRequest");
93+
public getBalanceAccountBalanceNotificationRequest(): balanceWebhooks.BalanceAccountBalanceNotificationRequest {
94+
return balanceWebhooks.ObjectSerializer.deserialize(this.payload, "BalanceAccountBalanceNotificationRequest");
95+
}
96+
97+
public getAccountHolderNotificationRequest(): configurationWebhooks.AccountHolderNotificationRequest {
98+
return configurationWebhooks.ObjectSerializer.deserialize(this.payload, "AccountHolderNotificationRequest");
8399
}
84100

85101
public getCardOrderNotificationRequest(): configurationWebhooks.CardOrderNotificationRequest {
@@ -109,6 +125,10 @@ class BankingWebhookHandler {
109125
public getTransactionNotificationRequest(): transactionWebhooks.TransactionNotificationRequestV4 {
110126
return transactionWebhooks.ObjectSerializer.deserialize(this.payload, "TransactionNotificationRequestV4");
111127
}
128+
129+
public BalanceAccountBalanceNotificationRequest(): balanceWebhooks.BalanceAccountBalanceNotificationRequest {
130+
return balanceWebhooks.ObjectSerializer.deserialize(this.payload, "BalanceAccountBalanceNotificationRequest");
131+
}
112132
}
113133

114134
export default BankingWebhookHandler;
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+

0 commit comments

Comments
 (0)