diff --git a/.github/workflows/e2e-checkout-advanced.yml b/.github/workflows/e2e-checkout-advanced.yml index 0b14c847..0b55095b 100644 --- a/.github/workflows/e2e-checkout-advanced.yml +++ b/.github/workflows/e2e-checkout-advanced.yml @@ -12,8 +12,30 @@ on: - 'checkout-example-advanced/**' jobs: + # e2e testing with Adyen.Web Drop-in/Components v5 (see /_archive/v5 folder) + checkout-advanced-v5: + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v3 + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + - name: Grant execute permission for gradlew + run: chmod +x checkout-example-advanced/_archive/v5/gradlew + - name: Build checkout-example-advanced/v5 with Gradle + run: cd checkout-example-advanced/_archive/v5; ./gradlew build + - name: Build checkout-example-advanced image + run: docker build -t checkout-example-advanced-v5:latest -f ./checkout-example-advanced/_archive/v5/Dockerfile ./checkout-example-advanced/_archive/v5 + - name: Start checkout-example-advanced container + run: docker run --rm -d --name checkout-example-advanced-v5 -p 8080:8080 -e ADYEN_API_KEY="${{ secrets.ADYEN_API_KEY }}" -e ADYEN_MERCHANT_ACCOUNT=${{ secrets.ADYEN_MERCHANT_ACCOUNT }} -e ADYEN_CLIENT_KEY=${{ secrets.ADYEN_CLIENT_KEY }} -e ADYEN_HMAC_KEY=${{ secrets.ADYEN_HMAC_KEY }} checkout-example-advanced-v5:latest + - name: Run testing suite + run: docker run --rm --name adyen-testing-suite -e PLAYWRIGHT_FOLDERNAME=advanced-checkout/v5 -e ADYEN_HMAC_KEY=${{ secrets.ADYEN_HMAC_KEY }} --network host ghcr.io/adyen-examples/adyen-testing-suite:main - checkout-advanced: + # e2e testing with Adyen.Web Drop-in/Components v6 + checkout-advanced-v6: runs-on: ubuntu-latest steps: - name: Checkout project @@ -33,3 +55,4 @@ jobs: run: docker run --rm -d --name checkout-example-advanced -p 8080:8080 -e ADYEN_API_KEY="${{ secrets.ADYEN_API_KEY }}" -e ADYEN_MERCHANT_ACCOUNT=${{ secrets.ADYEN_MERCHANT_ACCOUNT }} -e ADYEN_CLIENT_KEY=${{ secrets.ADYEN_CLIENT_KEY }} -e ADYEN_HMAC_KEY=${{ secrets.ADYEN_HMAC_KEY }} checkout-example-advanced:latest - name: Run testing suite run: docker run --rm --name adyen-testing-suite -e PLAYWRIGHT_FOLDERNAME=advanced-checkout -e ADYEN_HMAC_KEY=${{ secrets.ADYEN_HMAC_KEY }} --network host ghcr.io/adyen-examples/adyen-testing-suite:main + diff --git a/checkout-example-advanced/Dockerfile b/checkout-example-advanced/Dockerfile index b9e9562c..f052365a 100644 --- a/checkout-example-advanced/Dockerfile +++ b/checkout-example-advanced/Dockerfile @@ -1,4 +1,3 @@ FROM amazoncorretto:17-alpine-jdk -MAINTAINER jlengrand -COPY build/libs/adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar -ENTRYPOINT ["java","-jar","/adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar"] +COPY build/libs/adyen-java-spring-online-payments-checkout-advanced.jar adyen-java-spring-online-payments-checkout-advanced.jar +ENTRYPOINT ["java","-jar","/adyen-java-spring-online-payments-checkout-advanced.jar"] diff --git a/checkout-example-advanced/README.md b/checkout-example-advanced/README.md index 70bfc55f..745a0014 100644 --- a/checkout-example-advanced/README.md +++ b/checkout-example-advanced/README.md @@ -1,5 +1,8 @@ # Adyen [Online Payments](https://docs.adyen.com/online-payments) Integration Demo - Advanced Flow +Checkout advanced sample application using `Adyen.Web Drop-in/Components v6.x.x`, (see [folder /_archive/v5](./_archive/v5) to access the previous version using `Adyen.Web Drop-in/Components v5.x.x`). + + [![Java CI with Gradle](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/build-checkout-advanced.yml/badge.svg)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/build-checkout-advanced.yml) [![E2E (Playwright)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/e2e-checkout-advanced.yml/badge.svg)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/e2e-checkout-advanced.yml) diff --git a/checkout-example-advanced/_archive/v5/.gitignore b/checkout-example-advanced/_archive/v5/.gitignore new file mode 100644 index 00000000..b63da455 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/checkout-example-advanced/_archive/v5/Dockerfile b/checkout-example-advanced/_archive/v5/Dockerfile new file mode 100644 index 00000000..c01c11d1 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/Dockerfile @@ -0,0 +1,3 @@ +FROM amazoncorretto:17-alpine-jdk +COPY build/libs/adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar +ENTRYPOINT ["java","-jar","/adyen-java-spring-online-payments-checkout-advanced-0.0.1-SNAPSHOT.jar"] diff --git a/checkout-example-advanced/_archive/v5/README.md b/checkout-example-advanced/_archive/v5/README.md new file mode 100644 index 00000000..5b86f9b2 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/README.md @@ -0,0 +1,106 @@ +# Sample app with Adyen Adyen Web 5.68.0. + +This folder contains the **previous version** of the sample application that uses **Adyen Web 5.68.x**. + +**Check the root folder of the repository to use the latest version of Adyen Web/Components 6.x.x** + +# Adyen [Online Payments](https://docs.adyen.com/online-payments) Integration Demo - Advanced Flow + +[![Java CI with Gradle](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/build-checkout-advanced.yml/badge.svg)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/build-checkout-advanced.yml) +[![E2E (Playwright)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/e2e-checkout-advanced.yml/badge.svg)](https://github.com/adyen-examples/adyen-java-spring-online-payments/actions/workflows/e2e-checkout-advanced.yml) + +## Run demo in one-click + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/adyen-examples/adyen-java-spring-online-payments/tree/main/checkout-example-advanced) + [First time with Gitpod?](https://github.com/adyen-examples/.github/blob/main/pages/gitpod-get-started.md) + +## Description +This repository showcases a PCI-compliant integration of the **Advanced Flow**. Explore this simplified e-commerce demo to discover the code, libraries and configuration you need to enable various payment options in your checkout experience. + +It includes a **Java + Spring Boot + Thymeleaf** application that supports [Adyen Drop-in and Components](https://docs.adyen.com/online-payments/build-your-integration) +(ACH, Alipay, Cards, Dotpay, iDEAL, Klarna, PayPal, etc..) using the Adyen's API Library for Java ([GitHub](https://github.com/Adyen/adyen-java-api-library)). + + +> **Note:** +For a simpler flow using `/sessions`, check out the demo in the [`checkout-example`](../checkout-example) folder. + +![Card checkout demo](src/main/resources/static/images/cardcheckout.gif) + + +## Requirements + +- [Adyen API Credentials](https://docs.adyen.com/development-resources/api-credentials/) +- Java 17 +- Network access to Maven central + +## 1. Installation + +``` +git clone https://github.com/adyen-examples/adyen-java-spring-online-payments.git +``` + +## 2. Set the environment variables +* [API key](https://docs.adyen.com/user-management/how-to-get-the-api-key) +* [Client Key](https://docs.adyen.com/user-management/client-side-authentication) +* [Merchant Account](https://docs.adyen.com/account/account-structure) +* [HMAC Key](https://docs.adyen.com/development-resources/webhooks/verify-hmac-signatures) + +On Linux/Mac/Windows export/set the environment variables. +```shell +export ADYEN_API_KEY=yourAdyenApiKey +export ADYEN_MERCHANT_ACCOUNT=yourAdyenMerchantAccount +export ADYEN_CLIENT_KEY=yourAdyenClientKey +export ADYEN_HMAC_KEY=yourHmacKey +``` + +Alternatively, it's possible to define the variables in the `application.properties`. +```txt +ADYEN_API_KEY=yourAdyenApiKey +ADYEN_MERCHANT_ACCOUNT=yourAdyenMerchantAccount +ADYEN_CLIENT_KEY=yourAdyenClientKey +ADYEN_HMAC_KEY=yourHmacKey +``` + +## 3. Configure allowed origins (CORS) + +It is required to specify the domain or URL of the web applications that will make requests to Adyen. + +In the Customer Area add `http://localhost:8080` in the list of Allowed Origins associated with the Client Key. + +## 4. Run the application + +``` +cd checkout-example + +./gradlew bootRun +``` + +Visit [http://localhost:8080/](http://localhost:8080/) to choose an integration type. + +Try out the different payment methods with our [test card numbers](https://docs.adyen.com/development-resources/test-cards/test-card-numbers) and other payment method details. + +# Webhooks + +Webhooks deliver asynchronous notifications about the payment status and other events that are important to receive and process. +You can find more information about webhooks in [this blog post](https://www.adyen.com/knowledge-hub/consuming-webhooks). + +### Webhook setup + +In the Customer Area under the `Developers → Webhooks` section, [create](https://docs.adyen.com/development-resources/webhooks/#set-up-webhooks-in-your-customer-area) a new `Standard webhook`. + +A good practice is to set up basic authentication, copy the generated HMAC Key and set it as an environment variable. The application will use this to verify the [HMAC signatures](https://docs.adyen.com/development-resources/webhooks/verify-hmac-signatures/). + +Make sure the webhook is **enabled**, so it can receive notifications. + +### Expose an endpoint + +This demo provides a simple webhook implementation exposed at `/api/webhooks/notifications` that shows you how to receive, validate and consume the webhook payload. + +### Test your webhook + +The following webhooks `events` should be enabled: +* **AUTHORISATION** + + +To make sure that the Adyen platform can reach your application, we have written a [Webhooks Testing Guide](https://github.com/adyen-examples/.github/blob/main/pages/webhooks-testing.md) +that explores several options on how you can easily achieve this (e.g. running on localhost or cloud). diff --git a/checkout-example-advanced/_archive/v5/build.gradle b/checkout-example-advanced/_archive/v5/build.gradle new file mode 100644 index 00000000..4c02d942 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'org.springframework.boot' version '3.3.4' + id 'io.spring.dependency-management' version '1.1.6' + + id 'java' +} + +group = 'com.adyen' +version = '0.0.1-SNAPSHOT' + +sourceCompatibility = 17 +targetCompatibility = 17 + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' + implementation 'com.adyen:adyen-java-api-library:32.0.0' + + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } + +} + +test { + useJUnitPlatform() +} diff --git a/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.jar b/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.jar differ diff --git a/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.properties b/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..35bff925 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon May 22 09:48:27 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/checkout-example-advanced/_archive/v5/gradlew b/checkout-example-advanced/_archive/v5/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/checkout-example-advanced/_archive/v5/gradlew.bat b/checkout-example-advanced/_archive/v5/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/checkout-example-advanced/_archive/v5/settings.gradle b/checkout-example-advanced/_archive/v5/settings.gradle new file mode 100644 index 00000000..d6681414 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'adyen-java-spring-online-payments-checkout-advanced' + diff --git a/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/ApplicationProperty.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/ApplicationProperty.java new file mode 100644 index 00000000..907e54eb --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/ApplicationProperty.java @@ -0,0 +1,63 @@ +package com.adyen.checkout; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class ApplicationProperty { + + @Value("${server.port}") + private int serverPort; + + @Value("${ADYEN_API_KEY:#{null}}") + private String apiKey; + + @Value("${ADYEN_MERCHANT_ACCOUNT:#{null}}") + private String merchantAccount; + + @Value("${ADYEN_CLIENT_KEY:#{null}}") + private String clientKey; + + @Value("${ADYEN_HMAC_KEY:#{null}}") + private String hmacKey; + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getMerchantAccount() { + return merchantAccount; + } + + public void setMerchantAccount(String merchantAccount) { + this.merchantAccount = merchantAccount; + } + + public String getClientKey() { + return clientKey; + } + + public void setClientKey(String clientKey) { + this.clientKey = clientKey; + } + + public String getHmacKey() { + return hmacKey; + } + + public void setHmacKey(String hmacKey) { + this.hmacKey = hmacKey; + } +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/Config.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/Config.java similarity index 100% rename from checkout-example-advanced/src/main/java/com/adyen/checkout/Config.java rename to checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/Config.java diff --git a/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/OnlinePaymentsApplication.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/OnlinePaymentsApplication.java new file mode 100644 index 00000000..bcece038 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/OnlinePaymentsApplication.java @@ -0,0 +1,28 @@ +package com.adyen.checkout; + +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OnlinePaymentsApplication { + private static final Logger log = LoggerFactory.getLogger(OnlinePaymentsApplication.class); + + @Autowired + private ApplicationProperty applicationProperty; + + public static void main(String[] args) { + SpringApplication.run(OnlinePaymentsApplication.class, args); + } + + @PostConstruct + public void init() { + log.info("\n----------------------------------------------------------\n\t" + + "Application is running on http://localhost:" + applicationProperty.getServerPort() + + "\n----------------------------------------------------------"); + } + +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/api/CheckoutResource.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/api/CheckoutResource.java similarity index 100% rename from checkout-example-advanced/src/main/java/com/adyen/checkout/api/CheckoutResource.java rename to checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/api/CheckoutResource.java diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/api/WebhookResource.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/api/WebhookResource.java similarity index 100% rename from checkout-example-advanced/src/main/java/com/adyen/checkout/api/WebhookResource.java rename to checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/api/WebhookResource.java diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/web/CheckoutController.java b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/web/CheckoutController.java similarity index 97% rename from checkout-example-advanced/src/main/java/com/adyen/checkout/web/CheckoutController.java rename to checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/web/CheckoutController.java index e86aca75..f6846148 100644 --- a/checkout-example-advanced/src/main/java/com/adyen/checkout/web/CheckoutController.java +++ b/checkout-example-advanced/_archive/v5/src/main/java/com/adyen/checkout/web/CheckoutController.java @@ -1,4 +1,4 @@ -package com.adyen.checkout.web; +package com.adyen.checkout.views; import com.adyen.checkout.ApplicationProperty; import org.slf4j.Logger; diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/application.properties b/checkout-example-advanced/_archive/v5/src/main/resources/application.properties new file mode 100644 index 00000000..9e4ba1af --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.jackson.default-property-inclusion=non_null +spring.jackson.serialization.indent_output = true +server.port=8080 diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/adyenImplementation.js b/checkout-example-advanced/_archive/v5/src/main/resources/static/adyenImplementation.js new file mode 100644 index 00000000..1f8cac49 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/adyenImplementation.js @@ -0,0 +1,105 @@ +const clientKey = document.getElementById("clientKey").innerHTML; +const type = document.getElementById("type").innerHTML; + +async function initCheckout() { + try { + const paymentMethodsResponse = await callServer("/api/getPaymentMethods"); + const configuration = { + paymentMethodsResponse: paymentMethodsResponse, + clientKey, + locale: "en_US", + environment: "test", + showPayButton: true, + paymentMethodsConfiguration: { + ideal: { + showImage: true, + }, + card: { + hasHolderName: true, + holderNameRequired: true, + name: "Credit or debit card", + amount: { + value: 10000, + currency: "EUR", + }, + }, + paypal: { + amount: { + value: 10000, + currency: "USD", + }, + environment: "test", // Change this to "live" when you're ready to accept live PayPal payments + countryCode: "US", // Only needed for test. This will be automatically retrieved when you are in production. + onCancel: (data, component) => { + component.setStatus('ready'); + }, + } + }, + onSubmit: (state, component) => { + if (state.isValid) { + handleSubmission(state, component, "/api/initiatePayment"); + } + }, + onAdditionalDetails: (state, component) => { + handleSubmission(state, component, "/api/submitAdditionalDetails"); + }, + }; + // `spring.jackson.default-property-inclusion=non_null` needs to set in + // src/main/resources/application.properties to avoid NPE here + const checkout = await new AdyenCheckout(configuration); + checkout.create(type).mount(document.getElementById("payment")); + } catch (error) { + console.error(error); + alert("Error occurred. Look at console for details"); + } +} + +// Event handlers called when the shopper selects the pay button, +// or when additional information is required to complete the payment +async function handleSubmission(state, component, url) { + try { + const res = await callServer(url, state.data); + handleServerResponse(res, component); + } catch (error) { + console.error(error); + alert("Error occurred. Look at console for details"); + } +} + +// Calls your server endpoints +async function callServer(url, data) { + const res = await fetch(url, { + method: "POST", + body: data ? JSON.stringify(data) : "", + headers: { + "Content-Type": "application/json", + }, + }); + + return await res.json(); +} + +// Handles responses sent from your server to the client +function handleServerResponse(res, component) { + if (res.action) { + component.handleAction(res.action); + } else { + switch (res.resultCode) { + case "Authorised": + window.location.href = "/result/success"; + break; + case "Pending": + case "Received": + window.location.href = "/result/pending"; + break; + case "Refused": + window.location.href = "/result/failed"; + break; + default: + window.location.href = "/result/error"; + break; + } + } +} + +initCheckout(); diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/css/application.css b/checkout-example-advanced/_archive/v5/src/main/resources/static/css/application.css new file mode 100644 index 00000000..0d6fda34 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/css/application.css @@ -0,0 +1,413 @@ +/* General page body */ + +html, +body { + width: 100%; + margin: 0; + font-family: "Fakt", sans-serif, Helvetica, Arial; +} + +*, +:after, +:before { + box-sizing: border-box; +} + +a, +u { + text-decoration: none; +} + +a:hover { + text-decoration: none; +} + +.hidden { + display: none; +} + +#header { + background: #fff; + border-bottom: 1px solid #e6e9eb; + height: 44px; + left: 0; + margin-bottom: 24px; + padding: 14px 26px; + position: fixed; + text-align: center; + top: 0; + width: 100%; + z-index: 2; + box-sizing: border-box; +} + +/* Buttons */ + +.button { + background: #00112c; + border: 0; + border-radius: 6px; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1em; + font-weight: 500; + margin: 0; + padding: 15px; + text-align: center; + transition: background 0.3s ease-out, box-shadow 0.3s ease-out; + width: 100%; +} + +.button:hover { + background: #1c3045; + box-shadow: 0 3px 4px rgba(0, 15, 45, 0.2); +} + +.button:active { + background: #3a4a5c; +} + +.button:disabled { + background: #e6e9eb; + box-shadow: none; + cursor: not-allowed; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; +} + +/* end General page body */ + +/* Index page */ + +.main-container { + margin: auto; + max-width: 1048px; + padding: 0 16px; + display: flex; + flex-direction: column; +} + +.integration-list { + display: flex; + margin-top: 6%; + max-width: 1048px; + flex-wrap: wrap; + justify-content: space-between; + list-style: none; + margin: 0; + padding: 0; +} + +@media (min-width: 768px) { + .integration-list { + margin-left: -8px; + margin-bottom: -8px; + margin-right: -8px; + } +} + +@media (min-width: 1024px) { + .integration-list { + margin-left: -16px; + margin-bottom: -16px; + margin-right: -16px; + } +} + +.integration-list-item { + background: #f7f8f9; + border-radius: 6px; + flex: 1 1 0; + margin: 4px; + min-width: 40%; + position: relative; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid #f7f8f9; +} + +.integration-list-item:hover { + box-shadow: 0 16px 24px 0 #e5eaef; + text-decoration: none; + border: 1px solid #06f; +} + +@media (min-width: 768px) { + .integration-list-item { + margin-left: 16px; + margin-bottom: 16px; + margin-right: 16px; + margin-top: 16px; + min-width: 25%; + } +} + +.integration-list-item-link { + padding: 20px; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +@media (min-width: 768px) { + .integration-list-item-link { + padding-left: 28px; + padding-bottom: 28px; + padding-right: 28px; + padding-top: 28px; + } +} + +.integration-list-item-title { + margin: 0; + text-align: center; + color: #00112c; + font-size: 1em; + font-weight: 700; + margin: 10px 0 0; +} + +@media (min-width: 768px) { + .integration-list-item-title { + font-size: 24px; + margin-left: 0; + margin-bottom: 6px; + margin-right: 0; + } +} + +.integration-list-item-subtitle { + color: #00112c; + font-size: 0.67em; + font-weight: 700; + margin: 10px 0 0; +} + +@media (min-width: 768px) { + .integration-list-item-subtitle { + font-size: 16px; + margin-left: 0; + margin-bottom: 6px; + margin-right: 0; + } +} + +.title-container { + display: flex; + flex-direction: column; + align-items: center; +} + +.info { + margin-top: 10%; + color: #00112c; +} + +/* end Index page */ + +/* Cart preview page */ + +.shopping-cart { + float: right; +} +@media (min-width: 768px) { + .shopping-cart { + padding-left: 0; + padding-bottom: 0; + padding-right: 0; + padding-top: 3px; + } +} +.shopping-cart-link { + display: inline-block; + position: relative; +} +.order-summary-list { + border-top: 1px solid #e6e9eb; +} +.order-summary-list-list-item { + border-bottom: 1px solid #e6e9eb; + display: flex; + height: 97px; +} +.order-summary-list-list-item-image { + height: 64px; + margin: 16px; + width: 64px; +} +.order-summary-list-list-item-title { + font-weight: 700; + margin: auto auto auto 0; +} +.order-summary-list-list-item-price { + color: #687282; + margin: auto 16px; + text-align: right; + width: 80px; +} +@media (min-width: 768px) { + .order-summary-list-list-item-price { + margin-left: 24px; + margin-bottom: auto; + margin-right: 24px; + margin-top: auto; + } +} +.order-summary-list-list-item-remove-product { + background: none; + border: 0; + cursor: pointer; + height: 25px; + margin: auto 0; + padding: 0; + width: 25px; +} +.cart { + text-align: center; + margin-top: 80px; +} +.cart-footer { + font-weight: 700; + margin-top: 17px; + text-align: right; +} +@media (min-width: 768px) { + .cart-footer { + margin-top: 24px; + } +} +.cart-footer .button { + margin-top: 30px; + width: 100%; +} +@media (min-width: 768px) { + .cart-footer .button { + margin-top: 0; + width: 288px; + } +} +.cart-footer-amount { + margin-left: 16px; + margin-right: 24px; +} +.whole-preview { + margin: auto; + max-width: 1110px; + padding: 0 16px; +} +@media (min-width: 1440px) { + .whole-preview { + padding-left: 0; + padding-bottom: 0; + padding-right: 0; + padding-top: 0; + } +} + +/* end of Cart preview page */ + +/* Payment page */ + +#payment-page { + display: flex; + flex-direction: column; + align-items: center; +} + +#payment-page .container { + margin-top: 100px; + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + max-width: 1110px; + padding-left: 8px; + padding-right: 8px; +} + +.checkout-component { + background: #f7f8f9; + border: 1px solid #e6e9eb; + border-radius: 12px; + margin: 8px 0; +} + +/* Adyen Components */ +.payment { + width: 100%; + padding-top: 0px !important; + padding-left: 20px; + padding-right: 20px; +} + +@media screen and (max-width: 1076px) { + #payment-page .container { + display: flex; + flex-direction: column; + align-items: center; + } + + .payment { + align-self: center; + } +} + +.payment-container { + display: flex; + justify-content: center; + background: #f7f8f9; + border: 1px solid #e6e9eb; + border-radius: 12px; + padding-top: 18px; + padding-bottom: 18px; + width: 100%; + height: 100%; +} + +/* end Payments page */ + + +/* Status page */ + +.status-container { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.status { + margin: 100px 0 126px; + text-align: center; +} + +.status .status-image { + display: block; + height: 100px; + margin: 16px auto 0; +} + +.status .status-image-thank-you { + height: 66px; +} + +.status .status-message { + margin: 8px 0 24px; +} + +.status .button { + max-width: 236px; +} + +@media (min-width: 768px) { + .status .button { + max-width: 200px; + } +} + +/* end Status page */ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/.keep b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/.keep new file mode 100644 index 00000000..e69de29b diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/cardcheckout.gif b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/cardcheckout.gif new file mode 100644 index 00000000..dc3e612c Binary files /dev/null and b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/cardcheckout.gif differ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/error.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/error.svg new file mode 100644 index 00000000..4db9773b --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/failed.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/failed.svg new file mode 100644 index 00000000..4db9773b --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/failed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/headphones.png b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/headphones.png new file mode 100644 index 00000000..12d1cf44 Binary files /dev/null and b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/headphones.png differ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/mystore-logo.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/mystore-logo.svg new file mode 100644 index 00000000..0418b242 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/mystore-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/pending.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/pending.svg new file mode 100644 index 00000000..465c4b46 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/pending.svg @@ -0,0 +1,24 @@ + + + + Icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/success.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/success.svg new file mode 100644 index 00000000..465c4b46 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/success.svg @@ -0,0 +1,24 @@ + + + + Icon + Created with Sketch. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/sunglasses.png b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/sunglasses.png new file mode 100644 index 00000000..1f280537 Binary files /dev/null and b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/sunglasses.png differ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/static/images/thank-you.svg b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/thank-you.svg new file mode 100644 index 00000000..587d3b5b --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/static/images/thank-you.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/templates/checkout.html b/checkout-example-advanced/_archive/v5/src/main/resources/templates/checkout.html new file mode 100644 index 00000000..03e6907f --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/templates/checkout.html @@ -0,0 +1,24 @@ + + + Checkout + + +
+ + + + +
+ + +
+
+
+
+ + +
+ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/templates/index.html b/checkout-example-advanced/_archive/v5/src/main/resources/templates/index.html new file mode 100644 index 00000000..d573b4f5 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/templates/index.html @@ -0,0 +1,118 @@ + + + Select type + + +
+
+
+

Select a demo

+

Click to view an interactive example of a PCI-compliant UI integration for online payments.

+

+ Make sure the payment method you want to use are enabled for your account. + Refer the documentation + to add missing + payment methods. +

+

To learn more about client-side integration solutions, check out Online + payments.

+
+ +
+
+ + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/templates/layout.html b/checkout-example-advanced/_archive/v5/src/main/resources/templates/layout.html new file mode 100644 index 00000000..b3895e5b --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/templates/layout.html @@ -0,0 +1,46 @@ + + + + + Checkout Demo Advanced + + + + + + + + + + + + + + + + + + +
+ +
+ + diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/templates/preview.html b/checkout-example-advanced/_archive/v5/src/main/resources/templates/preview.html new file mode 100644 index 00000000..41a09e74 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/templates/preview.html @@ -0,0 +1,34 @@ + + + Preview + + +
+
+

Cart

+
+
    +
  • + +

    Sunglasses

    +

    50.00

    +
  • +
  • + +

    Headphones

    +

    50.00

    +
  • +
+
+ +
+
+ diff --git a/checkout-example-advanced/_archive/v5/src/main/resources/templates/result.html b/checkout-example-advanced/_archive/v5/src/main/resources/templates/result.html new file mode 100644 index 00000000..8e145b12 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/main/resources/templates/result.html @@ -0,0 +1,26 @@ + + + + + +
+
+ + + +

+ Your order has been successfully placed. + Your order has been received! Payment completion pending. + The payment was refused. Please try a different payment method or card. + + Error! Please review response in console and refer to + Response handling. + +

+ Return Home +
+
+ diff --git a/checkout-example-advanced/_archive/v5/src/test/java/com/adyen/checkout/OnlinePaymentsApplicationTests.java b/checkout-example-advanced/_archive/v5/src/test/java/com/adyen/checkout/OnlinePaymentsApplicationTests.java new file mode 100644 index 00000000..e07d9e45 --- /dev/null +++ b/checkout-example-advanced/_archive/v5/src/test/java/com/adyen/checkout/OnlinePaymentsApplicationTests.java @@ -0,0 +1,26 @@ +package com.adyen.checkout; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.event.annotation.BeforeTestClass; + +@SpringBootTest +class OnlinePaymentsApplicationTests { + + @BeforeAll + public static void onceExecutedBeforeAll() { + System.setProperty("ADYEN_API_KEY", "testKey"); + System.setProperty("ADYEN_MERCHANT_ACCOUNT", "testAccount"); + System.setProperty("ADYEN_CLIENT_KEY", "testKey"); + } + + @AfterAll + public static void onceExecutedAfterAll(){ + System.clearProperty("ADYEN_API_KEY"); + System.clearProperty("ADYEN_MERCHANT_ACCOUNT"); + System.clearProperty("ADYEN_CLIENT_KEY"); + } + +} diff --git a/checkout-example-advanced/_archive/v5/startDocker.sh b/checkout-example-advanced/_archive/v5/startDocker.sh new file mode 100755 index 00000000..a61dc10f --- /dev/null +++ b/checkout-example-advanced/_archive/v5/startDocker.sh @@ -0,0 +1,6 @@ +docker run \ +-e ADYEN_CLIENT_KEY \ +-e ADYEN_MERCHANT_ACCOUNT \ +-e ADYEN_HMAC_KEY \ +-e ADYEN_API_KEY \ +-p8080:8080 checkout-example-advanced:latest diff --git a/checkout-example-advanced/build.gradle b/checkout-example-advanced/build.gradle index 36caf9f3..ce0bd808 100644 --- a/checkout-example-advanced/build.gradle +++ b/checkout-example-advanced/build.gradle @@ -6,7 +6,6 @@ plugins { } group = 'com.adyen' -version = '0.0.1-SNAPSHOT' sourceCompatibility = 17 targetCompatibility = 17 diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/ApplicationProperty.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/ApplicationProperty.java index 907e54eb..9227f1e6 100644 --- a/checkout-example-advanced/src/main/java/com/adyen/checkout/ApplicationProperty.java +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/ApplicationProperty.java @@ -60,4 +60,4 @@ public String getHmacKey() { public void setHmacKey(String hmacKey) { this.hmacKey = hmacKey; } -} +} \ No newline at end of file diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/api/CheckoutController.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/api/CheckoutController.java new file mode 100644 index 00000000..642b80c6 --- /dev/null +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/api/CheckoutController.java @@ -0,0 +1,184 @@ +package com.adyen.checkout.api; + +import java.io.IOException; +import java.util.Arrays; +import java.util.UUID; + +import com.adyen.checkout.configurations.ApplicationConfiguration; +import com.adyen.model.RequestOptions; +import com.adyen.service.checkout.PaymentsApi; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.view.RedirectView; +import com.adyen.model.checkout.*; +import com.adyen.service.exception.ApiException; + +/** + * REST controller for using Adyen checkout API + */ +@RestController +public class CheckoutController { + private final Logger log = LoggerFactory.getLogger(CheckoutController.class); + + private final ApplicationConfiguration applicationConfiguration; + + private final PaymentsApi paymentsApi; + + public CheckoutController(PaymentsApi paymentsApi, ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + this.paymentsApi = paymentsApi; + } + + /** + * {@code POST /api/paymentMethods}: Retrieves available payment methods from Adyen. + * + * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the paymentMethods response. + * @throws IOException from Adyen API. + * @throws ApiException from Adyen API. + */ + @PostMapping("/api/paymentMethods") + public ResponseEntity paymentMethods() throws IOException, ApiException { + PaymentMethodsRequest paymentMethodsRequest = new PaymentMethodsRequest(); + paymentMethodsRequest.setMerchantAccount(this.applicationConfiguration.getAdyenMerchantAccount()); + paymentMethodsRequest.setChannel(PaymentMethodsRequest.ChannelEnum.WEB); + + log.info("Sending request to /paymentMethods"); + PaymentMethodsResponse response = paymentsApi.paymentMethods(paymentMethodsRequest); + log.info("Adyen: /paymentMethods response: {}", response); + return ResponseEntity.ok().body(response); + } + + /** + * {@code POST /api/payments}: Make a payment request to Adyen. + * + * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the paymentMethods response. + * @throws IOException from Adyen API. + * @throws ApiException from Adyen API. + */ + @PostMapping("/api/payments") + public ResponseEntity payments(@RequestHeader String host, @RequestBody PaymentRequest body, HttpServletRequest request) throws IOException, ApiException { + PaymentRequest paymentRequest = new PaymentRequest(); + + Amount amount = new Amount() + .currency("EUR") + .value(10000L); // value is 100€ in minor units, hardcoded in the example, total amount should be retrieved from a storage, e.g. database. + + // Sets ADYEN_MERCHANT_ACCOUNT in the request + paymentRequest.setMerchantAccount(this.applicationConfiguration.getAdyenMerchantAccount()); + paymentRequest.setChannel(PaymentRequest.ChannelEnum.WEB); + paymentRequest.setReference(UUID.randomUUID().toString()); // required + paymentRequest.setReturnUrl(request.getScheme() + "://" + host + "/api/handleShopperRedirect"); // Redirect flow + + paymentRequest.setAmount(amount); + + // Set countryCode and lineItems fields, which is required for some payment methods (e.g. Klarna) + paymentRequest.setCountryCode("NL"); + paymentRequest.setLineItems(Arrays.asList( + new LineItem().quantity(1L).amountIncludingTax(5000L).description("Sunglasses"), + new LineItem().quantity(1L).amountIncludingTax(5000L).description("Headphones")) + ); + + AuthenticationData authenticationData = new AuthenticationData(); + authenticationData.setAttemptAuthentication(AuthenticationData.AttemptAuthenticationEnum.ALWAYS); + // Add the following lines for Native 3DS2: + //ThreeDSRequestData threeDSRequestData = new ThreeDSRequestData(); + //threeDSRequestData.setNativeThreeDS(ThreeDSRequestData.NativeThreeDSEnum.PREFERRED); + //authenticationData.setThreeDSRequestData(threeDSRequestData); + + paymentRequest.setAuthenticationData(authenticationData); + + // Required for authentication - 3DS2 redirect flow + paymentRequest.setOrigin(request.getScheme() + "://" + host); + paymentRequest.setBrowserInfo(body.getBrowserInfo()); + paymentRequest.setShopperIP(request.getRemoteAddr()); + paymentRequest.setPaymentMethod(body.getPaymentMethod()); + + // we strongly recommend that you the billingAddress in your request + // card schemes require this for channel web, iOS, and Android implementations + /*paymentRequest.setBillingAddress(new BillingAddress() + .city("Amsterdam") + .country("NL") + .postalCode("1234AA") + .street("Street 1") + .houseNumberOrName("1")); + */ + + // Sets the shopper email + //paymentRequest.setShopperEmail("youremail@email.com"); + + // https://docs.adyen.com/development-resources/api-idempotency/ + RequestOptions requestOptions = new RequestOptions(); + requestOptions.setIdempotencyKey(UUID.randomUUID().toString()); + + log.info("Sending request to /payments"); + PaymentResponse paymentResponse = paymentsApi.payments(paymentRequest, requestOptions); + log.info("Adyen: /payments response: {}", paymentRequest); + return ResponseEntity.ok().body(paymentResponse); + } + + /** + * {@code POST /api/payments/details} : Make a payments/details request to Adyen. + * + * + * @return the {@link ResponseEntity} with status {@code 200 (Ok)} and with body the paymentMethods response. + * @throws IOException from Adyen API. + * @throws ApiException from Adyen API. + */ + @PostMapping("/api/payments/details") + public ResponseEntity paymentsDetails(@RequestBody PaymentDetailsRequest detailsRequest) throws IOException, ApiException { + log.info("Sending request to /payments/details {}", detailsRequest); + PaymentDetailsResponse detailsResponse = paymentsApi.paymentsDetails(detailsRequest); + log.info("Adyen: /payments/details response: {}", detailsResponse); + return ResponseEntity.ok().body(detailsResponse); + } + + /** + * {@code GET /api/handleShopperRedirect} : Handle redirect during payment. This gets called during the redirect flow, you can specify this in the `returnUrl`-field when constructing the /payments request to Adyen. + * + * @return the {@link RedirectView} with status {@code 302} + * @throws IOException from Adyen API. + * @throws ApiException from Adyen API. + */ + @GetMapping("/api/handleShopperRedirect") + public RedirectView redirect(@RequestParam(required = false) String payload, @RequestParam(required = false) String redirectResult) throws IOException, ApiException { + // Sends a request to /payments/completionDetails when redirect occurs + PaymentCompletionDetails completionDetails = new PaymentCompletionDetails(); + if (redirectResult != null && !redirectResult.isEmpty()) { + // For redirect, you are redirected to an Adyen domain to complete the 3DS2 challenge + // After completing the 3DS2 challenge, you get the redirect result from Adyen in the returnUrl + // We then pass the redirectResult to the /completionDetails call to Adyen. + completionDetails.redirectResult(redirectResult); + } else if (payload != null && !payload.isEmpty()) { + completionDetails.payload(payload); + } + + PaymentDetailsRequest detailsRequest = new PaymentDetailsRequest(); + detailsRequest.setDetails(completionDetails); + + log.info("Sending request to /payments/details {}", detailsRequest); + PaymentDetailsResponse paymentDetailsResponse = paymentsApi.paymentsDetails(detailsRequest); + log.info("Adyen: /payments/details response: {}", paymentDetailsResponse); + + // Handle redirect after getting the /payment/completionDetails paymentDetailsResponse + String redirectURL = "/result/"; + switch (paymentDetailsResponse.getResultCode()) { + case AUTHORISED: + redirectURL += "success"; + break; + case PENDING: + case RECEIVED: + redirectURL += "pending"; + break; + case REFUSED: + redirectURL += "failed"; + break; + default: + redirectURL += "error"; + break; + } + return new RedirectView(redirectURL + "?reason=" + paymentDetailsResponse.getResultCode()); + } +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/api/WebhookController.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/api/WebhookController.java new file mode 100644 index 00000000..b443966f --- /dev/null +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/api/WebhookController.java @@ -0,0 +1,103 @@ +package com.adyen.checkout.api; + +import com.adyen.checkout.ApplicationProperty; +import com.adyen.model.notification.NotificationRequest; +import com.adyen.model.notification.NotificationRequestItem; +import com.adyen.util.HMACValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.SignatureException; +import java.util.Optional; + +/** + * REST controller for receiving Adyen webhook notifications + */ +@RestController +public class WebhookController { + private final Logger log = LoggerFactory.getLogger(WebhookController.class); + + private final ApplicationProperty applicationProperty; + private final HMACValidator hmacValidator; + + @Autowired + public WebhookController(ApplicationProperty applicationProperty, HMACValidator hmacValidator) { + this.applicationProperty = applicationProperty; + + if (this.applicationProperty.getHmacKey() == null) { + log.warn("ADYEN_HMAC_KEY is UNDEFINED (Webhook cannot be authenticated)"); + //throw new RuntimeException("ADYEN_HMAC_KEY is UNDEFINED"); + } + this.hmacValidator = hmacValidator; + } + + /** + * Process incoming Webhook notification: get NotificationRequestItem, validate HMAC signature + * Consume the webhook asynchronously, send response status 202: Accepted + * Best practice: Add authentication to protect this endpoint, see https://docs.adyen.com/development-resources/webhooks/ + * @param json The payload of the incoming webhook + * @return 202 "Accepted" - If your server has finished processing the payload (e.g. storing it). + */ + @PostMapping("/api/webhooks/notifications") + public ResponseEntity webhooks(@RequestBody String json) throws Exception { + log.warn("Incoming (not validated) webhook {}", json); + // From JSON string to object + NotificationRequest notificationRequest = NotificationRequest.fromJson(json); + + // Fetch first (and only) NotificationRequestItem + Optional notificationRequestItem = notificationRequest.getNotificationItems().stream().findFirst(); + + if (notificationRequestItem.isEmpty()) { + // Unexpected webhook with no payload + log.warn("Empty NotificationItem"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Empty NotificationItem"); + } + + try { + NotificationRequestItem item = notificationRequestItem.get(); + if (hmacValidator.validateHMAC(item, this.applicationProperty.getHmacKey())) { + log.info(""" + Received webhook with event {} :\s + Merchant Reference: {} + Alias : {} + PSP reference : {}""" + , item.getEventCode(), item.getMerchantReference(), item.getAdditionalData().get("alias"), item.getPspReference()); + + // Consume event asynchronously + consumeEvent(item); + + } else { + // Invalid HMAC signature + log.warn("Could HMAC signature did not match: {}", item); + throw new RuntimeException("HMAC signature did not match"); + } + } catch (SignatureException e) { + // Unexpected error during HMAC validation + log.error("Error while validating HMAC Key", e); + throw new SignatureException(e); + } + + // Acknowledge event has been consumed + return ResponseEntity.status(HttpStatus.ACCEPTED).build(); + } + + // process payload asynchronously + void consumeEvent(NotificationRequestItem item) { + log.info("Consumed webhook {}", item.toString()); + + // add item to DB, queue or different thread + + // example: send to Kafka consumer + + // producer.send(producerRecord); + // producer.flush(); + // producer.close(); + } +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/ApplicationConfiguration.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/ApplicationConfiguration.java new file mode 100644 index 00000000..6180d08e --- /dev/null +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/ApplicationConfiguration.java @@ -0,0 +1,62 @@ +package com.adyen.checkout.configurations; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ApplicationConfiguration { + @Value("${server.port}") + private int serverPort; + + @Value("${ADYEN_API_KEY:#{null}}") + private String adyenApiKey; + + @Value("${ADYEN_MERCHANT_ACCOUNT:#{null}}") + private String adyenMerchantAccount; + + @Value("${ADYEN_CLIENT_KEY:#{null}}") + private String adyenClientKey; + + @Value("${ADYEN_HMAC_KEY:#{null}}") + private String adyenHmacKey; + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public String getAdyenApiKey() { + return adyenApiKey; + } + + public void setAdyenApiKey(String adyenApiKey) { + this.adyenApiKey = adyenApiKey; + } + + public String getAdyenMerchantAccount() { + return adyenMerchantAccount; + } + + public void setAdyenMerchantAccount(String adyenMerchantAccount) { + this.adyenMerchantAccount = adyenMerchantAccount; + } + + public String getAdyenClientKey() { + return adyenClientKey; + } + + public void setAdyenClientKey(String adyenClientKey) { + this.adyenClientKey = adyenClientKey; + } + + public String getAdyenHmacKey() { + return adyenHmacKey; + } + + public void setAdyenHmacKey(String adyenHmacKey) { + this.adyenHmacKey = adyenHmacKey; + } +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/DependencyInjectionConfiguration.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/DependencyInjectionConfiguration.java new file mode 100644 index 00000000..f4a59e03 --- /dev/null +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/configurations/DependencyInjectionConfiguration.java @@ -0,0 +1,63 @@ +package com.adyen.checkout.configurations; + +import com.adyen.Client; +import com.adyen.Config; +import com.adyen.enums.Environment; +import com.adyen.service.checkout.PaymentsApi; +import com.adyen.util.HMACValidator; +import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class DependencyInjectionConfiguration { + private final Logger log = LoggerFactory.getLogger(DependencyInjectionConfiguration.class); + private final ApplicationConfiguration applicationConfiguration; + + public DependencyInjectionConfiguration(ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + + // https://docs.adyen.com/development-resources/api-credentials/#generate-api-key + if (applicationConfiguration.getAdyenApiKey() == null) { + log.error("ADYEN_API_KEY is null"); + //throw new RuntimeException("ADYEN_API_KEY is null"); + } + + // https://docs.adyen.com/account/account-structure/#merchant-accounts + if (applicationConfiguration.getAdyenMerchantAccount() == null) { + log.error("ADYEN_MERCHANT_ACCOUNT is null"); + //throw new RuntimeException("ADYEN_MERCHANT_ACCOUNT is null"); + } + + // https://docs.adyen.com/development-resources/client-side-authentication/#get-your-client-key + if (applicationConfiguration.getAdyenClientKey() == null) { + log.error("ADYEN_CLIENT_KEY is null"); + //throw new RuntimeException("ADYEN_CLIENT_KEY is null"); + } + } + + @Bean + Client client() { + Config config = new Config(); + config.setApiKey(applicationConfiguration.getAdyenApiKey()); + config.setEnvironment(Environment.TEST); + return new Client(config); + } + + @Bean + PaymentsApi paymentsApi() { + return new PaymentsApi(client()); + } + + @Bean + HMACValidator hmacValidator() { + return new HMACValidator(); + } + + @Bean + public LayoutDialect layoutDialect() { + return new LayoutDialect(); + } +} diff --git a/checkout-example-advanced/src/main/java/com/adyen/checkout/views/ViewController.java b/checkout-example-advanced/src/main/java/com/adyen/checkout/views/ViewController.java new file mode 100644 index 00000000..65f8c076 --- /dev/null +++ b/checkout-example-advanced/src/main/java/com/adyen/checkout/views/ViewController.java @@ -0,0 +1,52 @@ +package com.adyen.checkout.views; + +import com.adyen.checkout.ApplicationProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class ViewController { + + @Autowired + private ApplicationProperty applicationProperty; + + @Autowired + public ViewController(ApplicationProperty applicationProperty) { + this.applicationProperty = applicationProperty; + + if (this.applicationProperty.getClientKey() == null) { + Logger log = LoggerFactory.getLogger(ViewController.class); + log.warn("ADYEN_CLIENT_KEY is undefined "); + } + } + + @GetMapping("/") + public String index() { + return "index"; + } + + @GetMapping("/preview") + public String preview(@RequestParam String type, Model model) { + model.addAttribute("type", type); + return "preview"; + } + + @GetMapping("/checkout") + public String checkout(@RequestParam String type, Model model) { + model.addAttribute("type", type); + model.addAttribute("clientKey", this.applicationProperty.getClientKey()); + return "checkout"; + } + + @GetMapping("/result/{type}") + public String result(@PathVariable String type, Model model) { + model.addAttribute("type", type); + return "result"; + } +} \ No newline at end of file diff --git a/checkout-example-advanced/src/main/resources/static/adyenImplementation.js b/checkout-example-advanced/src/main/resources/static/adyenImplementation.js index 1f8cac49..dd22053c 100644 --- a/checkout-example-advanced/src/main/resources/static/adyenImplementation.js +++ b/checkout-example-advanced/src/main/resources/static/adyenImplementation.js @@ -1,105 +1,143 @@ const clientKey = document.getElementById("clientKey").innerHTML; -const type = document.getElementById("type").innerHTML; +const { AdyenCheckout, Dropin } = window.AdyenWeb; -async function initCheckout() { - try { - const paymentMethodsResponse = await callServer("/api/getPaymentMethods"); - const configuration = { - paymentMethodsResponse: paymentMethodsResponse, - clientKey, - locale: "en_US", - environment: "test", - showPayButton: true, - paymentMethodsConfiguration: { - ideal: { - showImage: true, - }, - card: { - hasHolderName: true, - holderNameRequired: true, - name: "Credit or debit card", - amount: { - value: 10000, - currency: "EUR", - }, - }, - paypal: { - amount: { - value: 10000, - currency: "USD", - }, - environment: "test", // Change this to "live" when you're ready to accept live PayPal payments - countryCode: "US", // Only needed for test. This will be automatically retrieved when you are in production. - onCancel: (data, component) => { - component.setStatus('ready'); - }, - } - }, - onSubmit: (state, component) => { - if (state.isValid) { - handleSubmission(state, component, "/api/initiatePayment"); - } - }, - onAdditionalDetails: (state, component) => { - handleSubmission(state, component, "/api/submitAdditionalDetails"); - }, - }; - // `spring.jackson.default-property-inclusion=non_null` needs to set in - // src/main/resources/application.properties to avoid NPE here - const checkout = await new AdyenCheckout(configuration); - checkout.create(type).mount(document.getElementById("payment")); - } catch (error) { - console.error(error); - alert("Error occurred. Look at console for details"); - } -} +async function startCheckout() { + try { + const paymentMethodsResponse = await fetch("/api/paymentMethods", { + method: "POST", + headers: { + "Content-Type": "application/json", + } + }).then(response => response.json()); -// Event handlers called when the shopper selects the pay button, -// or when additional information is required to complete the payment -async function handleSubmission(state, component, url) { - try { - const res = await callServer(url, state.data); - handleServerResponse(res, component); - } catch (error) { - console.error(error); - alert("Error occurred. Look at console for details"); - } -} + const configuration = { + paymentMethodsResponse: paymentMethodsResponse, + clientKey, + locale: "en_US", + countryCode: 'NL', + environment: "test", + showPayButton: true, + translations: { + 'en-US': { + 'creditCard.securityCode.label': 'CVV/CVC' + } + }, + onSubmit: async (state, component, actions) => { + console.info("onSubmit", state, component, actions); + try { + if (state.isValid) { + const {action, order, resultCode, donationToken} = await fetch("/api/payments", { + method: "POST", + body: state.data ? JSON.stringify(state.data) : "", + headers: { + "Content-Type": "application/json", + } + }).then(response => response.json()); + + if (!resultCode) { + actions.reject(); + } + + actions.resolve({ + resultCode, + action, + order, + donationToken + }); + } + } catch (error) { + console.error(error); + actions.reject(); + } + }, + onPaymentCompleted: (result, component) => { + console.info("onPaymentCompleted", result, component); + handleOnPaymentCompleted(result, component); + }, + onPaymentFailed: (result, component) => { + console.info("onPaymentFailed", result, component); + handleOnPaymentFailed(result, component); + }, + onError: (error, component) => { + console.error("onError", error.name, error.message, error.stack, component); + window.location.href = "/result/error"; + }, + onAdditionalDetails: async (state, component) => { + console.info("onAdditionalDetails", state, component); + const response = await fetch("/api/payments/details", { + method: "POST", + body: state.data ? JSON.stringify(state.data) : "", + headers: { + "Content-Type": "application/json", + } + }).then(response => response.json()); + + if (response.action) { + component.handleAction(response.action); + } else { + handleOnPaymentCompleted(response); + } + } + }; -// Calls your server endpoints -async function callServer(url, data) { - const res = await fetch(url, { - method: "POST", - body: data ? JSON.stringify(data) : "", - headers: { - "Content-Type": "application/json", - }, - }); + const paymentMethodsConfiguration = { + card: { + showBrandIcon: true, + hasHolderName: true, + holderNameRequired: true, + name: "Credit or debit card", + amount: { + value: 10000, + currency: "EUR", + }, + placeholders: { + cardNumber: '1234 5678 9012 3456', + expiryDate: 'MM/YY', + securityCodeThreeDigits: '123', + securityCodeFourDigits: '1234', + holderName: 'J. Smith' + } + } + }; - return await res.json(); + // Start the AdyenCheckout and mount the element onto the 'payment' div. + const adyenCheckout = await AdyenCheckout(configuration); + const dropin = new Dropin(adyenCheckout, { + paymentMethodsConfiguration: paymentMethodsConfiguration + }).mount(document.getElementById("payment")); + } catch (error) { + console.error(error); + alert("Error occurred. Look at console for details."); + } +} + +// Function to handle payment completion redirects +function handleOnPaymentCompleted(response) { + switch (response.resultCode) { + case "Authorised": + window.location.href = "/result/success"; + break; + case "Pending": + case "Received": + window.location.href = "/result/pending"; + break; + default: + window.location.href = "/result/error"; + break; + } } -// Handles responses sent from your server to the client -function handleServerResponse(res, component) { - if (res.action) { - component.handleAction(res.action); - } else { - switch (res.resultCode) { - case "Authorised": - window.location.href = "/result/success"; - break; - case "Pending": - case "Received": - window.location.href = "/result/pending"; - break; - case "Refused": - window.location.href = "/result/failed"; - break; - default: - window.location.href = "/result/error"; - break; +// Function to handle payment failure redirects +function handleOnPaymentFailed(response) { + switch (response.resultCode) { + case "Cancelled": + case "Refused": + window.location.href = "/result/failed"; + break; + default: + window.location.href = "/result/error"; + break; } - } } -initCheckout(); +startCheckout(); diff --git a/checkout-example-advanced/src/main/resources/templates/layout.html b/checkout-example-advanced/src/main/resources/templates/layout.html index b3895e5b..18d94bfa 100644 --- a/checkout-example-advanced/src/main/resources/templates/layout.html +++ b/checkout-example-advanced/src/main/resources/templates/layout.html @@ -20,17 +20,17 @@ crossorigin="anonymous" /> - - - + + +