mirror of
https://github.com/valitydev/adapter-flow-lib.git
synced 2024-11-05 15:55:21 +00:00
Init integration logic (#1)
This commit is contained in:
parent
5ba0c3628a
commit
a7872508fc
10
.github/workflows/build.yml
vendored
Normal file
10
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
name: Maven Build Artifact
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: valitydev/base-workflow/.github/workflows/maven-library-build.yml@v1
|
16
.github/workflows/deploy.yml
vendored
Normal file
16
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Maven Deploy Artifact
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
uses: valitydev/base-workflow/.github/workflows/maven-library-deploy.yml@v1
|
||||
secrets:
|
||||
server-username: ${{ secrets.OSSRH_USERNAME }}
|
||||
server-password: ${{ secrets.OSSRH_TOKEN }}
|
||||
deploy-secret-key: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
|
||||
deploy-secret-key-password: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
|
78
.gitignore
vendored
Normal file
78
.gitignore
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
.eunit
|
||||
deps
|
||||
*.o
|
||||
*.beam
|
||||
*.plt
|
||||
erl_crash.dump
|
||||
ebin/*.beam
|
||||
rel/example_project
|
||||
.concrete/DEV_MODE
|
||||
.rebar
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
*.iws
|
||||
*.ipr
|
||||
*.iml
|
||||
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
env.list
|
18
docs/client_implementations_manual.md
Normal file
18
docs/client_implementations_manual.md
Normal file
@ -0,0 +1,18 @@
|
||||
### Имплементация клиента?
|
||||
|
||||
Во все методы клиента приходит полная модель со всеми необходимыми полями для вызова:
|
||||
|
||||
[BaseRequestModel](../src/main/java/dev/vality/adapter/flow/lib/model/BaseRequestModel.java)
|
||||
|
||||
После формирования необходимой внутренней модели и внешнего вызова, вы должны сформировать следующую модель:
|
||||
|
||||
[BaseResponseModel](../src/main/java/dev/vality/adapter/flow/lib/model/BaseResponseModel.java)
|
||||
|
||||
Необходимо учитывать следующие особенности:
|
||||
|
||||
* Можно влиять на выбор следующего шага с помощью
|
||||
поля [BaseResponseModel.status](../src/main/java/dev/vality/adapter/flow/lib/constant/Status.java).
|
||||
* Также если был выбран статус <b>NEED_RETRY</b>, есть возможность выбрать конкретную версию процесса, если он
|
||||
существует в рамках вашего общего взаимодействия. Необходимо сверяться с общим флоу и необходимостью идти в ту или
|
||||
иную ветку.
|
||||
* При указании errorCode, в не зависимости от передаваемого статуса, процесс будет завершон с ошибкой.
|
12
docs/flow_using_lib.md
Normal file
12
docs/flow_using_lib.md
Normal file
@ -0,0 +1,12 @@
|
||||
### Как использовать бибилиотеку?
|
||||
|
||||
1. Необходимо провести аналитику и определить какой процесс вам подходит. Список процессов:
|
||||
* [Процесс с полным прохождением 3дс версии 1.0 и 2.0](./flows/Full Three Ds V1 V2 Flow Steps.md)
|
||||
* [Простой процесс с редиректом и поллингом статусов после всех операций](./flows/Full Three Ds V1 V2 Flow Steps.md)
|
||||
2. Если подходящий вам процесс найден, процесс продолжается по следующему пути:
|
||||
1. Подготавливается конфигурация под spring-boot [настройка конфигурации](./spring-boot-configuration.md).
|
||||
2. Релизовать, соответствующие выбранному флоу, методы
|
||||
из [RemoteClient](../src/main/java/dev/vality/adapter/flow/lib/client/RemoteClient.java) [инструкция](./client_implementations_manual.md)
|
||||
3. Реализовать валидаторы для ваших входных параметров, уникальных для вашего адаптера и настраевымых командой
|
||||
поддержки поддержкой(options - на уровне протокола damsel) [интерфейс](../src/main/java/dev/vality/adapter/flow/lib/validator/Validator.java)
|
||||
4. Реализация тестов
|
1
docs/flows/Full Three Ds V1 V2 Flow Steps.md
Normal file
1
docs/flows/Full Three Ds V1 V2 Flow Steps.md
Normal file
@ -0,0 +1 @@
|
||||
### Процесс с полным прохождением 3дс версии 1.0 и 2.0
|
1
docs/flows/Simple Redirect With Polling Flow Steps.md
Normal file
1
docs/flows/Simple Redirect With Polling Flow Steps.md
Normal file
@ -0,0 +1 @@
|
||||
### Простой процесс с редиректом и поллингом статусов после всех операций
|
470
docs/flows/imgs/Full Three Ds V1 V2 Flow Steps.svg
Normal file
470
docs/flows/imgs/Full Three Ds V1 V2 Flow Steps.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 119 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 67 KiB |
1
docs/spring-boot-configuration.md
Normal file
1
docs/spring-boot-configuration.md
Normal file
@ -0,0 +1 @@
|
||||
### Конфигурация Spring Boot?
|
247
pom.xml
Normal file
247
pom.xml
Normal file
@ -0,0 +1,247 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>library-parent-pom</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<artifactId>adapter-flow-lib</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>adapter-flow-lib</name>
|
||||
<description>Adapter flows libs for reuse</description>
|
||||
<url>https://github.com/valitydev/adapter-flow-lib.git</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<email>devs@vality.dev</email>
|
||||
<organization>Vality</organization>
|
||||
<organizationUrl>https://vality.dev</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/valitydev/library-parent-pom.git</connection>
|
||||
<developerConnection>scm:git:ssh://github.com/valitydev/library-parent-pom.git</developerConnection>
|
||||
<url>https://github.com/valitydev/library-parent-pom/tree/master</url>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<dockerfile.registry>${env.REGISTRY}</dockerfile.registry>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>15</java.version>
|
||||
<shared.resources.version>0.3.6</shared.resources.version>
|
||||
|
||||
<bender-proto.version>1.20-be9cdeb</bender-proto.version>
|
||||
|
||||
<apache.commons.lang3.version>3.10</apache.commons.lang3.version>
|
||||
<apache.commons.codec.version>1.15</apache.commons.codec.version>
|
||||
|
||||
<slf4j.version>1.7.33</slf4j.version>
|
||||
<lombok.version>1.18.22</lombok.version>
|
||||
<logback.version>1.2.10</logback.version>
|
||||
<logstash-logback-encoder.version>7.0.1</logstash-logback-encoder.version>
|
||||
|
||||
<jackson.version>2.11.2</jackson.version>
|
||||
<woody.version>[1.0.4,)</woody.version>
|
||||
|
||||
<damsel.version>1.544-dcd92dd</damsel.version>
|
||||
<cds-proto.version>1.62-07f2b0f</cds-proto.version>
|
||||
|
||||
<error-mapping-java.version>1.0.6</error-mapping-java.version>
|
||||
<adapter-client-lib.version>1.0.1</adapter-client-lib.version>
|
||||
<adapter-common-lib.version>1.0.0</adapter-common-lib.version>
|
||||
<junit-jupiter-engine.version>5.8.2</junit-jupiter-engine.version>
|
||||
<nop-rolling.version>0.0.1</nop-rolling.version>
|
||||
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
|
||||
<spring.version>5.3.14</spring.version>
|
||||
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
|
||||
<validation-api.version>2.0.1.Final</validation-api.version>
|
||||
<error-mapping-java.version>1.0.0</error-mapping-java.version>
|
||||
<jackson.version>2.11.2</jackson.version>
|
||||
|
||||
<spring-boot-starter.version>2.6.3</spring-boot-starter.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Third party libs -->
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>${validation-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${javax.servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>${spring-boot-starter.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${apache.commons.lang3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${apache.commons.codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
<version>${logstash-logback-encoder.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--Vality libs-->
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>damsel</artifactId>
|
||||
<version>${damsel.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.woody</groupId>
|
||||
<artifactId>woody-thrift</artifactId>
|
||||
<version>${woody.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.adapter-client-lib</groupId>
|
||||
<artifactId>cds-client-storage</artifactId>
|
||||
<version>${adapter-client-lib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.adapter-client-lib</groupId>
|
||||
<artifactId>hellgate-adapter-client</artifactId>
|
||||
<version>${adapter-client-lib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>bender-proto</artifactId>
|
||||
<version>${bender-proto.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>error-mapping-java</artifactId>
|
||||
<version>${error-mapping-java.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<!--Spring libs-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot-starter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<version>${spring-boot-starter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring-boot-starter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter-engine.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.geck</groupId>
|
||||
<artifactId>serializer</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.logback</groupId>
|
||||
<artifactId>nop-rolling</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,53 @@
|
||||
package dev.vality.adapter.flow.lib.client;
|
||||
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
|
||||
/**
|
||||
* This interface is used to call an external system in order to perform some operation in the payment flow.
|
||||
* Not all methods are required for flows.
|
||||
*/
|
||||
public interface RemoteClient {
|
||||
|
||||
default BaseResponseModel auth(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel generateToken(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel pay(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel capture(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel cancel(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel refund(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel finish3ds(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel check3dsV2(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel finish3dsV2(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
default BaseResponseModel status(BaseRequestModel request) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MetaData {
|
||||
|
||||
public static final String META_REC_TOKEN = "meta_rec_token";
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
public enum OptionFields {
|
||||
|
||||
STAGE
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum RedirectFields {
|
||||
|
||||
TERM_URL("TermUrl");
|
||||
|
||||
private final String value;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Stage {
|
||||
|
||||
public static final String ONE = "ONE";
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
public enum Status {
|
||||
|
||||
/**
|
||||
* The operation will complete successfully or move on to the next step if a step exists.
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* The operation will fail and exit.
|
||||
*/
|
||||
ERROR,
|
||||
/**
|
||||
* The operation will redirect if there are options for the redirect.
|
||||
*/
|
||||
NEED_REDIRECT,
|
||||
/**
|
||||
* This status need for cycle or retry operation (example: polling status).
|
||||
*/
|
||||
NEED_RETRY
|
||||
|
||||
}
|
18
src/main/java/dev/vality/adapter/flow/lib/constant/Step.java
Normal file
18
src/main/java/dev/vality/adapter/flow/lib/constant/Step.java
Normal file
@ -0,0 +1,18 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
public enum Step {
|
||||
|
||||
AUTH,
|
||||
PAY,
|
||||
GENERATE_TOKEN,
|
||||
FINISH_THREE_DS_V1,
|
||||
FINISH_THREE_DS_V2,
|
||||
CANCEL,
|
||||
REFUND,
|
||||
CHECK_STATUS,
|
||||
CHECK_STATUS_3DS_V2,
|
||||
CHECK_NEED_3DS_V2,
|
||||
CAPTURE,
|
||||
DO_NOTHING
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
public enum TargetStatus {
|
||||
PROCESSED,
|
||||
CAPTURED,
|
||||
CANCELLED,
|
||||
REFUNDED
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.vality.adapter.flow.lib.constant;
|
||||
|
||||
public enum ThreeDsType {
|
||||
|
||||
V1,
|
||||
V2_SIMPLE,
|
||||
V2_FULL
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package dev.vality.adapter.flow.lib.controller;
|
||||
|
||||
import dev.vality.adapter.flow.lib.service.ThreeDsAdapterService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping({"/${server.rest.endpoint}"})
|
||||
@RequiredArgsConstructor
|
||||
public class ThreeDsCallbackController {
|
||||
|
||||
private final ThreeDsAdapterService threeDsAdapterService;
|
||||
|
||||
@PostMapping({"term-url"})
|
||||
public String receivePaymentIncomingParameters(HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) {
|
||||
return this.threeDsAdapterService.receivePaymentIncomingParameters(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
@PostMapping({"recurrent-term-url"})
|
||||
public String receiveRecurrentIncomingParameters(HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) {
|
||||
return this.threeDsAdapterService.receiveRecurrentIncomingParameters(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package dev.vality.adapter.flow.lib.converter;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.TemporaryContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class ExitStateModelToTemporaryContextConverter implements Converter<ExitStateModel, TemporaryContext> {
|
||||
|
||||
@Override
|
||||
public TemporaryContext convert(ExitStateModel source) {
|
||||
return TemporaryContext.builder()
|
||||
.providerTrxId(source.getProviderTrxId())
|
||||
.nextStep(source.getNextStep())
|
||||
.pollingInfo(source.getPollingInfo())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package dev.vality.adapter.flow.lib.converter.base;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EntryModelToBaseRequestModelConverter implements Converter<EntryStateModel, BaseRequestModel> {
|
||||
|
||||
@Override
|
||||
public BaseRequestModel convert(EntryStateModel entryStateModel) {
|
||||
return entryStateModel.getBaseRequestModel();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,145 @@
|
||||
package dev.vality.adapter.flow.lib.converter.entry;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.MetaData;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.constant.TargetStatus;
|
||||
import dev.vality.adapter.flow.lib.model.*;
|
||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
||||
import dev.vality.adapter.flow.lib.service.IdGenerator;
|
||||
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
||||
import dev.vality.adapter.flow.lib.utils.CallbackUrlExtractor;
|
||||
import dev.vality.adapter.flow.lib.utils.CardDataUtils;
|
||||
import dev.vality.adapter.flow.lib.utils.TargetStatusResolver;
|
||||
import dev.vality.cds.client.storage.CdsClientStorage;
|
||||
import dev.vality.cds.client.storage.utils.BankCardExtractor;
|
||||
import dev.vality.cds.storage.Auth3DS;
|
||||
import dev.vality.cds.storage.CardData;
|
||||
import dev.vality.cds.storage.SessionData;
|
||||
import dev.vality.damsel.domain.BankCard;
|
||||
import dev.vality.damsel.domain.TransactionInfo;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import dev.vality.java.cds.utils.model.CardDataProxyModel;
|
||||
import dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators;
|
||||
import dev.vality.java.damsel.utils.extractors.ProxyProviderPackageExtractors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CtxToEntryModelConverter implements Converter<PaymentContext, EntryStateModel> {
|
||||
|
||||
private final CdsClientStorage cdsStorage;
|
||||
private final TemporaryContextDeserializer temporaryContextDeserializer;
|
||||
private final IdGenerator idGenerator;
|
||||
private final TemporaryContextService temporaryContextService;
|
||||
private final CallbackUrlExtractor callbackUrlExtractor;
|
||||
|
||||
@Override
|
||||
public EntryStateModel convert(PaymentContext context) {
|
||||
var paymentInfo = context.getPaymentInfo();
|
||||
var payment = paymentInfo.getPayment();
|
||||
var temporaryContext = temporaryContextService.getTemporaryContext(context, temporaryContextDeserializer);
|
||||
var paymentResource = payment.getPaymentResource();
|
||||
var mobilePaymentDataBuilder = MobilePaymentData.builder();
|
||||
var cardDataBuilder = dev.vality.adapter.flow.lib.model.CardData.builder();
|
||||
|
||||
Step currentStep = temporaryContext.getNextStep();
|
||||
TargetStatus targetStatus = TargetStatusResolver.extractTargetStatus(context.getSession().getTarget());
|
||||
initPaymentData(context, paymentResource, mobilePaymentDataBuilder, cardDataBuilder, currentStep, targetStatus);
|
||||
|
||||
TransactionInfo trx = payment.getTrx();
|
||||
RecurrentPaymentData recurrentPaymentData = initRecurrentPaymentData(payment, paymentResource, trx);
|
||||
Map<String, String> adapterConfigurations = context.getOptions();
|
||||
return EntryStateModel.builder()
|
||||
.baseRequestModel(BaseRequestModel.builder().recurrentPaymentData(recurrentPaymentData)
|
||||
.mobilePaymentData(mobilePaymentDataBuilder.build())
|
||||
.cardData(cardDataBuilder.build())
|
||||
.refundData(initRefundData(paymentInfo))
|
||||
.paymentId(idGenerator.get(paymentInfo.getInvoice().getId()))
|
||||
.createdAt(paymentInfo.getPayment().getCreatedAt())
|
||||
.currency(payment.getCost().getCurrency().getSymbolicCode())
|
||||
.amount(payment.getCost().getAmount())
|
||||
.details(paymentInfo.getInvoice().getDetails().getDescription())
|
||||
.payerInfo(PayerInfo.builder()
|
||||
.ip(ProxyProviderPackageCreators.extractIpAddress(context))
|
||||
.build())
|
||||
.adapterConfigurations(adapterConfigurations)
|
||||
.providerTrxId(trx != null ? trx.getId() : temporaryContext.getProviderTrxId())
|
||||
.savedData(trx != null ? trx.getExtra() : new HashMap<>())
|
||||
.successRedirectUrl(getSuccessRedirectUrl(payment, adapterConfigurations))
|
||||
.build())
|
||||
.targetStatus(targetStatus)
|
||||
.currentStep(currentStep)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String getSuccessRedirectUrl(InvoicePayment payment, Map<String, String> adapterConfigurations) {
|
||||
return callbackUrlExtractor.getSuccessRedirectUrl(
|
||||
adapterConfigurations,
|
||||
payment.isSetPayerSessionInfo()
|
||||
? payment.getPayerSessionInfo().getRedirectUrl()
|
||||
: null);
|
||||
}
|
||||
|
||||
private void initPaymentData(PaymentContext context, PaymentResource paymentResource,
|
||||
MobilePaymentData.MobilePaymentDataBuilder<?, ?> mobilePaymentDataBuilder,
|
||||
dev.vality.adapter.flow.lib.model.CardData.CardDataBuilder<?, ?> cardDataBuilder,
|
||||
Step currentStep, TargetStatus targetStatus) {
|
||||
if (paymentResource.isSetDisposablePaymentResource()
|
||||
&& currentStep == null
|
||||
&& targetStatus == TargetStatus.PROCESSED) {
|
||||
SessionData sessionData = cdsStorage.getSessionData(context);
|
||||
if (sessionData.isSetAuthData() && sessionData.getAuthData().isSetAuth3ds()) {
|
||||
Auth3DS auth3ds = sessionData.getAuthData().getAuth3ds();
|
||||
mobilePaymentDataBuilder.cryptogram(auth3ds.getCryptogram())
|
||||
.eci(auth3ds.getEci());
|
||||
} else {
|
||||
CardDataProxyModel cardData = getCardData(context, paymentResource);
|
||||
cardDataBuilder.cardHolder(cardData.getCardholderName())
|
||||
.pan(cardData.getPan())
|
||||
.cvv2(CardDataUtils.extractCvv2(sessionData))
|
||||
.expYear(cardData.getExpYear())
|
||||
.expMonth(cardData.getExpMonth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RefundData initRefundData(PaymentInfo paymentInfo) {
|
||||
RefundData refundData = null;
|
||||
if (paymentInfo.isSetRefund()) {
|
||||
InvoicePaymentRefund refund = paymentInfo.getRefund();
|
||||
refundData = RefundData.builder()
|
||||
.id(refund.getId())
|
||||
.amount(refund.getCash().getAmount())
|
||||
.build();
|
||||
}
|
||||
return refundData;
|
||||
}
|
||||
|
||||
private RecurrentPaymentData initRecurrentPaymentData(InvoicePayment payment,
|
||||
PaymentResource paymentResource,
|
||||
TransactionInfo transactionInfo) {
|
||||
var recurrentPaymentDataBuilder = RecurrentPaymentData.builder()
|
||||
.makeRecurrent(payment.make_recurrent);
|
||||
if (paymentResource.isSetRecurrentPaymentResource()) {
|
||||
recurrentPaymentDataBuilder.recToken(paymentResource.getRecurrentPaymentResource().getRecToken());
|
||||
} else if (transactionInfo != null && transactionInfo.getExtra() != null
|
||||
&& transactionInfo.getExtra().containsKey(MetaData.META_REC_TOKEN)) {
|
||||
recurrentPaymentDataBuilder.recToken(transactionInfo.getExtra().get(MetaData.META_REC_TOKEN));
|
||||
}
|
||||
return recurrentPaymentDataBuilder.build();
|
||||
}
|
||||
|
||||
private CardDataProxyModel getCardData(PaymentContext context, PaymentResource paymentResource) {
|
||||
if (paymentResource.isSetDisposablePaymentResource()) {
|
||||
String cardToken = ProxyProviderPackageExtractors.extractBankCardToken(paymentResource);
|
||||
CardData cardData = cdsStorage.getCardData(cardToken);
|
||||
BankCard bankCard = ProxyProviderPackageExtractors.extractBankCard(context);
|
||||
return BankCardExtractor.initCardDataProxyModel(bankCard, cardData);
|
||||
}
|
||||
return cdsStorage.getCardData(context);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,125 @@
|
||||
package dev.vality.adapter.flow.lib.converter.entry;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.MetaData;
|
||||
import dev.vality.adapter.flow.lib.model.*;
|
||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
||||
import dev.vality.adapter.flow.lib.service.IdGenerator;
|
||||
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
||||
import dev.vality.adapter.flow.lib.utils.CardDataUtils;
|
||||
import dev.vality.cds.client.storage.CdsClientStorage;
|
||||
import dev.vality.cds.client.storage.utils.BankCardExtractor;
|
||||
import dev.vality.cds.storage.Auth3DS;
|
||||
import dev.vality.cds.storage.CardData;
|
||||
import dev.vality.cds.storage.SessionData;
|
||||
import dev.vality.damsel.domain.BankCard;
|
||||
import dev.vality.damsel.domain.DisposablePaymentResource;
|
||||
import dev.vality.damsel.domain.PaymentTool;
|
||||
import dev.vality.damsel.domain.TransactionInfo;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentPaymentTool;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenContext;
|
||||
import dev.vality.java.cds.utils.model.CardDataProxyModel;
|
||||
import dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators;
|
||||
import dev.vality.java.damsel.utils.extractors.ProxyProviderPackageExtractors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RecCtxToEntryModelConverter implements Converter<RecurrentTokenContext, EntryStateModel> {
|
||||
|
||||
private final TemporaryContextDeserializer temporaryContextDeserializer;
|
||||
private final CdsClientStorage cdsStorage;
|
||||
private final IdGenerator idGenerator;
|
||||
private final TemporaryContextService temporaryContextService;
|
||||
|
||||
@Override
|
||||
public EntryStateModel convert(RecurrentTokenContext context) {
|
||||
var generalExitStateModel = temporaryContextService.getTemporaryContext(
|
||||
context, temporaryContextDeserializer);
|
||||
var tokenInfo = context.getTokenInfo();
|
||||
var recurrentPaymentTool = tokenInfo.getPaymentTool();
|
||||
var paymentResource = recurrentPaymentTool.getPaymentResource();
|
||||
var paymentTool = paymentResource.getPaymentTool();
|
||||
validatePaymentTool(paymentTool);
|
||||
var entryStateModelBuilder =
|
||||
EntryStateModel.builder();
|
||||
var mobilePaymentDataBuilder = MobilePaymentData.builder();
|
||||
var cardDataBuilder = dev.vality.adapter.flow.lib.model.CardData.builder();
|
||||
initPaymentData(context, generalExitStateModel, paymentResource, mobilePaymentDataBuilder, cardDataBuilder);
|
||||
|
||||
TransactionInfo transactionInfo = tokenInfo.getTrx();
|
||||
Long orderId = idGenerator.get(tokenInfo.getPaymentTool().getId());
|
||||
return entryStateModelBuilder
|
||||
.baseRequestModel(BaseRequestModel.builder()
|
||||
.recurrentPaymentData(RecurrentPaymentData
|
||||
.builder()
|
||||
.makeRecurrent(true)
|
||||
.recToken(transactionInfo != null && transactionInfo.getExtra() != null
|
||||
? transactionInfo.getExtra().get(MetaData.META_REC_TOKEN)
|
||||
: null)
|
||||
.build())
|
||||
.mobilePaymentData(mobilePaymentDataBuilder.build())
|
||||
.cardData(cardDataBuilder.build())
|
||||
.refundData(initRefundData(recurrentPaymentTool, orderId))
|
||||
.paymentId(orderId)
|
||||
.currency(recurrentPaymentTool.getMinimalPaymentCost().getCurrency().getSymbolicCode())
|
||||
.amount(recurrentPaymentTool.getMinimalPaymentCost().getAmount())
|
||||
.details(recurrentPaymentTool.getId())
|
||||
.payerInfo(PayerInfo.builder()
|
||||
.ip(ProxyProviderPackageCreators.extractIpAddress(context))
|
||||
.build())
|
||||
.adapterConfigurations(context.getOptions())
|
||||
.providerTrxId(transactionInfo != null ? transactionInfo.getId() : null)
|
||||
.savedData(transactionInfo == null || transactionInfo.getExtra() == null
|
||||
? new HashMap<>()
|
||||
: transactionInfo.getExtra())
|
||||
.build())
|
||||
.currentStep(generalExitStateModel.getNextStep())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void initPaymentData(RecurrentTokenContext context, TemporaryContext generalExitStateModel,
|
||||
DisposablePaymentResource paymentResource,
|
||||
MobilePaymentData.MobilePaymentDataBuilder<?, ?> mobilePaymentDataBuilder,
|
||||
dev.vality.adapter.flow.lib.model.CardData.CardDataBuilder<?, ?> cardDataBuilder) {
|
||||
if (generalExitStateModel == null || generalExitStateModel.getNextStep() == null) {
|
||||
SessionData sessionData = cdsStorage.getSessionData(context);
|
||||
if (sessionData.getAuthData().isSetAuth3ds()) {
|
||||
Auth3DS auth3ds = sessionData.getAuthData().getAuth3ds();
|
||||
mobilePaymentDataBuilder.cryptogram(auth3ds.getCryptogram())
|
||||
.eci(auth3ds.getEci());
|
||||
} else {
|
||||
CardDataProxyModel cardData = getCardData(context, paymentResource);
|
||||
cardDataBuilder.cardHolder(cardData.getCardholderName())
|
||||
.pan(cardData.getPan())
|
||||
.cvv2(CardDataUtils.extractCvv2(sessionData))
|
||||
.expYear(cardData.getExpYear())
|
||||
.expMonth(cardData.getExpMonth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePaymentTool(PaymentTool paymentTool) {
|
||||
if (!paymentTool.isSetBankCard()) {
|
||||
throw new IllegalArgumentException("Wrong recurrentPaymentTool. It should be bank card");
|
||||
}
|
||||
}
|
||||
|
||||
private RefundData initRefundData(RecurrentPaymentTool recurrentPaymentTool, Long orderId) {
|
||||
return RefundData.builder()
|
||||
.id(String.valueOf(orderId))
|
||||
.amount(recurrentPaymentTool.getMinimalPaymentCost().getAmount())
|
||||
.build();
|
||||
}
|
||||
|
||||
private CardDataProxyModel getCardData(RecurrentTokenContext context, DisposablePaymentResource paymentResource) {
|
||||
String cardToken = ProxyProviderPackageExtractors.extractBankCardToken(paymentResource);
|
||||
CardData cardData = cdsStorage.getCardData(cardToken);
|
||||
BankCard bankCard = ProxyProviderPackageExtractors.extractBankCard(context);
|
||||
return BankCardExtractor.initCardDataProxyModel(bankCard, cardData);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package dev.vality.adapter.flow.lib.converter.exit;
|
||||
|
||||
import dev.vality.adapter.flow.lib.converter.ExitStateModelToTemporaryContextConverter;
|
||||
import dev.vality.adapter.flow.lib.flow.ResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextSerializer;
|
||||
import dev.vality.adapter.flow.lib.service.IntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.Intent;
|
||||
import dev.vality.damsel.proxy_provider.PaymentProxyResult;
|
||||
import dev.vality.java.damsel.utils.creators.DomainPackageCreators;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ExitModelToProxyResultConverter implements Converter<ExitStateModel, PaymentProxyResult> {
|
||||
|
||||
private final IntentResultFactory intentResultFactory;
|
||||
private final TemporaryContextSerializer serializer;
|
||||
private final ResultIntentResolver resultIntentResolver;
|
||||
private final ExitStateModelToTemporaryContextConverter contextConverter;
|
||||
|
||||
@Override
|
||||
public PaymentProxyResult convert(ExitStateModel exitStateModel) {
|
||||
if (StringUtils.hasText(exitStateModel.getErrorCode())) {
|
||||
return new PaymentProxyResult(intentResultFactory.createFinishIntentFailed(exitStateModel));
|
||||
}
|
||||
|
||||
Intent intent = resultIntentResolver.initIntentByStep(exitStateModel);
|
||||
|
||||
return new PaymentProxyResult(intent)
|
||||
.setNextState(serializer.writeByte(contextConverter.convert(exitStateModel)))
|
||||
.setTrx(DomainPackageCreators.createTransactionInfo(
|
||||
exitStateModel.getProviderTrxId(), exitStateModel.getTrxExtra())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
package dev.vality.adapter.flow.lib.converter.exit;
|
||||
|
||||
import dev.vality.adapter.flow.lib.converter.ExitStateModelToTemporaryContextConverter;
|
||||
import dev.vality.adapter.flow.lib.flow.RecurrentResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextSerializer;
|
||||
import dev.vality.adapter.flow.lib.service.RecurrentIntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenProxyResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import static dev.vality.java.damsel.utils.creators.DomainPackageCreators.createTransactionInfo;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ExitModelToRecTokenProxyResultConverter implements Converter<ExitStateModel, RecurrentTokenProxyResult> {
|
||||
|
||||
private final RecurrentIntentResultFactory recurrentIntentResultFactory;
|
||||
private final TemporaryContextSerializer serializer;
|
||||
private final RecurrentResultIntentResolver recurrentResultIntentResolver;
|
||||
private final ExitStateModelToTemporaryContextConverter contextConverter;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenProxyResult convert(ExitStateModel exitStateModel) {
|
||||
if (exitStateModel.getErrorCode() != null) {
|
||||
return new RecurrentTokenProxyResult(
|
||||
recurrentIntentResultFactory.createFinishIntentFailed(
|
||||
exitStateModel.getErrorCode(),
|
||||
exitStateModel.getErrorMessage())
|
||||
);
|
||||
}
|
||||
|
||||
RecurrentTokenIntent intent = recurrentResultIntentResolver.initIntentByStep(exitStateModel);
|
||||
|
||||
return new RecurrentTokenProxyResult(intent)
|
||||
.setNextState(serializer.writeByte(contextConverter.convert(exitStateModel)))
|
||||
.setTrx(createTransactionInfo(
|
||||
exitStateModel.getProviderTrxId(), exitStateModel.getTrxExtra())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package dev.vality.adapter.flow.lib.exception;
|
||||
|
||||
public class DataNotCorrespondStateException extends RuntimeException {
|
||||
|
||||
public DataNotCorrespondStateException() {
|
||||
}
|
||||
|
||||
public DataNotCorrespondStateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DataNotCorrespondStateException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DataNotCorrespondStateException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public DataNotCorrespondStateException(String message, Throwable cause, boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package dev.vality.adapter.flow.lib.exception;
|
||||
|
||||
public class DeserializationException extends RuntimeException {
|
||||
public DeserializationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DeserializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DeserializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public DeserializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package dev.vality.adapter.flow.lib.exception;
|
||||
|
||||
public class UnknownHandlerForStepException extends RuntimeException {
|
||||
public UnknownHandlerForStepException() {
|
||||
}
|
||||
|
||||
public UnknownHandlerForStepException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnknownHandlerForStepException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UnknownHandlerForStepException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public UnknownHandlerForStepException(String message, Throwable cause, boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package dev.vality.adapter.flow.lib.exception;
|
||||
|
||||
public class UnknownTargetStatusException extends RuntimeException {
|
||||
|
||||
public UnknownTargetStatusException() {
|
||||
super("Unknown target status!");
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package dev.vality.adapter.flow.lib.flow;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.StageFlowResolver;
|
||||
|
||||
public abstract class AbstractGenerateTokenStepResolver<T extends EntryStateModel, R extends ExitStateModel>
|
||||
implements StepResolver<T, R> {
|
||||
|
||||
@Override
|
||||
public Step resolveCurrentStep(EntryStateModel stateModel) {
|
||||
Step currentStep = stateModel.getCurrentStep();
|
||||
if (currentStep != null) {
|
||||
return currentStep;
|
||||
}
|
||||
return StageFlowResolver.isOneStageFlow(stateModel) ? Step.PAY : Step.AUTH;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package dev.vality.adapter.flow.lib.flow;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.StageFlowResolver;
|
||||
|
||||
public abstract class AbstractPaymentStepResolver<T extends EntryStateModel, R extends ExitStateModel>
|
||||
implements StepResolver<T, R> {
|
||||
|
||||
@Override
|
||||
public Step resolveCurrentStep(T entryStateModel) {
|
||||
Step currentStep = entryStateModel.getCurrentStep();
|
||||
if (currentStep != null) {
|
||||
return currentStep;
|
||||
}
|
||||
return switch (entryStateModel.getTargetStatus()) {
|
||||
case PROCESSED -> StageFlowResolver.isOneStageFlow(entryStateModel) ? Step.PAY : Step.AUTH;
|
||||
case CAPTURED -> StageFlowResolver.isOneStageFlow(entryStateModel) ? Step.DO_NOTHING : Step.CAPTURE;
|
||||
case CANCELLED -> Step.CANCEL;
|
||||
case REFUNDED -> Step.REFUND;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package dev.vality.adapter.flow.lib.flow;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
|
||||
public interface RecurrentResultIntentResolver {
|
||||
|
||||
RecurrentTokenIntent initIntentByStep(ExitStateModel exitStateModel);
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package dev.vality.adapter.flow.lib.flow;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.damsel.proxy_provider.Intent;
|
||||
|
||||
public interface ResultIntentResolver {
|
||||
|
||||
Intent initIntentByStep(ExitStateModel exitStateModel);
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package dev.vality.adapter.flow.lib.flow;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
|
||||
public interface StepResolver<T extends EntryStateModel, R extends ExitStateModel> {
|
||||
|
||||
Step resolveCurrentStep(T entryStateModel);
|
||||
|
||||
Step resolveNextStep(R exitStateModel);
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package dev.vality.adapter.flow.lib.flow.full;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.AbstractPaymentStepResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
|
||||
public class FullThreeDsAllVersionsStepResolverImpl
|
||||
extends AbstractPaymentStepResolver<EntryStateModel, ExitStateModel> {
|
||||
|
||||
@Override
|
||||
public Step resolveNextStep(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step step = entryStateModel.getCurrentStep();
|
||||
switch (step) {
|
||||
case AUTH, PAY:
|
||||
if (ThreeDsBranchResolver.isRedirectForThreeDsV1(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V1;
|
||||
} else if (ThreeDsBranchResolver.isRedirectForThreeDsV2Simple(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V2;
|
||||
} else if (ThreeDsBranchResolver.isRedirectForThreeDsV2Full(exitStateModel)) {
|
||||
return Step.CHECK_NEED_3DS_V2;
|
||||
} else {
|
||||
return Step.DO_NOTHING;
|
||||
}
|
||||
case CHECK_NEED_3DS_V2:
|
||||
if (ThreeDsBranchResolver.isRedirectForThreeDsV2Full(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V2;
|
||||
} else {
|
||||
return Step.DO_NOTHING;
|
||||
}
|
||||
case FINISH_THREE_DS_V1, FINISH_THREE_DS_V2, CANCEL, REFUND, CAPTURE:
|
||||
return Step.DO_NOTHING;
|
||||
default:
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package dev.vality.adapter.flow.lib.flow.full;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.AbstractGenerateTokenStepResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
|
||||
public class GenerateTokenFullThreeDsAllVersionsStepResolverImpl extends
|
||||
AbstractGenerateTokenStepResolver<EntryStateModel, ExitStateModel> {
|
||||
|
||||
@Override
|
||||
public Step resolveNextStep(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step step = entryStateModel.getCurrentStep();
|
||||
switch (step) {
|
||||
case AUTH, PAY:
|
||||
if (ThreeDsBranchResolver.isRedirectForThreeDsV1(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V1;
|
||||
} else if (ThreeDsBranchResolver.isRedirectForThreeDsV2Simple(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V2;
|
||||
} else if (ThreeDsBranchResolver.isRedirectForThreeDsV2Full(exitStateModel)) {
|
||||
return Step.CHECK_NEED_3DS_V2;
|
||||
} else {
|
||||
return Step.CAPTURE;
|
||||
}
|
||||
case CHECK_NEED_3DS_V2:
|
||||
if (ThreeDsBranchResolver.isRedirectForThreeDsV2Full(exitStateModel)) {
|
||||
return Step.FINISH_THREE_DS_V2;
|
||||
} else {
|
||||
return Step.CAPTURE;
|
||||
}
|
||||
case FINISH_THREE_DS_V1, FINISH_THREE_DS_V2:
|
||||
return Step.CAPTURE;
|
||||
case CAPTURE:
|
||||
return Step.REFUND;
|
||||
case REFUND:
|
||||
return Step.DO_NOTHING;
|
||||
default:
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package dev.vality.adapter.flow.lib.flow.full;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.RecurrentResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.service.RecurrentIntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GenerateTokenResultIntentResolverImpl implements RecurrentResultIntentResolver {
|
||||
|
||||
private final RecurrentIntentResultFactory recurrentIntentResultFactory;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenIntent initIntentByStep(ExitStateModel exitStateModel) {
|
||||
Step nextStep = exitStateModel.getNextStep();
|
||||
return switch (nextStep) {
|
||||
case REFUND, CAPTURE -> recurrentIntentResultFactory.createSleepIntentForReinvocation();
|
||||
case FINISH_THREE_DS_V1, FINISH_THREE_DS_V2 -> recurrentIntentResultFactory.createIntentWithSuspension(
|
||||
exitStateModel);
|
||||
case DO_NOTHING -> recurrentIntentResultFactory.createFinishIntent(exitStateModel.getRecToken());
|
||||
default -> throw new IllegalStateException("Wrong state: " + nextStep);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package dev.vality.adapter.flow.lib.flow.full;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.ResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.service.IntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.Intent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import static dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators.createFinishIntentSuccess;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ResultIntentResolverImpl implements ResultIntentResolver {
|
||||
|
||||
private final IntentResultFactory intentResultFactory;
|
||||
|
||||
@Override
|
||||
public Intent initIntentByStep(ExitStateModel exitStateModel) {
|
||||
Step nextStep = exitStateModel.getNextStep();
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step currentStep = entryStateModel.getCurrentStep();
|
||||
return switch (nextStep) {
|
||||
case FINISH_THREE_DS_V1, CHECK_NEED_3DS_V2, FINISH_THREE_DS_V2 -> intentResultFactory
|
||||
.createSuspendIntentWithFailedAfterTimeout(exitStateModel);
|
||||
case DO_NOTHING -> createIntentByCurrentStep(exitStateModel, currentStep);
|
||||
case CAPTURE, REFUND, CANCEL -> createFinishIntentSuccess();
|
||||
default -> throw new IllegalStateException("Wrong nextStep: " + nextStep);
|
||||
};
|
||||
}
|
||||
|
||||
private Intent createIntentByCurrentStep(ExitStateModel exitStateModel, Step currentStep) {
|
||||
return switch (currentStep) {
|
||||
case CHECK_NEED_3DS_V2, FINISH_THREE_DS_V1, FINISH_THREE_DS_V2,
|
||||
DO_NOTHING, PAY, AUTH, CAPTURE -> intentResultFactory
|
||||
.createFinishIntentSuccessWithCheckToken(exitStateModel);
|
||||
case REFUND, CANCEL -> createFinishIntentSuccess();
|
||||
default -> throw new IllegalStateException("Wrong currentStep: " + currentStep);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package dev.vality.adapter.flow.lib.flow.full;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.constant.ThreeDsType;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ThreeDsBranchResolver {
|
||||
|
||||
public static boolean isRedirectForThreeDsV2Full(ExitStateModel exitStateModel) {
|
||||
return isRedirectForThreeDsType(exitStateModel, ThreeDsType.V2_FULL);
|
||||
}
|
||||
|
||||
public static boolean isRedirectForThreeDsV2Simple(ExitStateModel exitStateModel) {
|
||||
return isRedirectForThreeDsType(exitStateModel, ThreeDsType.V2_SIMPLE);
|
||||
}
|
||||
|
||||
public static boolean isRedirectForThreeDsV1(ExitStateModel exitStateModel) {
|
||||
return isRedirectForThreeDsType(exitStateModel, ThreeDsType.V1);
|
||||
}
|
||||
|
||||
private static boolean isRedirectForThreeDsType(ExitStateModel exitStateModel, ThreeDsType threeDsType) {
|
||||
return exitStateModel.getLastOperationStatus() == Status.NEED_REDIRECT
|
||||
&& exitStateModel.getThreeDsData() != null
|
||||
&& exitStateModel.getThreeDsData().getThreeDsType() == threeDsType;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package dev.vality.adapter.flow.lib.flow.simple;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.AbstractGenerateTokenStepResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
|
||||
public class GenerateTokenSimpleRedirectWithPollingStepResolverImpl extends
|
||||
AbstractGenerateTokenStepResolver<EntryStateModel, ExitStateModel> {
|
||||
|
||||
@Override
|
||||
public Step resolveCurrentStep(EntryStateModel stateModel) {
|
||||
Step currentStep = stateModel.getCurrentStep();
|
||||
if (currentStep != null) {
|
||||
return currentStep;
|
||||
}
|
||||
return Step.GENERATE_TOKEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step resolveNextStep(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step step = entryStateModel.getCurrentStep();
|
||||
return switch (step) {
|
||||
case GENERATE_TOKEN -> Step.CHECK_STATUS;
|
||||
case CHECK_STATUS -> exitStateModel.getLastOperationStatus() == Status.NEED_RETRY
|
||||
? Step.CHECK_STATUS
|
||||
: Step.DO_NOTHING;
|
||||
default -> Step.DO_NOTHING;
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.flow.simple;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.RecurrentResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.service.RecurrentIntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleRedirectGenerateTokenResultIntentResolver implements RecurrentResultIntentResolver {
|
||||
|
||||
private final RecurrentIntentResultFactory recurrentIntentResultFactory;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenIntent initIntentByStep(ExitStateModel exitStateModel) {
|
||||
Step nextStep = exitStateModel.getNextStep();
|
||||
Step currentStep = exitStateModel.getEntryStateModel().getCurrentStep();
|
||||
return switch (nextStep) {
|
||||
case CHECK_STATUS -> currentStep == Step.GENERATE_TOKEN
|
||||
? recurrentIntentResultFactory.createIntentWithSuspension(exitStateModel)
|
||||
: recurrentIntentResultFactory.createSleepIntentWithExponentialPolling(exitStateModel);
|
||||
case DO_NOTHING -> recurrentIntentResultFactory.createFinishIntent(exitStateModel.getRecToken());
|
||||
default -> throw new IllegalStateException("Wrong state: " + nextStep);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package dev.vality.adapter.flow.lib.flow.simple;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.AbstractPaymentStepResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
|
||||
public class SimpleRedirectWIthPollingStepResolverImpl
|
||||
extends AbstractPaymentStepResolver<EntryStateModel, ExitStateModel> {
|
||||
|
||||
@Override
|
||||
public Step resolveNextStep(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step step = entryStateModel.getCurrentStep();
|
||||
return switch (step) {
|
||||
case AUTH, PAY, CAPTURE, REFUND, CANCEL -> Step.CHECK_STATUS;
|
||||
case CHECK_STATUS -> exitStateModel.getLastOperationStatus() == Status.NEED_RETRY
|
||||
? Step.CHECK_STATUS
|
||||
: Step.DO_NOTHING;
|
||||
default -> step;
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package dev.vality.adapter.flow.lib.flow.simple;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.flow.ResultIntentResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.service.IntentResultFactory;
|
||||
import dev.vality.damsel.proxy_provider.Intent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleRedirectWithPollingResultIntentResolver implements ResultIntentResolver {
|
||||
|
||||
private final IntentResultFactory intentResultFactory;
|
||||
|
||||
@Override
|
||||
public Intent initIntentByStep(ExitStateModel exitStateModel) {
|
||||
Step nextStep = exitStateModel.getNextStep();
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
Step currentStep = entryStateModel.getCurrentStep();
|
||||
return switch (nextStep) {
|
||||
case CHECK_STATUS -> exitStateModel.getLastOperationStatus() == Status.NEED_REDIRECT
|
||||
? intentResultFactory.createSuspendIntentWithCallbackAfterTimeout(exitStateModel)
|
||||
: intentResultFactory.createSleepIntentWithExponentialPolling(exitStateModel);
|
||||
case DO_NOTHING -> createIntentByCurrentStep(exitStateModel, currentStep);
|
||||
case REFUND, CANCEL -> intentResultFactory.createFinishIntentSuccess();
|
||||
default -> throw new IllegalStateException("Wrong nextStep: " + nextStep);
|
||||
};
|
||||
}
|
||||
|
||||
private Intent createIntentByCurrentStep(ExitStateModel exitStateModel, Step currentStep) {
|
||||
return switch (currentStep) {
|
||||
case CHECK_STATUS, CHECK_NEED_3DS_V2, FINISH_THREE_DS_V1, FINISH_THREE_DS_V2, DO_NOTHING,
|
||||
PAY, AUTH -> intentResultFactory.createFinishIntentSuccessWithCheckToken(exitStateModel);
|
||||
case REFUND, CANCEL -> intentResultFactory.createFinishIntentSuccess();
|
||||
default -> throw new IllegalStateException("Wrong currentStep: " + currentStep);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
public interface CommonHandler<T, E> {
|
||||
|
||||
boolean isHandle(E model);
|
||||
|
||||
T handle(E context) throws TException;
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class CommonHandlerImpl<P, R, E, T> implements CommonHandler<T, E> {
|
||||
|
||||
private final Function<P, R> requestFunction;
|
||||
private final Converter<E, P> converter;
|
||||
private final Processor<T, R, E> processor;
|
||||
|
||||
@Override
|
||||
public T handle(E entryStateModel) {
|
||||
P request = converter.convert(entryStateModel);
|
||||
R response = requestFunction.apply(request);
|
||||
return processor.process(response, entryStateModel);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import dev.vality.adapter.flow.lib.handler.callback.CallbackHandler;
|
||||
import dev.vality.adapter.flow.lib.validator.AdapterConfigurationValidator;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ProxyProviderServiceImpl implements ProviderProxySrv.Iface {
|
||||
|
||||
private final CallbackHandler<PaymentCallbackResult, PaymentContext> paymentCallbackHandler;
|
||||
private final CallbackHandler<RecurrentTokenCallbackResult, RecurrentTokenContext> recurrentTokenCallbackHandler;
|
||||
private final ServerFlowHandler<PaymentContext, PaymentProxyResult> serverFlowHandler;
|
||||
private final ServerFlowHandler<RecurrentTokenContext, RecurrentTokenProxyResult> generateTokenFlowHandler;
|
||||
private final AdapterConfigurationValidator adapterConfigurationValidator;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenProxyResult generateToken(RecurrentTokenContext context) throws TException {
|
||||
adapterConfigurationValidator.validate(context.getOptions());
|
||||
return generateTokenFlowHandler.handle(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecurrentTokenCallbackResult handleRecurrentTokenCallback(
|
||||
ByteBuffer byteBuffer,
|
||||
RecurrentTokenContext context
|
||||
) throws TException {
|
||||
return recurrentTokenCallbackHandler.handleCallback(byteBuffer, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentProxyResult processPayment(PaymentContext context) throws TException {
|
||||
adapterConfigurationValidator.validate(context.getOptions());
|
||||
return serverFlowHandler.handle(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentCallbackResult handlePaymentCallback(ByteBuffer byteBuffer, PaymentContext context)
|
||||
throws TException {
|
||||
return paymentCallbackHandler.handleCallback(byteBuffer, context);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import dev.vality.adapter.flow.lib.exception.UnknownHandlerForStepException;
|
||||
import dev.vality.adapter.flow.lib.flow.StepResolver;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.thrift.TException;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ServerFlowHandler<T, R> {
|
||||
|
||||
private final List<CommonHandler<ExitStateModel, EntryStateModel>> handlers;
|
||||
private final StepResolver<EntryStateModel, ExitStateModel> stepResolver;
|
||||
private final Converter<T, EntryStateModel> entryConverter;
|
||||
private final Converter<ExitStateModel, R> exitConverter;
|
||||
|
||||
public R handle(T context) throws TException {
|
||||
var entryStateModel = prepareEntryState(entryConverter, context);
|
||||
log.info("EntryStateModel: {}", entryStateModel);
|
||||
var exitStateModel = handlers.stream()
|
||||
.filter(handler -> handler.isHandle(entryStateModel))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new UnknownHandlerForStepException("Can't find handler to data: " + entryStateModel))
|
||||
.handle(entryStateModel);
|
||||
log.info("ExitStateModel: {}", exitStateModel);
|
||||
exitStateModel.setEntryStateModel(entryStateModel);
|
||||
exitStateModel.setNextStep(stepResolver.resolveNextStep(exitStateModel));
|
||||
log.info("Step changing: {} -> {}", entryStateModel.getCurrentStep(), exitStateModel.getNextStep());
|
||||
return exitConverter.convert(exitStateModel);
|
||||
}
|
||||
|
||||
private EntryStateModel prepareEntryState(Converter<T, EntryStateModel> entryConverter, T context) {
|
||||
EntryStateModel entryStateModel = entryConverter.convert(context);
|
||||
entryStateModel.setCurrentStep(stepResolver.resolveCurrentStep(entryStateModel));
|
||||
return entryStateModel;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import dev.vality.adapter.flow.lib.utils.PaymentResourceTypeResolver;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import dev.vality.java.damsel.utils.extractors.ProxyProviderPackageExtractors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static dev.vality.java.damsel.utils.verification.ProxyProviderVerification.isUndefinedResultOrUnavailable;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ServerHandlerLogDecorator implements ProviderProxySrv.Iface {
|
||||
|
||||
private final ProviderProxySrv.Iface handler;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenProxyResult generateToken(RecurrentTokenContext context) throws TException {
|
||||
String recurrentId = ProxyProviderPackageExtractors.extractRecurrentId(context);
|
||||
log.info("Generate token started with recurrentId {}", recurrentId);
|
||||
try {
|
||||
RecurrentTokenProxyResult proxyResult = handler.generateToken(context);
|
||||
log.info("Generate token finished {} with recurrentId {}", proxyResult, recurrentId);
|
||||
return proxyResult;
|
||||
} catch (Exception ex) {
|
||||
String message = "Failed handle generate token with recurrentId " + recurrentId;
|
||||
logMessage(ex, message);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecurrentTokenCallbackResult handleRecurrentTokenCallback(ByteBuffer byteBuffer,
|
||||
RecurrentTokenContext context) throws TException {
|
||||
String recurrentId = ProxyProviderPackageExtractors.extractRecurrentId(context);
|
||||
log.info("handleRecurrentTokenCallback: start with recurrentId {}", recurrentId);
|
||||
RecurrentTokenCallbackResult result = handler.handleRecurrentTokenCallback(byteBuffer, context);
|
||||
log.info("handleRecurrentTokenCallback end {} with recurrentId {}", result, recurrentId);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentProxyResult processPayment(PaymentContext context) throws TException {
|
||||
String invoiceId = ProxyProviderPackageExtractors.extractInvoiceId(context);
|
||||
String invoicePaymentStatus = ProxyProviderPackageExtractors.extractTargetInvoicePaymentStatus(context);
|
||||
String paymentResourceType = PaymentResourceTypeResolver.extractPaymentResourceType(context);
|
||||
log.info("Process payment handle resource='{}', status='{}' start with invoiceId {}", paymentResourceType,
|
||||
invoicePaymentStatus, invoiceId);
|
||||
try {
|
||||
PaymentProxyResult proxyResult = handler.processPayment(context);
|
||||
log.info("Process payment handle resource='{}', status='{}' finished with invoiceId {} and proxyResult {}",
|
||||
paymentResourceType, invoicePaymentStatus, invoiceId, proxyResult);
|
||||
return proxyResult;
|
||||
} catch (Exception e) {
|
||||
String message = String.format(
|
||||
"Failed handle resource=%s, status=%s process payment for operation with invoiceId %s",
|
||||
paymentResourceType, invoicePaymentStatus, invoiceId);
|
||||
logMessage(e, message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentCallbackResult handlePaymentCallback(ByteBuffer byteBuffer,
|
||||
PaymentContext context) throws TException {
|
||||
String invoiceId = ProxyProviderPackageExtractors.extractInvoiceId(context);
|
||||
log.info("handlePaymentCallback start with invoiceId {}", invoiceId);
|
||||
PaymentCallbackResult result = handler.handlePaymentCallback(byteBuffer, context);
|
||||
log.info("handlePaymentCallback finish {} with invoiceId {}", result, invoiceId);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void logMessage(Exception ex, String message) {
|
||||
if (isUndefinedResultOrUnavailable(ex)) {
|
||||
log.warn(message, ex);
|
||||
} else {
|
||||
log.error(message, ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package dev.vality.adapter.flow.lib.handler;
|
||||
|
||||
import dev.vality.adapter.flow.lib.logback.mdc.MdcContext;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.thrift.TException;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ServerHandlerMdcDecorator implements ProviderProxySrv.Iface {
|
||||
|
||||
private final ProviderProxySrv.Iface serverHandlerLogDecorator;
|
||||
|
||||
public RecurrentTokenProxyResult generateToken(RecurrentTokenContext recurrentTokenContext) throws TException {
|
||||
MdcContext.mdcPutContext(recurrentTokenContext);
|
||||
try {
|
||||
return serverHandlerLogDecorator.generateToken(recurrentTokenContext);
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecurrentTokenCallbackResult handleRecurrentTokenCallback(ByteBuffer byteBuffer,
|
||||
RecurrentTokenContext recurrentTokenContext)
|
||||
throws TException {
|
||||
MdcContext.mdcPutContext(recurrentTokenContext);
|
||||
try {
|
||||
return serverHandlerLogDecorator.handleRecurrentTokenCallback(byteBuffer, recurrentTokenContext);
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentProxyResult processPayment(PaymentContext paymentContext) throws TException {
|
||||
MdcContext.mdcPutContext(paymentContext);
|
||||
try {
|
||||
return serverHandlerLogDecorator.processPayment(paymentContext);
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentCallbackResult handlePaymentCallback(ByteBuffer byteBuffer, PaymentContext paymentContext)
|
||||
throws TException {
|
||||
MdcContext.mdcPutContext(paymentContext);
|
||||
try {
|
||||
return serverHandlerLogDecorator.handlePaymentCallback(byteBuffer, paymentContext);
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.vality.adapter.flow.lib.handler.callback;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface CallbackHandler<T, R> {
|
||||
|
||||
T handleCallback(ByteBuffer byteBuffer, R context);
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package dev.vality.adapter.flow.lib.handler.callback;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.TemporaryContext;
|
||||
import dev.vality.adapter.flow.lib.serde.Deserializer;
|
||||
import dev.vality.adapter.flow.lib.serde.StateSerializer;
|
||||
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
||||
import dev.vality.damsel.proxy_provider.PaymentCallbackProxyResult;
|
||||
import dev.vality.damsel.proxy_provider.PaymentCallbackResult;
|
||||
import dev.vality.damsel.proxy_provider.PaymentContext;
|
||||
import dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentCallbackHandler implements CallbackHandler<PaymentCallbackResult, PaymentContext> {
|
||||
|
||||
private final Deserializer<TemporaryContext> temporaryContextDeserializer;
|
||||
private final StateSerializer<TemporaryContext> adapterSerializer;
|
||||
private final TemporaryContextService temporaryContextService;
|
||||
|
||||
public PaymentCallbackResult handleCallback(ByteBuffer callback, PaymentContext context) {
|
||||
var generalExitStateModel = initTemporaryContext(callback, context);
|
||||
byte[] callbackResponse = new byte[0];
|
||||
return ProxyProviderPackageCreators.createCallbackResult(callbackResponse, (new PaymentCallbackProxyResult())
|
||||
.setIntent(ProxyProviderPackageCreators.createIntentWithSleepIntent(0))
|
||||
.setNextState(this.adapterSerializer.writeByte(generalExitStateModel)));
|
||||
}
|
||||
|
||||
private TemporaryContext initTemporaryContext(ByteBuffer callback, PaymentContext context) {
|
||||
var temporaryContext = temporaryContextService.getTemporaryContext(context, this.temporaryContextDeserializer);
|
||||
return temporaryContextService.appendThreeDsParametersToContext(callback, temporaryContext);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package dev.vality.adapter.flow.lib.handler.callback;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.TemporaryContext;
|
||||
import dev.vality.adapter.flow.lib.serde.Deserializer;
|
||||
import dev.vality.adapter.flow.lib.serde.StateSerializer;
|
||||
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenCallbackResult;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenContext;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenProxyResult;
|
||||
import dev.vality.java.damsel.utils.creators.BasePackageCreators;
|
||||
import dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RecurrentTokenCallbackHandler
|
||||
implements CallbackHandler<RecurrentTokenCallbackResult, RecurrentTokenContext> {
|
||||
|
||||
private final Deserializer<TemporaryContext> adapterDeserializer;
|
||||
private final StateSerializer<TemporaryContext> adapterSerializer;
|
||||
private final TemporaryContextService temporaryContextService;
|
||||
|
||||
public RecurrentTokenCallbackResult handleCallback(ByteBuffer callback, RecurrentTokenContext context) {
|
||||
var generalExitStateModel = initTemporaryContext(callback, context);
|
||||
byte[] callbackResponse = new byte[0];
|
||||
return ProxyProviderPackageCreators.createRecurrentTokenCallbackResult(callbackResponse,
|
||||
(new RecurrentTokenProxyResult()).setIntent(RecurrentTokenIntent
|
||||
.sleep(ProxyProviderPackageCreators.createSleepIntent(
|
||||
BasePackageCreators.createTimerTimeout(0))))
|
||||
.setNextState(this.adapterSerializer.writeByte(generalExitStateModel))
|
||||
);
|
||||
}
|
||||
|
||||
private TemporaryContext initTemporaryContext(ByteBuffer callback, RecurrentTokenContext context) {
|
||||
var temporaryContext = temporaryContextService.getTemporaryContext(context, this.adapterDeserializer);
|
||||
return temporaryContextService.appendThreeDsParametersToContext(callback, temporaryContext);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
|
||||
public class AuthHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public AuthHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::auth, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.AUTH == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class CancelHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public CancelHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::cancel, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.CANCEL == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class CaptureHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public CaptureHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::capture, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.CAPTURE == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class Check3dsV2Handler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public Check3dsV2Handler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::check3dsV2, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.CHECK_NEED_3DS_V2 == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
|
||||
public class DoNothingHandler implements CommonHandler<ExitStateModel, EntryStateModel> {
|
||||
|
||||
@Override
|
||||
public ExitStateModel handle(EntryStateModel entryStateModel) throws TException {
|
||||
var exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setEntryStateModel(entryStateModel);
|
||||
exitStateModel.setProviderTrxId(entryStateModel.getBaseRequestModel().getProviderTrxId());
|
||||
exitStateModel.setNextStep(Step.DO_NOTHING);
|
||||
exitStateModel.setTrxExtra(entryStateModel.getBaseRequestModel().getSavedData());
|
||||
if (entryStateModel.getBaseRequestModel().getRecurrentPaymentData().isMakeRecurrent()) {
|
||||
exitStateModel.setRecToken(entryStateModel.getBaseRequestModel().getRecurrentPaymentData().getRecToken());
|
||||
}
|
||||
return exitStateModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel model) {
|
||||
return model.getCurrentStep() == Step.DO_NOTHING;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class Finish3dsHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public Finish3dsHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::finish3ds, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.FINISH_THREE_DS_V1 == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class Finish3dsV2Handler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public Finish3dsV2Handler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::finish3dsV2, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.FINISH_THREE_DS_V2 == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
|
||||
public class GenerateTokenHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public GenerateTokenHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::generateToken, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.GENERATE_TOKEN == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class PaymentHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public PaymentHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::pay, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.PAY == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class RefundHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public RefundHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::refund, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.REFUND == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.handler.payment;
|
||||
|
||||
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.handler.CommonHandlerImpl;
|
||||
import dev.vality.adapter.flow.lib.model.BaseRequestModel;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.processor.Processor;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
public class StatusHandler
|
||||
extends CommonHandlerImpl<BaseRequestModel, BaseResponseModel, EntryStateModel, ExitStateModel> {
|
||||
|
||||
public StatusHandler(
|
||||
RemoteClient client,
|
||||
Converter<EntryStateModel, BaseRequestModel> converter,
|
||||
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> responseProcessorChain
|
||||
) {
|
||||
super(client::status, converter, responseProcessorChain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHandle(EntryStateModel entryStateModel) {
|
||||
return Step.CHECK_STATUS == entryStateModel.getCurrentStep();
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package dev.vality.adapter.flow.lib.logback.mask;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.IThrowableProxy;
|
||||
import ch.qos.logback.classic.spi.LoggerContextVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Marker;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MaskedEvent implements ILoggingEvent {
|
||||
|
||||
private final ILoggingEvent event;
|
||||
private final String message;
|
||||
|
||||
@Override
|
||||
public String getThreadName() {
|
||||
return event.getThreadName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
return event.getLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return event.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArgumentArray() {
|
||||
return new Object[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoggerName() {
|
||||
return event.getLoggerName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggerContextVO getLoggerContextVO() {
|
||||
return event.getLoggerContextVO();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IThrowableProxy getThrowableProxy() {
|
||||
return event.getThrowableProxy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackTraceElement[] getCallerData() {
|
||||
return new StackTraceElement[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCallerData() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Marker getMarker() {
|
||||
return event.getMarker();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMDCPropertyMap() {
|
||||
return event.getMDCPropertyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMdc() {
|
||||
return event.getMDCPropertyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeStamp() {
|
||||
return event.getTimeStamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForDeferredProcessing() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package dev.vality.adapter.flow.lib.logback.mask;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MaskingMessageWithPattern {
|
||||
|
||||
public static String maskMessage(String message, Pattern multilinePattern) {
|
||||
if (multilinePattern == null) {
|
||||
return message;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(message);
|
||||
Matcher matcher = multilinePattern.matcher(sb);
|
||||
while (matcher.find()) {
|
||||
IntStream.rangeClosed(1, matcher.groupCount())
|
||||
.filter(group -> matcher.group(group) != null)
|
||||
.forEach(
|
||||
group -> IntStream.range(matcher.start(group), matcher.end(group))
|
||||
.forEach(i -> sb.setCharAt(i, '*'))
|
||||
);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package dev.vality.adapter.flow.lib.logback.mask;
|
||||
|
||||
import ch.qos.logback.classic.PatternLayout;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PatternMaskingLayout extends PatternLayout {
|
||||
|
||||
private Pattern multilinePattern;
|
||||
private List<String> maskPatterns = new ArrayList<>();
|
||||
private static final String DELIMITER = "|";
|
||||
|
||||
public void addMaskPattern(String maskPattern) {
|
||||
maskPatterns.add(maskPattern);
|
||||
multilinePattern = Pattern.compile(String.join(DELIMITER, maskPatterns), Pattern.MULTILINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String doLayout(ILoggingEvent event) {
|
||||
return super.doLayout(new MaskedEvent(event,
|
||||
MaskingMessageWithPattern.maskMessage(event.getFormattedMessage(), multilinePattern)));
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package dev.vality.adapter.flow.lib.logback.mask;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import net.logstash.logback.composite.AbstractFieldJsonProvider;
|
||||
import net.logstash.logback.composite.FieldNamesAware;
|
||||
import net.logstash.logback.composite.JsonWritingUtils;
|
||||
import net.logstash.logback.fieldnames.LogstashFieldNames;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PatternMaskingMessageJsonProvider extends AbstractFieldJsonProvider<ILoggingEvent>
|
||||
implements FieldNamesAware<LogstashFieldNames> {
|
||||
private Pattern multilinePattern;
|
||||
private List<String> maskPatterns = new ArrayList<>();
|
||||
private static final String FIELD_MESSAGE = "message";
|
||||
private static final String DELIMITER = "|";
|
||||
|
||||
public void addMaskPattern(String maskPattern) {
|
||||
maskPatterns.add(maskPattern);
|
||||
multilinePattern = Pattern.compile(String.join(DELIMITER, maskPatterns), Pattern.MULTILINE);
|
||||
}
|
||||
|
||||
public PatternMaskingMessageJsonProvider() {
|
||||
setFieldName(FIELD_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
|
||||
JsonWritingUtils.writeStringField(generator, getFieldName(),
|
||||
MaskingMessageWithPattern.maskMessage(event.getFormattedMessage(), multilinePattern)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFieldNames(LogstashFieldNames fieldNames) {
|
||||
setFieldName(fieldNames.getMessage());
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package dev.vality.adapter.flow.lib.logback.mdc;
|
||||
|
||||
import dev.vality.damsel.domain.TransactionInfo;
|
||||
import dev.vality.damsel.proxy_provider.PaymentContext;
|
||||
import dev.vality.damsel.proxy_provider.RecurrentTokenContext;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MdcContext {
|
||||
|
||||
public static void mdcPutContext(RecurrentTokenContext context, String[] fieldsToPutInMdc) {
|
||||
TransactionInfo transactionInfo = context.getTokenInfo().getTrx();
|
||||
mdcPutContextTransactionInfo(transactionInfo, fieldsToPutInMdc);
|
||||
}
|
||||
|
||||
public static void mdcPutContext(PaymentContext context, String[] fieldsToPutInMdc) {
|
||||
TransactionInfo transactionInfo = context.getPaymentInfo().getPayment().getTrx();
|
||||
mdcPutContextTransactionInfo(transactionInfo, fieldsToPutInMdc);
|
||||
}
|
||||
|
||||
public static void mdcPutContext(PaymentContext context) {
|
||||
TransactionInfo transactionInfo = context.getPaymentInfo().getPayment().getTrx();
|
||||
mdcPutContextTransactionInfo(transactionInfo);
|
||||
}
|
||||
|
||||
public static void mdcPutContext(RecurrentTokenContext context) {
|
||||
TransactionInfo transactionInfo = context.getTokenInfo().getTrx();
|
||||
mdcPutContextTransactionInfo(transactionInfo);
|
||||
}
|
||||
|
||||
public static void mdcPutContextTransactionInfo(TransactionInfo transactionInfo, String[] fieldsToPutInMdc) {
|
||||
if (transactionInfo != null) {
|
||||
Map<String, String> trxextra = transactionInfo.getExtra();
|
||||
for (String field : fieldsToPutInMdc) {
|
||||
MDC.put(field, trxextra.get(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void mdcPutContextTransactionInfo(TransactionInfo transactionInfo) {
|
||||
if (transactionInfo != null) {
|
||||
Map<String, String> trxextra = transactionInfo.getExtra();
|
||||
String maskPattern = "\\b\\d{6}([\\d\\s]{2,9})\\d{4}\\b";
|
||||
Pattern pattern = Pattern.compile(maskPattern);
|
||||
for (Map.Entry<String, String> extra : trxextra.entrySet()) {
|
||||
if (pattern.matcher(extra.getValue()).find()) {
|
||||
MDC.put(extra.getKey(), extra.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void mdcRemoveContext(String[] fieldsToPutInMdc) {
|
||||
for (String field : fieldsToPutInMdc) {
|
||||
MDC.remove(field);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base class with all needed fields for pay and other operations
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BaseRequestModel {
|
||||
|
||||
/**
|
||||
* Uniq Long identifier for payment.
|
||||
*/
|
||||
private Long paymentId;
|
||||
/**
|
||||
* Uniq identifier from provider (if exists), not come in first methods.
|
||||
*/
|
||||
private String providerTrxId;
|
||||
/**
|
||||
* Amount in Long format and in minimum payment units (example: cents).
|
||||
*/
|
||||
private Long amount;
|
||||
/**
|
||||
* Currency in symbolic formats (example: "USD").
|
||||
*/
|
||||
private String currency;
|
||||
/**
|
||||
* Timestamp RFC 3339.
|
||||
*
|
||||
* The string must contain the date and time in UTC in the following format:
|
||||
* `2016-03-22T06:12:27Z`
|
||||
*/
|
||||
private String createdAt;
|
||||
/**
|
||||
* Description payment from merchant.
|
||||
*/
|
||||
private String details;
|
||||
/**
|
||||
* Card data, can be empty on some steps and recurrent or mobile operations.
|
||||
*/
|
||||
private CardData cardData;
|
||||
/**
|
||||
* Info about payer, send from merchant and can be empty.
|
||||
*/
|
||||
private PayerInfo payerInfo;
|
||||
/**
|
||||
* Data for mobile pay methods.
|
||||
*/
|
||||
private MobilePaymentData mobilePaymentData;
|
||||
/**
|
||||
* Data for recurrent payments.
|
||||
*/
|
||||
private RecurrentPaymentData recurrentPaymentData;
|
||||
/**
|
||||
* Full needed for refund data.
|
||||
*/
|
||||
private RefundData refundData;
|
||||
/**
|
||||
* The URL to which to redirect if the 3ds stream completed successfully.
|
||||
*/
|
||||
private String successRedirectUrl;
|
||||
/**
|
||||
* The URL to which to redirect if the 3ds stream completed fails.
|
||||
*/
|
||||
private String failedRedirectUrl;
|
||||
/**
|
||||
* All static parameters from configuration by support team.
|
||||
* (options - in old naming)
|
||||
*/
|
||||
private Map<String, String> adapterConfigurations;
|
||||
/**
|
||||
* Data that you send to save on previous steps.
|
||||
*/
|
||||
private Map<String, String> savedData;
|
||||
/**
|
||||
* Data that comes in a callback from mpi after the 3ds step.
|
||||
* Parameter names can be unique to your implementation, only you can know.
|
||||
*/
|
||||
private Map<String, String> threeDsDataFromMpiCallback;
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public class BaseResponseModel {
|
||||
|
||||
/**
|
||||
* Result status operation, by this status flow choose stop, retry or redirect branch.
|
||||
*/
|
||||
private Status status;
|
||||
/**
|
||||
* Error code from external system or your implementation
|
||||
*/
|
||||
private String errorCode;
|
||||
/**
|
||||
* Error description
|
||||
*/
|
||||
private String errorMessage;
|
||||
/**
|
||||
* Uniq identifier from provider (if exists).
|
||||
*/
|
||||
private String providerTrxId;
|
||||
/**
|
||||
* Token for pay in recurrent operations.
|
||||
*/
|
||||
private String recurrentToken;
|
||||
/**
|
||||
* Data that you want to use in next steps.
|
||||
*/
|
||||
private Map<String, String> saveData;
|
||||
/**
|
||||
* Data for choose 3ds flow and parameters for redirects.
|
||||
*/
|
||||
private ThreeDsData threeDsData;
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CardData {
|
||||
|
||||
@ToString.Exclude
|
||||
private String pan;
|
||||
@ToString.Exclude
|
||||
private Byte expMonth;
|
||||
@ToString.Exclude
|
||||
private Short expYear;
|
||||
@ToString.Exclude
|
||||
private String cvv2;
|
||||
@ToString.Exclude
|
||||
private String cardHolder;
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import dev.vality.adapter.flow.lib.constant.TargetStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EntryStateModel {
|
||||
|
||||
private BaseRequestModel baseRequestModel;
|
||||
|
||||
private Step currentStep;
|
||||
private PollingInfo startedPollingInfo;
|
||||
private TargetStatus targetStatus;
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ExitStateModel {
|
||||
|
||||
private Status lastOperationStatus;
|
||||
|
||||
private String errorCode;
|
||||
private String errorMessage;
|
||||
private Step nextStep;
|
||||
private EntryStateModel entryStateModel;
|
||||
|
||||
private String providerTrxId;
|
||||
private Map<String, String> trxExtra;
|
||||
private PollingInfo pollingInfo;
|
||||
private ThreeDsData threeDsData;
|
||||
|
||||
private String recToken;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MobilePaymentData {
|
||||
|
||||
private String tokenProvider;
|
||||
private String cryptogram;
|
||||
private String eci;
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PayerInfo {
|
||||
|
||||
private String ip;
|
||||
private String email;
|
||||
private String phone;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class PollingInfo {
|
||||
|
||||
@JsonProperty(value = "start_date_time_polling")
|
||||
private Instant startDateTimePolling;
|
||||
|
||||
@JsonProperty(value = "max_date_time_polling")
|
||||
private Instant maxDateTimePolling;
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RecurrentPaymentData {
|
||||
|
||||
private String recToken;
|
||||
private boolean makeRecurrent;
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RefundData {
|
||||
|
||||
private Long amount;
|
||||
private String id;
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Step;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TemporaryContext {
|
||||
|
||||
private Step nextStep;
|
||||
private String providerTrxId;
|
||||
private PollingInfo pollingInfo;
|
||||
private Map<String, String> threeDsData;
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package dev.vality.adapter.flow.lib.model;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.ThreeDsType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ThreeDsData {
|
||||
|
||||
/**
|
||||
* Use type to select desired 3ds flow.
|
||||
*/
|
||||
private ThreeDsType threeDsType;
|
||||
/**
|
||||
* Url for redirect user.
|
||||
*/
|
||||
private String acsUrl;
|
||||
/**
|
||||
* Parameters that would send in redirect request.
|
||||
* Name and value of parameters don't change and forward send in your format.
|
||||
*/
|
||||
private Map<String, String> parameters;
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package dev.vality.adapter.flow.lib.processor;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.ErrorUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ErrorProcessor implements Processor<ExitStateModel, BaseResponseModel, EntryStateModel> {
|
||||
|
||||
@Override
|
||||
public ExitStateModel process(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||
log.debug("Start error process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
|
||||
if (response != null && ErrorUtils.isError(response)) {
|
||||
ExitStateModel exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setErrorCode(String.valueOf(response.getErrorCode()));
|
||||
exitStateModel.setErrorMessage(response.getErrorMessage());
|
||||
log.debug("Finish error process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
return exitStateModel;
|
||||
}
|
||||
|
||||
ExitStateModel exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setErrorCode("unknown_error");
|
||||
exitStateModel.setErrorMessage("Unknown error reason!");
|
||||
exitStateModel.setEntryStateModel(entryStateModel);
|
||||
|
||||
log.debug("Finish error process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
return exitStateModel;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package dev.vality.adapter.flow.lib.processor;
|
||||
|
||||
public interface Processor<R, T, E> {
|
||||
|
||||
R process(T response, E context);
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package dev.vality.adapter.flow.lib.processor;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.ErrorUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RedirectProcessor implements Processor<ExitStateModel, BaseResponseModel, EntryStateModel> {
|
||||
|
||||
private final Processor<ExitStateModel, BaseResponseModel, EntryStateModel> nextProcessor;
|
||||
|
||||
@Override
|
||||
public ExitStateModel process(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||
if (response.getStatus() == Status.NEED_REDIRECT
|
||||
&& !ErrorUtils.isError(response)
|
||||
&& response.getThreeDsData() != null
|
||||
&& response.getThreeDsData().getThreeDsType() != null) {
|
||||
log.debug("Start redirect process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
ExitStateModel exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setThreeDsData(response.getThreeDsData());
|
||||
exitStateModel.setLastOperationStatus(response.getStatus());
|
||||
exitStateModel.setProviderTrxId(response.getProviderTrxId());
|
||||
log.debug("Finish redirect process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
return exitStateModel;
|
||||
}
|
||||
|
||||
if (nextProcessor != null) {
|
||||
return nextProcessor.process(response, entryStateModel);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Processor didn't match for response " + response);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package dev.vality.adapter.flow.lib.processor;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.ErrorUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RetryProcessor implements Processor<ExitStateModel, BaseResponseModel, EntryStateModel> {
|
||||
|
||||
private final Processor<ExitStateModel, BaseResponseModel, EntryStateModel> nextProcessor;
|
||||
|
||||
@Override
|
||||
public ExitStateModel process(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||
if (response.getStatus() == Status.NEED_RETRY
|
||||
&& !ErrorUtils.isError(response)) {
|
||||
log.debug("Start redirect process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
ExitStateModel exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setLastOperationStatus(response.getStatus());
|
||||
log.debug("Finish redirect process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
return exitStateModel;
|
||||
}
|
||||
|
||||
if (nextProcessor != null) {
|
||||
return nextProcessor.process(response, entryStateModel);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Processor didn't match for response " + response);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package dev.vality.adapter.flow.lib.processor;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.MetaData;
|
||||
import dev.vality.adapter.flow.lib.constant.Status;
|
||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.utils.ErrorUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class SuccessFinishProcessor
|
||||
implements Processor<ExitStateModel, BaseResponseModel, EntryStateModel> {
|
||||
|
||||
private final Processor<ExitStateModel, BaseResponseModel, EntryStateModel> nextProcessor;
|
||||
|
||||
@Override
|
||||
public ExitStateModel process(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||
if (response.getStatus() == Status.SUCCESS
|
||||
&& !ErrorUtils.isError(response)) {
|
||||
log.debug("Start success process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
ExitStateModel exitStateModel = new ExitStateModel();
|
||||
exitStateModel.setProviderTrxId(response.getProviderTrxId());
|
||||
exitStateModel.setLastOperationStatus(response.getStatus());
|
||||
Map<String, String> saveData = response.getSaveData();
|
||||
if (entryStateModel.getBaseRequestModel().getRecurrentPaymentData() != null
|
||||
&& entryStateModel.getBaseRequestModel().getRecurrentPaymentData().isMakeRecurrent()) {
|
||||
if (saveData == null) {
|
||||
saveData = new HashMap<>();
|
||||
}
|
||||
String recToken = initRecurrentToken(response, entryStateModel);
|
||||
saveData.put(MetaData.META_REC_TOKEN, recToken);
|
||||
exitStateModel.setRecToken(recToken);
|
||||
}
|
||||
exitStateModel.setTrxExtra(saveData);
|
||||
log.debug("Finish success process response: {} entryStateModel: {}", response, entryStateModel);
|
||||
return exitStateModel;
|
||||
}
|
||||
|
||||
if (nextProcessor != null) {
|
||||
return nextProcessor.process(response, entryStateModel);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Processor didn't match for response " + response);
|
||||
}
|
||||
|
||||
private String initRecurrentToken(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||
if (StringUtils.hasText(response.getRecurrentToken())) {
|
||||
return response.getRecurrentToken();
|
||||
} else if (entryStateModel.getBaseRequestModel().getRecurrentPaymentData() != null
|
||||
&& StringUtils.hasText(entryStateModel.getBaseRequestModel().getRecurrentPaymentData()
|
||||
.getRecToken())) {
|
||||
return entryStateModel.getBaseRequestModel().getRecurrentPaymentData().getRecToken();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
public interface Deserializer<T> {
|
||||
|
||||
T read(byte[] data);
|
||||
|
||||
T read(String data);
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.vality.adapter.flow.lib.exception.DeserializationException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ParametersDeserializer implements Deserializer<Map<String, String>> {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public Map<String, String> read(byte[] data) {
|
||||
if (data == null) {
|
||||
return new HashMap<>();
|
||||
} else {
|
||||
try {
|
||||
return mapper.readValue(data, new TypeReference<HashMap<String, String>>() {
|
||||
});
|
||||
} catch (IOException var3) {
|
||||
throw new IllegalArgumentException(var3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> read(String data) {
|
||||
throw new DeserializationException("Deserialization not supported");
|
||||
}
|
||||
|
||||
public Map<String, String> read(HttpServletRequest request) {
|
||||
return Optional.ofNullable(request.getParameterMap())
|
||||
.orElseGet(HashMap::new)
|
||||
.entrySet().stream()
|
||||
.collect(Collectors.toMap(k -> k.getKey().trim(),
|
||||
v -> v.getValue()[0]));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ParametersSerializer extends StateSerializer<Map<String, String>> {
|
||||
public ParametersSerializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
public interface Serializer<T> {
|
||||
|
||||
byte[] writeByte(T obj);
|
||||
|
||||
String writeString(T obj);
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.SerializationException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public abstract class StateSerializer<T> implements Serializer<T> {
|
||||
|
||||
protected final ObjectMapper mapper;
|
||||
|
||||
@Override
|
||||
public byte[] writeByte(Object obj) {
|
||||
try {
|
||||
return mapper.writeValueAsBytes(obj);
|
||||
} catch (IOException e) {
|
||||
throw new SerializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeString(Object obj) {
|
||||
try {
|
||||
return Base64.getEncoder().encodeToString(getMapper().writeValueAsBytes(obj));
|
||||
} catch (IOException e) {
|
||||
throw new SerializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.vality.adapter.flow.lib.exception.DeserializationException;
|
||||
import dev.vality.adapter.flow.lib.model.TemporaryContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TemporaryContextDeserializer implements Deserializer<TemporaryContext> {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public TemporaryContext read(byte[] data) {
|
||||
if (data == null) {
|
||||
return new TemporaryContext();
|
||||
} else {
|
||||
try {
|
||||
return this.getMapper().readValue(data, TemporaryContext.class);
|
||||
} catch (IOException var3) {
|
||||
throw new IllegalArgumentException(var3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TemporaryContext read(String data) {
|
||||
throw new DeserializationException("Deserialization not supported");
|
||||
}
|
||||
|
||||
public ObjectMapper getMapper() {
|
||||
return this.mapper;
|
||||
}
|
||||
|
||||
public TemporaryContextDeserializer(ObjectMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package dev.vality.adapter.flow.lib.serde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.vality.adapter.flow.lib.model.TemporaryContext;
|
||||
|
||||
public class TemporaryContextSerializer extends StateSerializer<TemporaryContext> {
|
||||
public TemporaryContextSerializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.PollingInfo;
|
||||
import dev.vality.adapter.flow.lib.utils.backoff.BackOffExecution;
|
||||
import dev.vality.adapter.flow.lib.utils.backoff.ExponentialBackOff;
|
||||
import dev.vality.adapter.flow.lib.utils.backoff.TimeOptionsExtractors;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static dev.vality.adapter.flow.lib.utils.backoff.ExponentialBackOff.*;
|
||||
|
||||
|
||||
public class ExponentialBackOffPollingService {
|
||||
|
||||
public BackOffExecution prepareBackOffExecution(PollingInfo pollingInfo, Map<String, String> options) {
|
||||
return exponentialBackOff(pollingInfo, options)
|
||||
.start();
|
||||
}
|
||||
|
||||
public int prepareNextPollingInterval(PollingInfo pollingInfo, Map<String, String> options) {
|
||||
return exponentialBackOff(pollingInfo, options)
|
||||
.start()
|
||||
.nextBackOff()
|
||||
.intValue();
|
||||
}
|
||||
|
||||
private ExponentialBackOff exponentialBackOff(PollingInfo pollingInfo, Map<String, String> options) {
|
||||
final Long currentLocalTime = Instant.now().toEpochMilli();
|
||||
|
||||
Long startTime = pollingInfo.getStartDateTimePolling() != null
|
||||
? pollingInfo.getStartDateTimePolling().toEpochMilli()
|
||||
: currentLocalTime;
|
||||
Integer exponential = TimeOptionsExtractors.extractExponent(options, DEFAULT_MUTIPLIER);
|
||||
Integer defaultInitialExponential =
|
||||
TimeOptionsExtractors.extractDefaultInitialExponential(options, DEFAULT_INITIAL_INTERVAL_SEC);
|
||||
Integer maxTimeBackOff = TimeOptionsExtractors.extractMaxTimeBackOff(options, DEFAULT_MAX_INTERVAL_SEC);
|
||||
|
||||
return new ExponentialBackOff(
|
||||
startTime,
|
||||
currentLocalTime,
|
||||
exponential,
|
||||
defaultInitialExponential,
|
||||
maxTimeBackOff);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.bender.BenderSrv;
|
||||
import dev.vality.bender.GenerationResult;
|
||||
import dev.vality.bender.GenerationSchema;
|
||||
import dev.vality.bender.SequenceSchema;
|
||||
import dev.vality.msgpack.Nil;
|
||||
import dev.vality.msgpack.Value;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class IdGenerator {
|
||||
|
||||
private static final String SEQ_ID = "orderId";
|
||||
|
||||
private final BenderSrv.Iface benderClient;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Value("adapter.prefix")
|
||||
private String adapterPrefix;
|
||||
|
||||
@SneakyThrows
|
||||
public Long get(String invoiceId) {
|
||||
GenerationSchema schema = GenerationSchema.sequence(new SequenceSchema().setSequenceId(SEQ_ID));
|
||||
GenerationResult result = benderClient.generateID(adapterPrefix + invoiceId, schema, Value.nl(new Nil()));
|
||||
return Long.parseLong(result.getInternalId());
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.RedirectFields;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.PollingInfo;
|
||||
import dev.vality.adapter.flow.lib.model.ThreeDsData;
|
||||
import dev.vality.adapter.flow.lib.serde.ParametersSerializer;
|
||||
import dev.vality.adapter.flow.lib.utils.CallbackUrlExtractor;
|
||||
import dev.vality.adapter.flow.lib.utils.ThreeDsDataInitializer;
|
||||
import dev.vality.adapter.flow.lib.utils.TimerProperties;
|
||||
import dev.vality.damsel.base.Timer;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import dev.vality.damsel.timeout_behaviour.TimeoutBehaviour;
|
||||
import dev.vality.error.mapping.ErrorMapping;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
import static dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators.createFinishIntentSuccessWithToken;
|
||||
import static dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators.createPostUserInteraction;
|
||||
import static dev.vality.java.damsel.utils.extractors.OptionsExtractors.extractRedirectTimeout;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class IntentResultFactory {
|
||||
|
||||
private final TimerProperties timerProperties;
|
||||
private final CallbackUrlExtractor callbackUrlExtractor;
|
||||
private final TagManagementService tagManagementService;
|
||||
private final ParametersSerializer parametersSerializer;
|
||||
private final PollingInfoService pollingInfoService;
|
||||
private final ErrorMapping errorMapping;
|
||||
private final ExponentialBackOffPollingService exponentialBackOffPollingService;
|
||||
|
||||
public Intent createFinishIntentSuccessWithCheckToken(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
if (entryStateModel.getBaseRequestModel().getRecurrentPaymentData() != null
|
||||
&& entryStateModel.getBaseRequestModel().getRecurrentPaymentData().isMakeRecurrent()) {
|
||||
return createFinishIntentSuccessWithToken(exitStateModel.getRecToken());
|
||||
}
|
||||
return createFinishIntentSuccess();
|
||||
}
|
||||
|
||||
public Intent createSuspendIntentWithFailedAfterTimeout(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
ThreeDsData threeDsData = exitStateModel.getThreeDsData();
|
||||
Map<String, String> params = ThreeDsDataInitializer.initThreeDsParameters(exitStateModel);
|
||||
String redirectUrl = entryStateModel.getBaseRequestModel().getSuccessRedirectUrl();
|
||||
params.put(RedirectFields.TERM_URL.getValue(), callbackUrlExtractor.extractCallbackUrl(redirectUrl));
|
||||
int timerRedirectTimeout = extractRedirectTimeout(
|
||||
entryStateModel.getBaseRequestModel().getAdapterConfigurations(),
|
||||
timerProperties.getRedirectTimeoutMin());
|
||||
return Intent.suspend(
|
||||
new SuspendIntent(
|
||||
tagManagementService.findTag(params),
|
||||
Timer.timeout(timerRedirectTimeout)
|
||||
).setUserInteraction(createPostUserInteraction(threeDsData.getAcsUrl(), params))
|
||||
);
|
||||
}
|
||||
|
||||
public Intent createSuspendIntentWithCallbackAfterTimeout(ExitStateModel exitStateModel) {
|
||||
Map<String, String> params = ThreeDsDataInitializer.initThreeDsParameters(exitStateModel);
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
|
||||
PollingInfo pollingInfo = pollingInfoService.initPollingInfo(entryStateModel);
|
||||
if (pollingInfoService.isDeadline(pollingInfo)) {
|
||||
return createFinishIntentFailed("Sleep timeout", "Max time pool limit reached");
|
||||
}
|
||||
exitStateModel.setPollingInfo(pollingInfo);
|
||||
|
||||
String redirectUrl = entryStateModel.getBaseRequestModel().getSuccessRedirectUrl();
|
||||
params.put(RedirectFields.TERM_URL.getValue(), callbackUrlExtractor.extractCallbackUrl(redirectUrl));
|
||||
ThreeDsData threeDsData = exitStateModel.getThreeDsData();
|
||||
int timerRedirectTimeout = extractRedirectTimeout(
|
||||
entryStateModel.getBaseRequestModel().getAdapterConfigurations(),
|
||||
timerProperties.getRedirectTimeoutMin());
|
||||
return Intent.suspend(
|
||||
new SuspendIntent(
|
||||
tagManagementService.findTag(params),
|
||||
Timer.timeout(timerRedirectTimeout))
|
||||
.setTimeoutBehaviour(TimeoutBehaviour.callback(
|
||||
ByteBuffer.wrap(parametersSerializer.writeByte(params)))
|
||||
).setUserInteraction(createPostUserInteraction(threeDsData.getAcsUrl(), params))
|
||||
);
|
||||
}
|
||||
|
||||
public Intent createFinishIntentSuccess() {
|
||||
return Intent.finish(new FinishIntent(FinishStatus.success(new Success())));
|
||||
}
|
||||
|
||||
public Intent createSleepIntentForReinvocation() {
|
||||
return Intent.sleep(new SleepIntent(Timer.timeout(0)));
|
||||
}
|
||||
|
||||
public Intent createSleepIntentWithExponentialPolling(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
PollingInfo pollingInfo = pollingInfoService.initPollingInfo(entryStateModel);
|
||||
if (pollingInfoService.isDeadline(pollingInfo)) {
|
||||
return createFinishIntentFailed("Sleep timeout", "Max time pool limit reached");
|
||||
}
|
||||
exitStateModel.setPollingInfo(pollingInfo);
|
||||
|
||||
Map<String, String> adapterConfigurations = entryStateModel.getBaseRequestModel().getAdapterConfigurations();
|
||||
int nextTimeout =
|
||||
exponentialBackOffPollingService.prepareNextPollingInterval(pollingInfo, adapterConfigurations);
|
||||
return Intent.sleep(
|
||||
new SleepIntent(Timer.timeout(nextTimeout))
|
||||
);
|
||||
}
|
||||
|
||||
public Intent createFinishIntentFailed(ExitStateModel exitStateModel) {
|
||||
return Intent.finish(new FinishIntent(FinishStatus.failure(
|
||||
errorMapping.mapFailure(exitStateModel.getErrorCode(),
|
||||
exitStateModel.getErrorMessage()))));
|
||||
}
|
||||
|
||||
public Intent createFinishIntentFailed(String errorCode, String errorMessage) {
|
||||
return Intent.finish(new FinishIntent(FinishStatus.failure(
|
||||
errorMapping.mapFailure(errorCode, errorMessage))));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.PollingInfo;
|
||||
import dev.vality.adapter.flow.lib.utils.TimerProperties;
|
||||
import dev.vality.java.damsel.utils.extractors.OptionsExtractors;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PollingInfoService {
|
||||
|
||||
private final TimerProperties timerProperties;
|
||||
|
||||
public PollingInfo initPollingInfo(EntryStateModel entryStateModel) {
|
||||
PollingInfo pollingInfo = entryStateModel.getStartedPollingInfo();
|
||||
if (pollingInfo == null) {
|
||||
pollingInfo = new PollingInfo();
|
||||
}
|
||||
if (pollingInfo.getStartDateTimePolling() == null) {
|
||||
pollingInfo.setStartDateTimePolling(Instant.now());
|
||||
}
|
||||
Instant maxDateTimePolling = calcDeadline(entryStateModel, pollingInfo);
|
||||
pollingInfo.setMaxDateTimePolling(maxDateTimePolling);
|
||||
return pollingInfo;
|
||||
}
|
||||
|
||||
public boolean isDeadline(PollingInfo pollingInfo) {
|
||||
Instant now = Instant.now();
|
||||
return now.isAfter(pollingInfo.getMaxDateTimePolling());
|
||||
}
|
||||
|
||||
private Instant calcDeadline(EntryStateModel entryStateModel, @NonNull PollingInfo pollingInfo) {
|
||||
if (pollingInfo.getMaxDateTimePolling() == null) {
|
||||
Integer maxTimePolling = OptionsExtractors.extractMaxTimePolling(
|
||||
entryStateModel.getBaseRequestModel().getAdapterConfigurations(),
|
||||
timerProperties.getMaxTimePollingMin());
|
||||
return pollingInfo.getStartDateTimePolling().plus(maxTimePolling, ChronoUnit.MINUTES);
|
||||
}
|
||||
return pollingInfo.getMaxDateTimePolling();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.adapter.flow.lib.constant.RedirectFields;
|
||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||
import dev.vality.adapter.flow.lib.model.PollingInfo;
|
||||
import dev.vality.adapter.flow.lib.model.ThreeDsData;
|
||||
import dev.vality.adapter.flow.lib.utils.CallbackUrlExtractor;
|
||||
import dev.vality.adapter.flow.lib.utils.ThreeDsDataInitializer;
|
||||
import dev.vality.adapter.flow.lib.utils.TimerProperties;
|
||||
import dev.vality.damsel.base.Timer;
|
||||
import dev.vality.damsel.proxy_provider.*;
|
||||
import dev.vality.error.mapping.ErrorMapping;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators.createPostUserInteraction;
|
||||
import static dev.vality.java.damsel.utils.creators.ProxyProviderPackageCreators.createRecurrentTokenStatusSuccess;
|
||||
import static dev.vality.java.damsel.utils.extractors.OptionsExtractors.extractRedirectTimeout;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class RecurrentIntentResultFactory {
|
||||
|
||||
private final TimerProperties timerProperties;
|
||||
private final CallbackUrlExtractor callbackUrlExtractor;
|
||||
private final TagManagementService tagManagementService;
|
||||
private final PollingInfoService pollingInfoService;
|
||||
private final ErrorMapping errorMapping;
|
||||
private final ExponentialBackOffPollingService exponentialBackOffPollingService;
|
||||
|
||||
public RecurrentTokenIntent createIntentWithSuspension(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
ThreeDsData threeDsData = exitStateModel.getThreeDsData();
|
||||
Map<String, String> params = ThreeDsDataInitializer.initThreeDsParameters(exitStateModel);
|
||||
String redirectUrl = entryStateModel.getBaseRequestModel().getSuccessRedirectUrl();
|
||||
params.put(RedirectFields.TERM_URL.getValue(), callbackUrlExtractor.extractCallbackUrl(redirectUrl));
|
||||
int timerRedirectTimeout = extractRedirectTimeout(
|
||||
entryStateModel.getBaseRequestModel().getAdapterConfigurations(),
|
||||
timerProperties.getRedirectTimeoutMin());
|
||||
return RecurrentTokenIntent.suspend(
|
||||
new SuspendIntent(
|
||||
tagManagementService.findTag(params),
|
||||
Timer.timeout(timerRedirectTimeout)
|
||||
).setUserInteraction(createPostUserInteraction(threeDsData.getAcsUrl(), params))
|
||||
);
|
||||
}
|
||||
|
||||
public RecurrentTokenIntent createSleepIntentForReinvocation() {
|
||||
return RecurrentTokenIntent.sleep(new SleepIntent(Timer.timeout(0)));
|
||||
}
|
||||
|
||||
public RecurrentTokenIntent createSleepIntentWithExponentialPolling(ExitStateModel exitStateModel) {
|
||||
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||
PollingInfo pollingInfo = pollingInfoService.initPollingInfo(entryStateModel);
|
||||
if (pollingInfoService.isDeadline(pollingInfo)) {
|
||||
return createFinishIntentFailed("Sleep timeout", "Max time pool limit reached");
|
||||
}
|
||||
exitStateModel.setPollingInfo(pollingInfo);
|
||||
|
||||
Map<String, String> adapterConfigurations = entryStateModel.getBaseRequestModel().getAdapterConfigurations();
|
||||
int nextTimeout =
|
||||
exponentialBackOffPollingService.prepareNextPollingInterval(pollingInfo, adapterConfigurations);
|
||||
return RecurrentTokenIntent.sleep(
|
||||
new SleepIntent(Timer.timeout(nextTimeout))
|
||||
);
|
||||
}
|
||||
|
||||
public RecurrentTokenIntent createFinishIntent(String recToken) {
|
||||
return RecurrentTokenIntent.finish(
|
||||
createRecurrentTokenStatusSuccess(recToken)
|
||||
);
|
||||
}
|
||||
|
||||
public RecurrentTokenIntent createFinishIntentFailed(String errorCode, String errorMessage) {
|
||||
return RecurrentTokenIntent.finish(new RecurrentTokenFinishIntent(RecurrentTokenFinishStatus.failure(
|
||||
errorMapping.mapFailure(errorCode, errorMessage)))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package dev.vality.adapter.flow.lib.service;
|
||||
|
||||
import dev.vality.adapter.flow.lib.utils.AdapterProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class TagManagementService {
|
||||
|
||||
private final AdapterProperties adapterProperties;
|
||||
|
||||
@SneakyThrows
|
||||
public String findTag(Map<String, String> parameters) {
|
||||
Optional<String> first = adapterProperties.getTagGeneratorFieldNames().stream()
|
||||
.filter(s -> StringUtils.hasText(parameters.get(s)))
|
||||
.findFirst();
|
||||
return first.get();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String get(Map<String, String> parameters) {
|
||||
Optional<String> first = adapterProperties.getTagGeneratorFieldNames().stream()
|
||||
.filter(s -> StringUtils.hasText(parameters.get(s)))
|
||||
.findFirst();
|
||||
return first.get();
|
||||
}
|
||||
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user