Adyen chargeback notifications on your phone.

Get a Highest-priority push notification when a shopper disputes an Adyen payment, with the reason and the defense deadline, plus pushes for requests for information, second chargebacks and reversals.

How it works

Three steps to your first push

STEP 01

Install the recipe

One click in Studio gives you a personal webhook URL.

STEP 02

Paste your URL into Adyen

Add it as a webhook for the dispute events (NOTIFICATION_OF_CHARGEBACK, CHARGEBACK, REQUEST_FOR_INFORMATION, SECOND_CHARGEBACK, CHARGEBACK_REVERSED).

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Adyen the dispute events (notification_of_chargeback, chargeback, request_for_information, second_chargeback, chargeback_reversed) webhook

  1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
  2. In your Adyen Customer Area, go to Developers > Webhooks, select Create new webhook and add a Standard webhook.
  3. Paste your webhook URL as the URL and set Method to JSON.https://••••••••/•••••••• your personal URL, shown after install
  4. Under Events settings, check that the dispute events (NOTIFICATION_OF_CHARGEBACK, CHARGEBACK, REQUEST_FOR_INFORMATION, SECOND_CHARGEBACK, CHARGEBACK_REVERSED) is selected, then select Save configuration.
  5. Use a **Standard** webhook and set **Method** to **JSON**. The HTTP POST and SOAP methods send the data in a different format that this recipe can't read. Under **Events settings**, make sure all the dispute events are selected.
  6. The recipe pushes for `NOTIFICATION_OF_CHARGEBACK` (a dispute was opened), `CHARGEBACK` (the money was deducted), `REQUEST_FOR_INFORMATION`, `SECOND_CHARGEBACK` and `CHARGEBACK_REVERSED`. One dispute usually sends a `NOTIFICATION_OF_CHARGEBACK` first and a `CHARGEBACK` later, so you get a push at each stage. Disputes Adyen defends automatically come in at Normal priority. Other dispute events, such as `NOTIFICATION_OF_FRAUD` or pre-arbitration, are ignored.
  7. Adyen's **Test configuration** button can only send `CHARGEBACK_REVERSED` of these events. To see a real dispute push, make a test card payment with a special `holderName` (see Adyen's "Test a chargeback scenario" guide) and capture it; those pushes are marked **(test mode)**. Adyen only needs a 2xx response; the old `[accepted]` response body isn't required.
  8. Adyen signs each event with an HMAC signature in `additionalData.hmacSignature`, but JustPush doesn't check it yet, so keep your endpoint URL private. You can leave the HMAC key and basic authentication settings empty.
The code

What runs when the webhook arrives

Studio calls handleRequest(request) with the incoming webhook and sends the message it returns. It's yours after install; change anything.

transform.js
1// Adyen → dispute events (NOTIFICATION_OF_CHARGEBACK, CHARGEBACK, REQUEST_FOR_INFORMATION,
2// SECOND_CHARGEBACK, CHARGEBACK_REVERSED)
3// Fires when a shopper's bank opens a dispute, asks for information, charges a payment back
4// (again), or when a defended chargeback is returned to you.
5// Docs: https://docs.adyen.com/risk-management/disputes-api/dispute-notifications/
6//
7// Needs a Standard webhook (Customer Area > Developers > Webhooks) with Method set to JSON and
8// the dispute events selected, pointing at the integration's endpoint URL.
9
10// Adyen amounts are in minor units, and Adyen sets the decimals per currency. Everything not
11// listed here has two. https://docs.adyen.com/development-resources/currency-codes
12const DECIMALS = {
13 CVE: 0, DJF: 0, GNF: 0, IDR: 0, JPY: 0, KMF: 0, KRW: 0, PYG: 0,
14 RWF: 0, UGX: 0, VND: 0, VUV: 0, XAF: 0, XOF: 0, XPF: 0,
15 BHD: 3, IQD: 3, JOD: 3, KWD: 3, LYD: 3, OMR: 3, TND: 3,
16}
17
18// Each dispute event, how to word it, and whether it opens a defense window.
19const EVENTS = {
20 NOTIFICATION_OF_CHARGEBACK: { title: "⚖️ Chargeback incoming", verb: "disputed", defend: true },
21 CHARGEBACK: { title: "⚖️ Chargeback", verb: "charged back", defend: true },
22 REQUEST_FOR_INFORMATION: { title: "📄 Request for information", verb: "queried by the shopper's bank", priority: 1 },
23 SECOND_CHARGEBACK: { title: "⚖️ Second chargeback", verb: "charged back again", priority: 1 },
24 CHARGEBACK_REVERSED: { title: "✅ Chargeback reversed", verb: "returned to your account", priority: -1 },
25}
26
27// Own-property lookup, so keys like "toString" fall back instead of hitting Object.prototype.
28function pick(table, key, fallback) {
29 return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
30}
31
32// A trimmed string, or null. Adyen sometimes sends the literal string "null".
33function text(value) {
34 if (typeof value !== "string") return null
35 const trimmed = value.trim()
36 return trimmed && trimmed.toLowerCase() !== "null" ? trimmed : null
37}
38
39// { value: 4995, currency: "EUR" } → "€49.95". Returns null when either part is missing.
40function money(amount) {
41 const raw = amount?.value
42 const minor = typeof raw === "string" && /^-?\d+$/.test(raw) ? Number(raw) : raw
43 const code = text(amount?.currency)?.toUpperCase()
44
45 if (typeof minor !== "number" || !Number.isFinite(minor) || !code) return null
46
47 const value = minor / 10 ** pick(DECIMALS, code, 2)
48
49 try {
50 return new Intl.NumberFormat("en-GB", { style: "currency", currency: code }).format(value)
51 } catch {
52 return `${value} ${code}`
53 }
54}
55
56// The NotificationRequestItem objects in the body. JSON webhooks carry one, but it's an array.
57function notificationItems(body) {
58 const list = Array.isArray(body?.notificationItems) ? body.notificationItems : []
59 return list.map((wrapper) => wrapper?.NotificationRequestItem).filter((item) => item && typeof item === "object")
60}
61
62// "2026-10-14T16:05:30+02:00" → "14 Oct". Returns null for anything unparseable.
63function day(iso) {
64 const date = new Date(text(iso) ?? "")
65 if (Number.isNaN(date.getTime())) return null
66 return date.toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" })
67}
68
69function handleRequest(request) {
70 const body = request.body && typeof request.body === "object" ? request.body : {}
71 const items = notificationItems(body)
72
73 // A custom test notification from Adyen; confirm the connection quietly.
74 const ping = items.find((item) => item.eventCode === "NOTIFICATIONTEST")
75 if (ping) {
76 return {
77 title: "🔔 Adyen connected",
78 message: `Webhook for ${text(ping.merchantAccountCode) ?? "your merchant account"} is working`,
79 topic: "Adyen",
80 priority: -1,
81 }
82 }
83
84 // Guard: only the dispute events above, so other Adyen events on this integration stay quiet.
85 const matches = items.filter((item) => pick(EVENTS, item.eventCode, null))
86 if (!matches.length) {
87 console.log(`Ignoring Adyen "${items[0]?.eventCode ?? "unknown"}" webhook`)
88 return null
89 }
90
91 const item = matches[0]
92 const event = EVENTS[item.eventCode]
93 const extra = item.additionalData && typeof item.additionalData === "object" ? item.additionalData : {}
94 const amount = money(item.amount)
95 const reason = text(item.reason)
96 const ref = text(item.merchantReference)
97 const autoDefended = String(extra.autoDefended).toLowerCase() === "true"
98 const undefendable = String(extra.defendable).toLowerCase() === "false"
99 const dueBy = day(extra.defensePeriodEndsAt)
100 const more = matches.length > 1 ? ` (+${matches.length - 1} more)` : ""
101 const test = body.live === "false" || body.live === false ? " (test mode)" : ""
102
103 let next = ""
104 let priority = event.priority
105 if (event.defend) {
106 if (autoDefended) {
107 next = ". Adyen defended it automatically."
108 priority = 0 // Normal — nothing to do unless you want to add evidence
109 } else if (undefendable) {
110 next = ". It can't be defended."
111 priority = 1 // High — money is (or will be) gone, but there's nothing to submit
112 } else {
113 next = dueBy ? `. Respond by ${dueBy}.` : ". Respond in your Customer Area."
114 priority = 2 // Highest — the defense clock is ticking
115 }
116 } else if (item.eventCode === "REQUEST_FOR_INFORMATION" && dueBy) {
117 // High — answering the RFI can prevent a chargeback.
118 next = `. Respond by ${dueBy}.`
119 }
120 // SECOND_CHARGEBACK is High: the defense was rejected and the money is gone again.
121 // CHARGEBACK_REVERSED is Low: good news, the funds are back (though not always final).
122
123 return {
124 title: event.title,
125 message:
126 (amount ? `${amount} ${event.verb}` : `A payment was ${event.verb}`) +
127 (reason ? ` — ${reason}` : "") +
128 (ref ? ` (${ref})` : "") +
129 more +
130 test +
131 next,
132 topic: "Adyen",
133 priority,
134 }
135}
Payload

The Adyen CHARGEBACK webhook

This is what Adyen sends to your URL for the dispute events (notification_of_chargeback, chargeback, request_for_information, second_chargeback, chargeback_reversed). It is a sample, trimmed to the fields recipes use.

CHARGEBACK · sample.json
1{
2 "live": "true",
3 "notificationItems": [
4 {
5 "NotificationRequestItem": {
6 "amount": {
7 "value": 4995,
8 "currency": "EUR"
9 },
10 "reason": "Other Fraud-Card Absent Environment",
11 "success": "true",
12 "eventCode": "NOTIFICATION_OF_CHARGEBACK",
13 "eventDate": "2026-09-29T16:05:30+02:00",
14 "pspReference": "D7XK2M4PQ9RT5W82",
15 "paymentMethod": "visa",
16 "additionalData": {
17 "defendable": "true",
18 "autoDefended": "false",
19 "disputeStatus": "Undefended",
20 "hmacSignature": "REDACTED_BASE64_SIGNATURE=",
21 "defensePeriodEndsAt": "2026-10-14T16:05:30+02:00",
22 "chargebackReasonCode": "10.4",
23 "chargebackSchemeCode": "visa",
24 "modificationMerchantReferences": ""
25 },
26 "merchantReference": "ORDER-12345",
27 "originalReference": "QFQTPCQ8HXSKGK82",
28 "merchantAccountCode": "YourCompanyECOM"
29 }
30 }
31 ]
32}
FAQ

Adyen chargeback notifications: questions

Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into Adyen and you are done. The code is there if you want to change the text, the sound or the buttons.

Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.

Ready when you are

Adyen on your phone in two minutes.

Install the recipe, paste one URL, done. Free for 30 days, no credit card required.