diff --git a/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_1.png b/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_1.png new file mode 100644 index 000000000..ae37a8665 Binary files /dev/null and b/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_1.png differ diff --git a/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_2.png b/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_2.png new file mode 100644 index 000000000..c7178098e Binary files /dev/null and b/docs/developer/app-store/apps/adyen/adyen_webhook_settings_delayed_capture_2.png differ diff --git a/docs/developer/app-store/apps/adyen/architecture.mdx b/docs/developer/app-store/apps/adyen/architecture.mdx index 497ed2eb2..1161e3b3f 100644 --- a/docs/developer/app-store/apps/adyen/architecture.mdx +++ b/docs/developer/app-store/apps/adyen/architecture.mdx @@ -39,18 +39,24 @@ The app restricts Saleor API response time to 5 seconds for [`TransactionInitial If such timeout happens, the created [`TransactionItem`](api-reference/payments/objects/transaction-item.mdx) will not have the metadata from Adyen on `additionalDetails` object, which includes payment method type, credit card brand, etc. -### Granted refunds vs manual refunds +### Refund with line items vs refund with manual amount -There are differences in how refunds are handled depending on wether an [`OrderGrantedRefund`](/api-reference/orders/objects/order-granted-refund.mdx) was created in Saleor and a refund via [`transactionRequestRefundForGrantedRefund`](api-reference/payments/mutations/transaction-request-refund-for-granted-refund.mdx) mutation was requested, or a "manual refund" sent via [`transactionRequestAction`](/api-reference/payments/mutations/transaction-request-action.mdx) mutation was requested. The differences is in how [`lineItems`](https://docs.adyen.com/api-explorer/Checkout/71/post/payments/_paymentPspReference_/refunds#request-lineItems.mdx) are reported to Adyen +:::note +In Saleor Dashboard you can choose between two types of refunds that use different Saleor APIs: +- **"Refund with line items"** - creates [OrderGrantedRefund](/api-reference/orders/objects/order-granted-refund.mdx) and sends a refund to Adyen App using [`transactionRequestRefundForGrantedRefund`](/api-reference/payments/mutations/transaction-request-refund-for-granted-refund.mdx) +- **"Refund with manual amount"** - sends refund request to Adyen App using [`transactionRequestAction`](api-reference/payments/mutations/transaction-request-action.mdx) +::: -#### Manual refund +There are differences in how refunds are handled depending on whether a [`OrderGrantedRefund`](/api-reference/orders/objects/order-granted-refund.mdx) was created in Saleor and a refund via [`transactionRequestRefundForGrantedRefund`](api-reference/payments/mutations/transaction-request-refund-for-granted-refund.mdx) mutation was requested, or a "manual refund" sent via [`transactionRequestAction`](/api-reference/payments/mutations/transaction-request-action.mdx) mutation was requested. The differences are in how [`lineItems`](https://docs.adyen.com/api-explorer/Checkout/71/post/payments/_paymentPspReference_/refunds#request-lineItems.mdx) are reported to Adyen -A manual refund is initiated using the [`transactionRequestAction`](/api-reference/payments/mutations/transaction-request-action.mdx) mutation. +#### Refund with manual amount -When manual refund is requested, the app will send `lineItems` to Adyen if the `amount` is equal to or greater than Order's total gross amount. -In other cases app will not send `lineItems` to Adyen. +This refund is initiated using the [`transactionRequestAction`](/api-reference/payments/mutations/transaction-request-action.mdx) mutation. -#### Granted refund +When a manual refund is requested, the app will send `lineItems` to Adyen if the `amount` is equal to or greater than the Order's total gross amount. +In other cases' app will not send `lineItems` to Adyen. + +#### Refund with line items -A granted refund is initiated using the [`transactionRequestRefundForGrantedRefund`](/api-reference/payments/mutations/transaction-request-refund-for-granted-refund.mdx) mutation. +This refund is initiated using the [`transactionRequestRefundForGrantedRefund`](/api-reference/payments/mutations/transaction-request-refund-for-granted-refund.mdx) mutation. + +When refund for granted refund is requested, app will map `grantedRefund.lines.orderLines` to `lineItems` reported to Adyen. Additionally, if `grantedRefund.shippingCostIncluded` is set to true, the app will include a shipping line in the `lineItems` sent to Adyen. + +## TransactionItem metadata + +App sets metadata on each `TransactionItem` it creates in Saleor. Metadata is set when app receives asynchronous confirmation from Adyen via webhook. + +### Configuring fields included in metadata + +Adyen app sets fields on metadata from `NotificationItem.additionalData` object it receives from Adyen. To configure which fields are included by Adyen go to `Developres > Additional data` and select your preferred fields. + +:::caution +Remember to always include the following fields for proper notification handling: +- `Authorization amount` +- `Authorisation amount (dynamic zero authorisation)` +- `Recurring details` +- `Merchant reference` +::: + +For example, if you want to have data about used payment method, and it's variant choose: +- `Subvariant` +- `Variant` +- `Co-brand` +- `Expiry date` +- `Card summary` + + +After choosing these settings, you will have the following fields set on your `TransactionItem` metadata: + +```json +{ + "cardSummary": "1111", + "expiryDate": "8/2018", + "paymentMethodVariant": "visadebit", + "paymentMethod": "visa", + "coBrandedWith": "visa", + "authorisedAmountCurrency": "EUR", + "authorisedAmountValue": "1000", + "recurring.shopperReference": "testshopper", + "recurring.recurringDetailReference": "1111111111111111", + "merchantReference": "ref_1234" +} +``` + +### Subscribing to metadata changes + +The best way to check metadata updates on TransactionItem is to subscribe to [`TRANSACTION_ITEM_METADATA_UPDATED`](/api-reference/webhooks/enums/webhook-event-type-async-enum.mdx#webhookeventtypeasyncenumtransaction_item_metadata_updated) async webhook event. This way you can be sure that metadata is up-to-date in your external system (e.g. if you use metadata to store which payment method was used for tax purposes). + +If you include `metafields` or `metadata` from `TransactionItem` in any other subscription, it might not be set at all, or be out-of-date. + +:::note +For example, if you use `ORDER_CREATED` webhook to check `TransactionItem.metadata` then if your customer has completed checkout very quickly, metadata might not yet be set on `TransactionItem`. This means it won't be included in a notification from Saleor. +::: + +#### Example + +Here's an example subscription query that listens to metadata changes + +```graphql +subscription { + event { + ... on TransactionItemMetadataUpdated { + __typename + issuingPrincipal { + ... on App { + identifier + } + } + transaction { + id + pspReference + order { + id + } + metafields + } + } + } +} +``` + +## Capture delay + +All payment methods that have "separate capture" feature available (to check availability, refer to [Adyen docs](https://docs.adyen.com/payment-methods/) and look for methods with "Separate captures" listed in the features) are impacted by capture delay setting in merchant account. + +Capture is a transfer of previously locked funds to the merchant. In Adyen, you can either: +- capture funds immediately after authorization: `captureDelay: immediate` +- delay it for a certain period of time: `captureDelay: x hours` +- disable automatic capture and always do it manually: `captureDelay: manual` + +:::tip +Remember that capture can expire after a certain period of time if it is not extended. This depends on used payment method. For example, most credit cards authorizations expire after 28 days. + +To see expiration time for specific payment methods, [see this documentation page in Adyen docs](https://docs.adyen.com/online-payments/adjust-authorisation/#validity) + +Expired payments can be re-authorized, although this comes with some risk. [See Adyen docs for more information](https://docs.adyen.com/account/manage-payments/#reauthorize-a-payment) +::: -When granted refund is requested, app will map `grantedRefund.lines.orderLines` to `lineItems` reported to Adyen. Additionally, if `grantedRefund.shippingCostIncluded` is set to true, the app will include a shipping line in the lineItems sent to Adyen. +The delay can be set in the Adyen dashboard in the merchant account settings. + +### TransactionFlowStrategy + +Saleor channel settings include a [`paymentSettings.defaultTransactionFlowStrategy`](/api-reference/payments/inputs/payment-settings-input.mdx) setting. Here's how it impacts payments done via Adyen App: + +- **`TransactionFlowStrategyEnum.CHARGE`** - Behavior of merchant account capture delay settings will be used, no change is made by app: + - If capture delay is set to **immediate**, capture will be created, there won't be authorization event in Transaction history + - If capture delay is set to **x hours**, first authorization will be made, then a Transaction will be automatically captured after x hours + - If capture delay is set to **manual**, authorization will be done instead of a charge, which must be captured either in Saleor Dashboard or Adyen Dashboard +- **`TransactionFlowStrategyEnum.AUTHORIZE`** - App will use [`manualCapture: true`](https://docs.adyen.com/online-payments/capture/?tab=1#enable-manual-capture) field when making payments, it behaves the same way as if `captureDelay: manual` was set in Adyen merchant account settings + - If capture delay is set to **immediate**, behavior is changed, this setting will be ignored and authorization will be done instead + - If capture delay is set to **x hours**, behavior is changed, this setting will be ignored and authorization will be done instead + - If capture delay is set to **manual**, there is no change in behavior, authorization will be created + +:::note +If payment method doesn't support "separate capture" feature, it will always be charged, no matter any setting in Adyen or Saleor. +::: diff --git a/docs/developer/app-store/apps/adyen/configuration.mdx b/docs/developer/app-store/apps/adyen/configuration.mdx index 265283e42..3093a6d5c 100644 --- a/docs/developer/app-store/apps/adyen/configuration.mdx +++ b/docs/developer/app-store/apps/adyen/configuration.mdx @@ -5,7 +5,7 @@ title: Configuration import Video from "@site/components/Video"; -For Adyen to appear as available payment gateway, you need to [install it in the Saleor Dashboard](developer/app-store/overview.mdx#usage). You must obtain the API key from Adyen and paste it into the Adyen App configuration form. Then, a wizard will guide you through the process of configuring the Adyen App, setting up the webhook to receive notifications from Adyen, generating the HMAC key, and adding allowed origins for the Client Key that's used on your Storefront. +For Adyen to appear as an available payment gateway, you need to [install it in the Saleor Dashboard](developer/app-store/overview.mdx#usage). You must obtain the API key from Adyen and paste it into the Adyen App configuration form. Then, a wizard will guide you through the process of configuring the Adyen App, setting up the webhook to receive notifications from Adyen, generating the HMAC key, and adding allowed origins for the Client Key that's used on your Storefront. ## Configuring Adyen @@ -17,7 +17,7 @@ You can install the Adyen app directly from your Saleor Dashboard. Go to the **A ### Creating new API Credentials -To create new Adyen API credentials head over to the Adyen dashboard, then **Developers -> API credentials**. Click **Create new credential** and choose **Web service user**. +To create new Adyen API credentials, head over to the Adyen dashboard, then **Developers -> API credentials**. Click **Create new credential** and choose **Web service user**. In **Server settings -> Authentication** section generate a new API key. @@ -31,30 +31,71 @@ The configuration will not work until you save API credentials in the Adyen dash Save API credentials in the Adyen dashboard, head over to the Adyen app configuration, and click **Save**. The rest of the form fields have been enabled. -In **Client key** field provide key from **Client settings -> Authentication** section. Select the merchant account you want to use from the dropdown. +In the **Client key** field, provide a key from the **Client settings -> Authentication** section. Select the merchant account you want to use from the drop-down. -### Webook configuration +### Webhook configuration -In Adyen dashboard head over to **Developers -> Webhooks**. Click the **+ Webhook** button and choose **Standard webhook**. Provide a description for the webhook. +:::caution +Before you begin configuration, make sure that you have disabled the **Delayed Capture** option in the Adyen Dashboard. Read more details on how to do this on the [Overview docs page](./overview.mdx) +::: + +In the Adyen dashboard, first go to **Developers -> Additional data**. Click on the following checkboxes: +- `Authorization amount` +- `Authorisation amount (dynamic zero authorisation)` +- `Recurring details` +- `Merchant reference` + +Your "Response preview" should look like this: + +```json +{ + "additionalData": { + "authorisedAmountCurrency": "EUR", + "authorisedAmountValue": "1000", + "recurring.shopperReference": "testshopper", + "recurring.recurringDetailReference": "1111111111111111", + "merchantReference": "ref_1234" + } +} +``` + +Click the "Save configuration" button to save your settings. + +:::tip +Adyen webhook will set `TransactionItem` metadata based on fields received in `NotifiactionItem.additionalData`. + +To configure additional fields that will be set when users make payments, go to `Developers > Additional data` and choose fields you want to have in your metadata. Read more about this on the [Architecture docs page](./architecture.mdx#transactionitem-metadata) +::: + +Then head over to **Developers -> Webhooks**. Click the **+ Webhook** button and choose **Standard webhook**. Provide a description for the webhook. -In the **Server configuration** section provide URL the Saleor Adyen app has generated for you. You can copy it from the **Webhook URL** input in the Adyen configuration form. Leave default settings and click **Apply**. +In the **Server configuration** section, provide the URL the Saleor Adyen app has generated for you. You can copy it from the **Webhook URL** input in the Adyen configuration form. Leave the default settings and click **Apply**. In **Merchant accounts** select **Include only specific merchant accounts** and choose the merchant account you would like to use. -In **Events**, apart from events selected by default, select `EXPIRE`. Deselect `ORDER_OPENED` event. +In **Events**, apart from events selected by default, select `EXPIRE`. Deselect the `ORDER_OPENED` event. -In **Additional settings -> Risk** select `Include the originalReference for CHARGEBACK_REVERSED events`. +In **Additional settings -> Risk** select: +- `Include the originalReference for CHARGEBACK_REVERSED events` +- `Include a success boolean for the payments listed in an ORDER_CLOSED event` +- `Include Authorised Amount (dynamic zero auth)` +- `Include Capture Delay Hours` +- `Include card info for recurring contract events)` +- `Include zero auth flag` +- `Include Shopper Interaction` +- `Include Mandate Details` +- `Add capture reference to dispute event` -In **Security -> Basic authentication** provide username and password. Apply changes. Use the same username and password in the **webhook username** and **webhook password** fields in the Adyen application. +In **Security -> Basic authentication** provide a username and a password. Apply changes. Use the same username and password in the **webhook username** and **webhook password** fields in the Adyen application. Generate a new **HMAC key** in the **Security -> HMAC Key** section and copy it. -Apply the configuration details in the Saleor Adyen app then in Adyen dashboard save the new Adyen webhook. +Apply the configuration details in the Saleor Adyen app, then in the Adyen dashboard save the new Adyen webhook. Save the configuration. If everything went well, you should see a new button **Make test webhook call**. Use it to test if your configuration is correct. :::caution -API Key provided inside the configuration must have the following permissions set in Adyen Dashboard: +The API Key provided inside the configuration must have the following permissions set in the Adyen Dashboard: - "Management API - Accounts read and write" - "Management API - API credentials read and write" diff --git a/docs/developer/app-store/apps/adyen/overview.mdx b/docs/developer/app-store/apps/adyen/overview.mdx index 98c2b1334..e851d9fcb 100644 --- a/docs/developer/app-store/apps/adyen/overview.mdx +++ b/docs/developer/app-store/apps/adyen/overview.mdx @@ -38,15 +38,55 @@ Adyen App uses Adyen [Checkout API v70](https://docs.adyen.com/api-explorer/#/Ch If you want to self-host the Adyen app, reach out to [our team ](mailto:hello@saleor.io). +### Adyen notifications + +:::caution +If notification event is not listed below it will be **ignored** by the app. +::: + +:::info +Notifications for payments made without using Adyen app will be ignored. This happens because they do not have required `metadata` ([see more details](#receiving-notifications-for-transactions-not-started-in-adyen-app)) +::: + +App supports following notification events from Adyen: + + - [`Authorisation`](https://docs.adyen.com/api-explorer/Webhooks/1/post/AUTHORISATION) + - [`AuthorisationAdjustment`](https://docs.adyen.com/api-explorer/Webhooks/1/post/AUTHORISATION_ADJUSTMENT) + - [`Cancellation`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CANCELLATION) + - [`CancelOrRefund`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CANCEL_OR_REFUND) + - [`Capture`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CAPTURE) + - [`CaptureFailed`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CAPTURE_FAILED) + - [`Expire`](https://docs.adyen.com/api-explorer/Webhooks/1/post/EXPIRE) + - [`OrderClosed`](https://docs.adyen.com/api-explorer/Webhooks/1/post/ORDER_CLOSED) + - [`Refund`](https://docs.adyen.com/api-explorer/Webhooks/1/post/REFUND) + - [`RefundFailed`](https://docs.adyen.com/api-explorer/Webhooks/1/post/REFUND_FAILED) + - [`RefundedReversed`](https://docs.adyen.com/api-explorer/Webhooks/1/post/REFUNDED_REVERSED) + - [`RefundWithData`](https://docs.adyen.com/api-explorer/Webhooks/1/post/REFUND_WITH_DATA) + - [`VoidPendingRefund`](https://docs.adyen.com/api-explorer/Webhooks/1/post/VOID_PENDING_REFUND) + - [`Chargeback`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CHARGEBACK) + - [`SecondChargeback`](https://docs.adyen.com/api-explorer/Webhooks/1/post/SECOND_CHARGEBACK) + - [`ChargebackReversed`](https://docs.adyen.com/api-explorer/Webhooks/1/post/CHARGEBACK_REVERSED) + +Following events will be mapped to `INFO` event in Saleor, it will not change amounts on `TransactionItem`: + - `HandledExternally` + - [`PostponedRefund`](https://docs.adyen.com/api-explorer/Webhooks/1/post/POSTPONED_REFUND) + - [`NotificationOfFraud`](https://docs.adyen.com/api-explorer/Webhooks/1/post/NOTIFICATION_OF_FRAUD) + - [`NotificationOfChargeback`](https://docs.adyen.com/api-explorer/Webhooks/1/post/NOTIFICATION_OF_CHARGEBACK) + - [`PrearbitrationLost`](https://docs.adyen.com/api-explorer/Webhooks/1/post/PREARBITRATION_LOST) + - [`PrearbitrationWon`](https://docs.adyen.com/api-explorer/Webhooks/1/post/PREARBITRATION_WON) + - [`RequestForInformation`](https://docs.adyen.com/api-explorer/Webhooks/1/post/REQUEST_FOR_INFORMATION) + - [`ManualReviewAccept`](https://docs.adyen.com/api-explorer/Webhooks/1/post/MANUAL_REVIEW_ACCEPT) + - [`ManualReviewReject`](https://docs.adyen.com/api-explorer/Webhooks/1/post/MANUAL_REVIEW_REJECT) + ## Assumptions - If Adyen doesn't respond to app request for [initialize transaction session](/developer/extending/webhooks/synchronous-events/transaction#initialize-transaction-session) or [process transaction session](/developer/extending/webhooks/synchronous-events/transaction#process-transaction-session) with `pspReference`: - App will return `CHARGE_ACTION_REQUIRED` or `AUTHORIZATION_ACTION_REQUIRED` as the transaction result. This is because Saleor doesn't require `pspReference` in app response for these transaction results. - - App can also return this status if an action is required to be performed on the storefront, this is incicated by an `action` object being included in the Adyen response. + - App can also return this status if an action is required to be performed on the storefront, this is indicated by an `action` object being included in the Adyen response. - App might not return `pspReference` for: - [Transaction refund webhook](/developer/extending/webhooks/synchronous-events/transaction#transaction-refund) with transaction status of `REFUND_FAILURE` - [Transaction charge webhook](/developer/extending/webhooks/synchronous-events/transaction#transaction-charge) with transaction status of `CHARGE_FAILURE` - - [Transaction cancelation webhook](/developer/extending/webhooks/synchronous-events/transaction#transaction-cancelation) with transaction status of `CANCEL_FAILURE` + - [Transaction cancellation webhook](/developer/extending/webhooks/synchronous-events/transaction#transaction-cancelation) with transaction status of `CANCEL_FAILURE` - [Initialize transaction session webhook](/developer/extending/webhooks/synchronous-events/transaction#initialize-transaction-session) with transaction status of: - `CHARGE_ACTION_REQUIRED` - `AUTHORIZATION_ACTION_REQUIRED` @@ -57,3 +97,116 @@ If you want to self-host the Adyen app, reach out to [our team - `AUTHORIZATION_ACTION_REQUIRED` - `CHARGE_FAILURE` - `AUTHORIZATION_FAILURE` +- [Webhook settings > Delayed Capture](#duplicated-chargedamount-in-saleor) is disabled in Adyen Dashboard +- Webhook in Adyen was created with all options (additional settings) described in [Saleor docs](./configuration.mdx#webhook-configuration) +- Before [user cancels a payment](./storefront.mdx#onordercancel), App will update `TransactionItem` in Saleor, so that it has either `REFUND_REQUEST` or `CANCEL_REQUEST` event. If update in Saleor fails, user won't be able to cancel payment. This prevents fraudulent orders from being created. + +## Limitations & troubleshooting + +### Changing channel to configuration mapping in production + +App stores its configuration assigned to specific Saleor channel. + +Each payment in Adyen stores `channelId` in its metadata, in order to get proper configuration from the app once app receives notification from Adyen. + +If you change the channel to configuration mapping, all enqueued notifications (pending notifications from Adyen) that were related such channel will be not handled properly by the app, causing the request to fail. + +Before you change the configuration mapping, make sure that you do one of the following (listed in order of the least intrusive changes): +- **Create new channel in Saleor** and create a new config so that you don't change mappings at all. This way you can gradually accept new orders on new merchant account and keep handling changes related to orders created on old merchant account. +- **Use the same webhook settings** for the new configuration (HMAC key, username, password). + - You can re-use old HMAC key in Adyen webhook, by choosing it from the list while creating the webhook. + - Keep in mind that if you change merchant account app will not be able to request changes on existing Transactions, but it will report changes made in Adyen Dashboard. To do that webhook needs to have selected previous merchant account in ["include specific merchant account" setting](https://docs.adyen.com/development-resources/webhooks/#set-up-webhooks-in-your-customer-area). + - If you want to use new HMAC key, remember to first create new configuration in app with the old HMAC key, and then change it in both new and old configuration, then you can save changes in Adyen. This way no notification will be rejected by app due to invalid HMAC signature +- **Not recommended**: Make sure you **don't have any pending notifications** in the Adyen queue. You can achieve that by disabling payments for your merchant account for a while and then migrating it. + - Keep in mind that if you request _any_ action on already created Transactions (refund, charge, cancel), they will fail. + - Any payment modifications requested in Adyen Dashboard will also not be reflected in Saleor. + +### Rejected Adyen webhook + +Adyen webhook notifications might get rejected by Adyen app for various reasons. You can investigate them by checking response returned by app in Adyen webhook logs. + +Here are some common reasons for rejected Adyen webhook + +#### Invalid HMAC signature or username or password + +Adyen's notifications content is signed using the HMAC key generated at the time of webhook configuration (to learn more about configuration see [Configuration docs page](./configuration.mdx)). + +App stores each HMAC key in its configuration that's based on `channelId` which in turn is stored in Adyen payment metadata. + +``` +Adyen payment -> Notification item -> Metadata channelId -> Channel x configuration mapping in app settings -> Configuration -> HMAC key +``` + +When signature is invalid, it can mean either that: +- You've changed HMAC key in your Adyen webhook settings, but didn't update it in app settings +- You've changed HMAC key in your app configuration, but HMAC wasn't changed on Adyen side +- You've changed a channel to configuration mapping in app, but new configuration has different HMAC key configured + +Same principle applies to webhook's `username` and `password`. + +If you detect such issue, we recommend you to check current configuration if it matches your expectations (accidental mapping change). If so then you can rotate HMAC key by going to Adyen settings, generating new HMAC key and updating it in Adyen app. + +Once you save changes, app will now store two (or more) HMAC keys which will be used to check incoming notifications. + +Note that notifications which were already queued by Adyen, will still be rejected by app, as they were signed using old HMAC key. You'll need to update Transaction status manually in Saleor and ignore these notifications on Adyen side. + +If number of enqueued webhooks is too high for manual ignore, you can also create brand-new webhook, and new configuration in Adyen app. See [Configuration docs page](./configuration.mdx) to see how this can be done. + +#### Missing configuration for channel + +As described in [Invalid HMAC signature section](#invalid-hmac-signature) notifications are dependent on your channel to configuration mapping. If you do one of the following: +- delete configuration that was assigned to a channel +- unset channel to configuration mapping + +Any notification for Transactions created in that channel will be rejected by app. + +To fix this, assign correct channel to configuration mapping. + +Keep in mind that if you decide to create new configuration and use a new HMAC key, previously enqueued notifications in Adyen will still be rejected, as they were signed using old HMAC key. + +#### Receiving notifications for transactions not started in Adyen app + +If you use the same merchant account in Adyen for multiple purposes (e.g. multiple Saleor instances, subscription payments done by other app, in store POS payments) Adyen app might reject these notifications, which will impact delivery of other notifications that were meant for Adyen app. + +In order to prevent this from happening you should: +- Treat merchant account used for Adyen app payments as dedicated only to the app, meaning if you want to process other payments, you will create new merchant accounts for any other use case +- **Only if above cannot be done**: make sure that you do not include any metadata in Adyen payment that could be used to identify it as a Saleor payment. This way Adyen app will not be able to identify it as a Saleor payment and will not reject it. Here are some common fields that are used to identify Saleor payment: + - `metadata.channelId` + - `metadata.transactionId` + +#### Changed `saleorApiUrl` + +If you change your Saleor instance domain, then you should also update webhook URL in Adyen app. To do that go to Adyen app settings, copy new webhook URL and update it in Adyen dashboard. + +### Flooded Adyen webhook queue + +Adyen uses a queue to deliver notifications to the app. The more unexpected error occur (app returning non-200 to Adyen), the more events will be marked as "error" in the Adyen queue. Adyen will keep retrying notifications delivery up to 30 days. + +After some arbitrary limit set by Adyen is reached for specific webhook events, there is a risk that Adyen will stop sending notifications until error is fixed or notification dropped manually in Adyen Dashboard. + +This situation is abnormal, but to ensure smooth process, you should regularly monitor Adyen's webhook queue and drop broken events if needed + +In order to drop notifications (e.g. due to a change in channel to configuration mapping in App) refer to [Adyen Docs](https://docs.adyen.com/development-resources/webhooks/troubleshoot/#ignore-notification). Keep in mind that if notification is dropped its current status won't be reflected in Saleor. You need to drop each broken notification separately by refreshing page in Adyen Dashboard. + +### Duplicated `chargedAmount` in Saleor + +If you have `chargedAmount` that's duplicated due to two `CHARGE_SUCCESS` event being created in Transaction history, make sure that you've disabled "Delayed capture" webhook setting in Adyen Dashboard. + +:::info +This setting causes app to receive two webhooks for each payment that uses `CHARGE` flow (capture delay in merchant account settings is set to "immediate" or "x days" and [`channel.paymentSettings.defaultTransactionFlowStrategy`](/api-reference/payments/inputs/payment-settings-input.mdx) is set to [`TransactionFlowStrategyEnum.CHARGE`](/api-reference/payments/enums/transaction-flow-strategy-enum.mdx)): + +- First `AUTHORIZED` webhook is sent when payment is first created, it's an authorization (funds locked on customer payment method) +- Then `CAPTURE` webhook is sent when transaction is charged (this happens automatically by Adyen) and funds are transferred to you. + +Adyen app currently assumes that `AUTHORIZED` webhook is used for both charge and captures and identifies which flow was used based on your Saleor and Adyen merchant account settings. + +`CHARGE` webhook is currently used only when reporting charges on previously authorized payment when you use either [`TransactionFlowStrategyEnum.AUTHORIZATION`](/api-reference/payments/enums/transaction-flow-strategy-enum.mdx) in your Saleor channel or you set Capture delay to "manual" in merchant account settings. +::: + +To disable this setting that go to the `Dashboard > Developers > Webhooks`. In upper corner click `Settings` button: + +![Adyen dashboard webhooks page, where "Settings" button is located](./adyen_webhook_settings_delayed_capture_1.png) + +On the Webhook settings page make sure to uncheck "Delayed Capture" setting: + +![Adyen dashboard webhook settings page, where Delayed Capture setting is available, the checkbox is unchecked](./adyen_webhook_settings_delayed_capture_2.png) diff --git a/docs/developer/app-store/apps/adyen/storefront.mdx b/docs/developer/app-store/apps/adyen/storefront.mdx index e9613290d..a493ba361 100644 --- a/docs/developer/app-store/apps/adyen/storefront.mdx +++ b/docs/developer/app-store/apps/adyen/storefront.mdx @@ -49,7 +49,7 @@ The response: ## Obtaining Adyen payment methods -Next, you need to fetch configured payment methods from Adyen. To do that, use the [`paymentGatewayInitialize`](api-reference/payments/mutations/payment-gateway-initialize.mdx) mutation. The mutation returns a `PaymentGatewayInitialize` object with `data` field containing a list of payment methods. The `data` field is an object with the following fields: +Next, you need to fetch configured payment methods from Adyen. To do that, use the [`paymentGatewayInitialize`](api-reference/payments/mutations/payment-gateway-initialize.mdx) mutation. The mutation returns an `PaymentGatewayInitialize` object with `data` field containing a list of payment methods. The `data` field is an object with the following fields: ```ts {