Adyen payment received or refused notifications on your phone.

Get a push notification with the amount, method and your reference every time an Adyen payment is authorised, and a High-priority one when a payment is refused.

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 AUTHORISATION.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Adyen authorisation 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 AUTHORISATION 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. `AUTHORISATION` is one of the Standard webhook's default events, so there's nothing extra to select.
  6. Refused payments are pushed too, at High priority. To only hear about received payments, use `your webhook URL?refused=0` as the webhook URL instead.
  7. To see the shopper's email in the push, enable **Shopper email** under **Additional settings** on the webhook. Without it the push shows the amount, payment method and your merchant reference.
  8. To test, edit the webhook and select **Test configuration**, then choose `AUTHORISATION`. Adyen sends a handful of test events (successful and failed, in EUR and GBP), so expect a few pushes marked **(test mode)**. Adyen only needs a 2xx response; the old `[accepted]` response body isn't required.
  9. 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 → AUTHORISATION
2// Fires when a payment is authorised (success "true") or refused (success "false").
3// Docs: https://docs.adyen.com/api-explorer/Webhooks/1/post/AUTHORISATION
4//
5// Needs a Standard webhook (Customer Area > Developers > Webhooks) with Method set to JSON,
6// pointing at the integration's endpoint URL. Add ?refused=0 to skip refused payments.
7
8// Adyen amounts are in minor units, and Adyen sets the decimals per currency. Everything not
9// listed here has two. https://docs.adyen.com/development-resources/currency-codes
10const DECIMALS = {
11 CVE: 0, DJF: 0, GNF: 0, IDR: 0, JPY: 0, KMF: 0, KRW: 0, PYG: 0,
12 RWF: 0, UGX: 0, VND: 0, VUV: 0, XAF: 0, XOF: 0, XPF: 0,
13 BHD: 3, IQD: 3, JOD: 3, KWD: 3, LYD: 3, OMR: 3, TND: 3,
14}
15
16// Friendlier names for common Adyen payment method codes. Unknown codes are shown as sent.
17const METHODS = {
18 visa: "Visa",
19 mc: "Mastercard",
20 amex: "Amex",
21 maestro: "Maestro",
22 cup: "UnionPay",
23 jcb: "JCB",
24 diners: "Diners",
25 discover: "Discover",
26 cartebancaire: "Cartes Bancaires",
27 bcmc: "Bancontact",
28 ideal: "iDEAL",
29 paypal: "PayPal",
30 klarna: "Klarna",
31 applepay: "Apple Pay",
32 googlepay: "Google Pay",
33 paywithgoogle: "Google Pay",
34 sepadirectdebit: "SEPA Direct Debit",
35 ach: "ACH",
36 twint: "TWINT",
37 blik: "BLIK",
38 swish: "Swish",
39 mobilepay: "MobilePay",
40 vipps: "Vipps",
41 trustly: "Trustly",
42}
43
44// Own-property lookup, so keys like "toString" fall back instead of hitting Object.prototype.
45function pick(table, key, fallback) {
46 return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
47}
48
49// A trimmed string, or null. Adyen sometimes sends the literal string "null".
50function text(value) {
51 if (typeof value !== "string") return null
52 const trimmed = value.trim()
53 return trimmed && trimmed.toLowerCase() !== "null" ? trimmed : null
54}
55
56// { value: 4995, currency: "EUR" } → "€49.95". Returns null when either part is missing.
57function money(amount) {
58 const raw = amount?.value
59 const minor = typeof raw === "string" && /^-?\d+$/.test(raw) ? Number(raw) : raw
60 const code = text(amount?.currency)?.toUpperCase()
61
62 if (typeof minor !== "number" || !Number.isFinite(minor) || !code) return null
63
64 const value = minor / 10 ** pick(DECIMALS, code, 2)
65
66 try {
67 return new Intl.NumberFormat("en-GB", { style: "currency", currency: code }).format(value)
68 } catch {
69 return `${value} ${code}`
70 }
71}
72
73// The NotificationRequestItem objects in the body. JSON webhooks carry one, but it's an array.
74function notificationItems(body) {
75 const list = Array.isArray(body?.notificationItems) ? body.notificationItems : []
76 return list.map((wrapper) => wrapper?.NotificationRequestItem).filter((item) => item && typeof item === "object")
77}
78
79// "visa" → "Visa", "klarna_paynow" → "Klarna", unknown codes as sent.
80function methodName(code) {
81 const raw = text(code)
82 if (!raw) return null
83 const key = raw.toLowerCase()
84 return pick(METHODS, key, null) ?? pick(METHODS, key.split("_")[0], null) ?? raw
85}
86
87// Last four card digits: additionalData.cardSummary, or the reason of a successful card
88// authorisation, which Adyen formats as "authCode:last4:expiry" (e.g. "874574:1935:11/2012").
89function lastFour(item, success) {
90 const summary = text(item.additionalData?.cardSummary)
91 if (summary && /^\d{4}$/.test(summary)) return summary
92 const match = success ? /^[^:]*:(\d{4}):\d{1,2}\/\d{2,4}$/.exec(text(item.reason) ?? "") : null
93 return match ? match[1] : null
94}
95
96function handleRequest(request) {
97 const body = request.body && typeof request.body === "object" ? request.body : {}
98 const items = notificationItems(body)
99
100 // A custom test notification from Adyen; confirm the connection quietly.
101 const ping = items.find((item) => item.eventCode === "NOTIFICATIONTEST")
102 if (ping) {
103 return {
104 title: "🔔 Adyen connected",
105 message: `Webhook for ${text(ping.merchantAccountCode) ?? "your merchant account"} is working`,
106 topic: "Adyen",
107 priority: -1,
108 }
109 }
110
111 // Guard: only AUTHORISATION items, so other Adyen events on this integration stay quiet.
112 const matches = items.filter((item) => item.eventCode === "AUTHORISATION")
113 if (!matches.length) {
114 console.log(`Ignoring Adyen "${items[0]?.eventCode ?? "unknown"}" webhook`)
115 return null
116 }
117
118 const item = matches[0]
119 const outcome = String(item.success).toLowerCase()
120 if (outcome !== "true" && outcome !== "false") {
121 console.log(`Skipping AUTHORISATION with success "${item.success}"`)
122 return null
123 }
124
125 const success = outcome === "true"
126 // ?refused=0 on the webhook URL mutes refused payments.
127 const showRefused = !["0", "false", "no"].includes(String(request.query?.refused ?? "").toLowerCase())
128 if (!success && !showRefused) {
129 console.log("Skipping refused payment (?refused=0)")
130 return null
131 }
132
133 const method = methodName(item.paymentMethod)
134 const last4 = lastFour(item, success)
135 const who = text(item.additionalData?.shopperEmail)
136 const reason = success ? null : text(item.reason)
137 const ref = text(item.merchantReference)
138 const more = matches.length > 1 ? ` (+${matches.length - 1} more)` : ""
139 const test = body.live === "false" || body.live === false ? " (test mode)" : ""
140
141 const message =
142 (money(item.amount) ?? "A payment") +
143 (method ? ` via ${method}` : "") +
144 (last4 ? ` ••${last4}` : "") +
145 (who ? ` from ${who}` : "") +
146 (success ? "" : " refused") +
147 (reason && reason.toLowerCase() !== "refused" ? `: ${reason}` : "") +
148 (ref ? ` — ${ref}` : "") +
149 more +
150 test
151
152 return {
153 title: success ? "💰 Payment received" : "❌ Payment refused",
154 message,
155 topic: "Adyen",
156 // Received: Normal, money in is good news. Refused: High, a sale didn't go through.
157 priority: success ? 0 : 1,
158 ...(success ? { sound: "cashregister" } : {}),
159 }
160}
Payload

The Adyen AUTHORISATION webhook

This is what Adyen sends to your URL for authorisation. It is a sample, trimmed to the fields recipes use.

AUTHORISATION · sample.json
1{
2 "live": "true",
3 "notificationItems": [
4 {
5 "NotificationRequestItem": {
6 "amount": {
7 "value": 4995,
8 "currency": "EUR"
9 },
10 "reason": "874574:1935:03/2030",
11 "success": "true",
12 "eventCode": "AUTHORISATION",
13 "eventDate": "2026-09-28T11:29:48+02:00",
14 "operations": [
15 "CANCEL",
16 "CAPTURE",
17 "REFUND"
18 ],
19 "pspReference": "QFQTPCQ8HXSKGK82",
20 "paymentMethod": "visa",
21 "additionalData": {
22 "shopperEmail": "[email protected]",
23 "hmacSignature": "REDACTED_BASE64_SIGNATURE="
24 },
25 "merchantReference": "ORDER-12345",
26 "merchantAccountCode": "YourCompanyECOM"
27 }
28 }
29 ]
30}
FAQ

Adyen payment received or refused 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.