mirror of
https://github.com/valitydev/adapter-common-lib.git
synced 2024-11-06 02:05:18 +00:00
init lib (#1)
This commit is contained in:
parent
19d6a98c24
commit
e9de8bf303
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
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "build_utils"]
|
||||
path = build_utils
|
||||
url = git@github.com:rbkmoney/build_utils.git
|
||||
branch = master
|
13
Jenkinsfile
vendored
Normal file
13
Jenkinsfile
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
#!groovy
|
||||
build('adapter-common-lib', 'docker-host') {
|
||||
checkoutRepo()
|
||||
loadBuildUtils()
|
||||
|
||||
def javaLibPipeline
|
||||
runStage('load JavaLib pipeline') {
|
||||
javaLibPipeline = load("build_utils/jenkins_lib/pipeJavaLib.groovy")
|
||||
}
|
||||
|
||||
def buildImageTag = "fcf116dd775cc2e91bffb6a36835754e3f2d5321"
|
||||
javaLibPipeline(buildImageTag)
|
||||
}
|
1
build_utils
Submodule
1
build_utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit ea4aa042f482551d624fd49a570d28488f479e93
|
198
pom.xml
Normal file
198
pom.xml
Normal file
@ -0,0 +1,198 @@
|
||||
<?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>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>adapter-common-lib</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<description></description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>8</java.version>
|
||||
<dockerfile.base.service.tag>bc95d0d6dc13c693acd2b274531a7d604b877bf3</dockerfile.base.service.tag>
|
||||
<dockerfile.registry>${env.REGISTRY}</dockerfile.registry>
|
||||
<shared.resources.version>0.3.6</shared.resources.version>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<apache.commons.lang3.version>3.9</apache.commons.lang3.version>
|
||||
<apache.commons.codec.version>1.12</apache.commons.codec.version>
|
||||
<damsel.version>1.283-27b28aa</damsel.version>
|
||||
<damsel.utils.version>2.1.9</damsel.utils.version>
|
||||
<woody.version>1.1.15</woody.version>
|
||||
<hellgate-adapter-client.version>2.1.9</hellgate-adapter-client.version>
|
||||
<error-mapping-java.version>1.0.2</error-mapping-java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<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>1.18.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>5.1.7.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>5.1.7.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- RBK.Money -->
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>damsel</artifactId>
|
||||
<version>${damsel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>damsel-utils</artifactId>
|
||||
<version>${damsel.utils.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney.woody</groupId>
|
||||
<artifactId>woody-thrift</artifactId>
|
||||
<version>${woody.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney.proxy-libs</groupId>
|
||||
<artifactId>hellgate-adapter-client</artifactId>
|
||||
<version>${hellgate-adapter-client.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>error-mapping-java</artifactId>
|
||||
<version>${error-mapping-java.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>2.1.5.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<distributionManagement>
|
||||
<downloadUrl>http://java-nexus.msk1.rbkmoney.net:8081/nexus/content/groups/public</downloadUrl>
|
||||
<repository>
|
||||
<uniqueVersion>false</uniqueVersion>
|
||||
<id>releases</id>
|
||||
<name>RBKmoney releases repository</name>
|
||||
<url>http://java-nexus.msk1.rbkmoney.net:8081/nexus/content/repositories/releases</url>
|
||||
<layout>default</layout>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<uniqueVersion>true</uniqueVersion>
|
||||
<id>snapshots</id>
|
||||
<name>RBKmoney snapshots repository</name>
|
||||
<url>http://java-nexus.msk1.rbkmoney.net:8081/nexus/content/repositories/snapshots</url>
|
||||
<layout>default</layout>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.2</version>
|
||||
<configuration>
|
||||
<destFile>${sonar.jacoco.reportPath}</destFile>
|
||||
<append>true</append>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
</project>
|
7
src/main/java/com/rbkmoney/adapter/common/Validator.java
Normal file
7
src/main/java/com/rbkmoney/adapter/common/Validator.java
Normal file
@ -0,0 +1,7 @@
|
||||
package com.rbkmoney.adapter.common;
|
||||
|
||||
public interface Validator<O> {
|
||||
|
||||
void validate(O object);
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package com.rbkmoney.adapter.common.component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Slf4j
|
||||
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private AtomicInteger requestNumberSequence = new AtomicInteger(0);
|
||||
|
||||
public LoggingInterceptor() {
|
||||
log.warn("Warning! LoggingInterceptor can write bank card data in RAW");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
int requestNumber = requestNumberSequence.incrementAndGet();
|
||||
logRequest(requestNumber, request, body);
|
||||
ClientHttpResponse response = execution.execute(request, body);
|
||||
response = new BufferedClientHttpResponse(response);
|
||||
logResponse(requestNumber, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private void logRequest(int requestNumber, HttpRequest request, byte[] body) {
|
||||
if (log.isDebugEnabled()) {
|
||||
String prefix = requestNumber + " > ";
|
||||
log.debug("{} Request: {} {}", prefix, request.getMethod(), request.getURI());
|
||||
log.debug("{} Headers: {}", prefix, request.getHeaders());
|
||||
if (body.length > 0) {
|
||||
log.debug("{} Body: \n{}", prefix, new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logResponse(int requestNumber, ClientHttpResponse response) throws IOException {
|
||||
if (log.isDebugEnabled()) {
|
||||
String prefix = requestNumber + " < ";
|
||||
log.debug("{} Response: {} {} {}", prefix, response.getStatusCode(), response.getStatusCode().name(), response.getStatusText());
|
||||
log.debug("{} Headers: {}", prefix, response.getHeaders());
|
||||
String body = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
|
||||
if (body.length() > 0) {
|
||||
log.debug("{} Body: \n{}", prefix, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around ClientHttpResponse, buffers the body so it can be read repeatedly (for logging & consuming the result).
|
||||
*/
|
||||
private class BufferedClientHttpResponse implements ClientHttpResponse {
|
||||
|
||||
private final ClientHttpResponse response;
|
||||
private byte[] body;
|
||||
|
||||
public BufferedClientHttpResponse(ClientHttpResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return response.getStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return response.getRawStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return response.getStatusText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
response.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
if (body == null) {
|
||||
body = StreamUtils.copyToByteArray(response.getBody());
|
||||
}
|
||||
return new ByteArrayInputStream(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return response.getHeaders();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.rbkmoney.adapter.common.component;
|
||||
|
||||
import com.rbkmoney.woody.api.flow.WFlow;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class NetworkFilterComponent {
|
||||
|
||||
public static final String HEALTH = "/actuator/health";
|
||||
|
||||
public FilterRegistrationBean externalPortRestrictingFilter(int restPort, String restEndpoint) {
|
||||
Filter filter = new OncePerRequestFilter() {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
String servletPath = request.getServletPath();
|
||||
if ((request.getLocalPort() == restPort)
|
||||
&& !(servletPath.startsWith(restEndpoint) || servletPath.startsWith(HEALTH))) {
|
||||
response.sendError(404, "Unknown address");
|
||||
return;
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
};
|
||||
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
|
||||
filterRegistrationBean.setFilter(filter);
|
||||
filterRegistrationBean.setOrder(-100);
|
||||
filterRegistrationBean.setName("httpPortFilter");
|
||||
filterRegistrationBean.addUrlPatterns("/*");
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
public FilterRegistrationBean woodyFilter(int restPort, String restEndpoint) {
|
||||
WFlow wFlow = new WFlow();
|
||||
Filter filter = new OncePerRequestFilter() {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
if ((request.getLocalPort() == restPort)
|
||||
&& request.getServletPath().startsWith(restEndpoint)) {
|
||||
wFlow.createServiceFork(() -> {
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (IOException | ServletException e) {
|
||||
sneakyThrow(e);
|
||||
}
|
||||
}).run();
|
||||
return;
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private <E extends Throwable, T> T sneakyThrow(Throwable t) throws E {
|
||||
throw (E) t;
|
||||
}
|
||||
};
|
||||
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
|
||||
filterRegistrationBean.setFilter(filter);
|
||||
filterRegistrationBean.setOrder(-50);
|
||||
filterRegistrationBean.setName("woodyFilter");
|
||||
filterRegistrationBean.addUrlPatterns(restEndpoint + "*");
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.rbkmoney.adapter.common.component;
|
||||
|
||||
import com.rbkmoney.woody.api.trace.ContextUtils;
|
||||
import com.rbkmoney.woody.api.trace.context.TraceContext;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.metrics.web.client.MetricsRestTemplateCustomizer;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class RestTemplateComponent {
|
||||
|
||||
public RestTemplate getSimpleRestTemplate(MetricsRestTemplateCustomizer metricsRestTemplateCustomizer,
|
||||
int networkTimeout) {
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = getRequestFactory(getSimpleHttpClient());
|
||||
RestTemplateBuilder restTemplateBuilder = getRestTemplateBuilder(requestFactory, metricsRestTemplateCustomizer);
|
||||
return getRestTemplate(restTemplateBuilder, networkTimeout);
|
||||
}
|
||||
|
||||
public RestTemplate getRestTemplateWithConverters(MetricsRestTemplateCustomizer metricsRestTemplateCustomizer,
|
||||
List<HttpMessageConverter<?>> messageConverterList,
|
||||
int networkTimeout) {
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = getRequestFactory(getSimpleHttpClient());
|
||||
RestTemplateBuilder restTemplateBuilder = getRestTemplateBuilder(requestFactory, metricsRestTemplateCustomizer);
|
||||
RestTemplate restTemplate = getRestTemplate(restTemplateBuilder, networkTimeout);
|
||||
restTemplate.setMessageConverters(messageConverterList);
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder, int networkTimeout) {
|
||||
int executionTimeout =
|
||||
ContextUtils.getExecutionTimeout(TraceContext.getCurrentTraceData().getServiceSpan(), networkTimeout);
|
||||
return restTemplateBuilder
|
||||
.setConnectTimeout(Duration.ofMillis(executionTimeout))
|
||||
.setReadTimeout(Duration.ofMillis(executionTimeout))
|
||||
.build();
|
||||
}
|
||||
|
||||
public RestTemplateBuilder getRestTemplateBuilder(HttpComponentsClientHttpRequestFactory requestFactory,
|
||||
MetricsRestTemplateCustomizer metricsRestTemplateCustomizer) {
|
||||
return new RestTemplateBuilder()
|
||||
.requestFactory(() -> requestFactory)
|
||||
.additionalCustomizers(metricsRestTemplateCustomizer);
|
||||
}
|
||||
|
||||
public HttpComponentsClientHttpRequestFactory getRequestFactory(CloseableHttpClient httpClient) {
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
|
||||
requestFactory.setHttpClient(httpClient);
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
public CloseableHttpClient getSimpleHttpClient() {
|
||||
return HttpClients.custom()
|
||||
.disableAutomaticRetries()
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.rbkmoney.adapter.common.constants;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ThreeDsFields {
|
||||
|
||||
public static final String MD = "MD";
|
||||
public static final String PA_REQ = "PaReq";
|
||||
public static final String PA_RES = "PaRes";
|
||||
public static final String TERM_URL = "TermUrl";
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.rbkmoney.adapter.common.controller;
|
||||
|
||||
import com.rbkmoney.adapter.common.model.Callback;
|
||||
import com.rbkmoney.adapter.common.serializer.CallbackSerializer;
|
||||
import com.rbkmoney.adapter.helpers.hellgate.HellgateAdapterClient;
|
||||
import com.rbkmoney.adapter.helpers.hellgate.exception.HellgateException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AdapterController {
|
||||
|
||||
private final HellgateAdapterClient hgClient;
|
||||
|
||||
private final CallbackSerializer callbackSerializer;
|
||||
|
||||
public String receivePaymentIncomingParameters(HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) throws IOException {
|
||||
return processCallback(servletRequest, servletResponse, hgClient::processPaymentCallback);
|
||||
}
|
||||
|
||||
public String receiveRecurrentIncomingParameters(HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) throws IOException {
|
||||
return processCallback(servletRequest, servletResponse, hgClient::processRecurrentTokenCallback);
|
||||
}
|
||||
|
||||
private String processCallback(HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse,
|
||||
BiFunction<String, ByteBuffer, ByteBuffer> hgFunction) throws IOException {
|
||||
String resp = "";
|
||||
Callback callbackObj = callbackSerializer.read(servletRequest);
|
||||
log.info("ProcessCallback {}", callbackObj);
|
||||
try {
|
||||
String tag = callbackObj.getMd();
|
||||
ByteBuffer callback = ByteBuffer.wrap(callbackSerializer.writeByte(callbackObj));
|
||||
ByteBuffer response = hgFunction.apply(tag, callback);
|
||||
resp = new String(response.array(), StandardCharsets.UTF_8);
|
||||
} catch (HellgateException e) {
|
||||
log.warn("Failed handle callback for recurrent", e);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed handle callback for recurrent", e);
|
||||
}
|
||||
if (StringUtils.hasText(callbackObj.getTerminationUri())) {
|
||||
servletResponse.sendRedirect(callbackObj.getTerminationUri());
|
||||
}
|
||||
log.info("ProcessCallback response {}", resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
}
|
24
src/main/java/com/rbkmoney/adapter/common/enums/Step.java
Normal file
24
src/main/java/com/rbkmoney/adapter/common/enums/Step.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.rbkmoney.adapter.common.enums;
|
||||
|
||||
public enum Step {
|
||||
|
||||
PRE_AUTH,
|
||||
AUTH,
|
||||
FINISH_THREE_DS,
|
||||
AWAIT_CALLBACK,
|
||||
CANCEL,
|
||||
REFUND,
|
||||
CHECK_STATUS,
|
||||
CAPTURE,
|
||||
|
||||
RECURRENT,
|
||||
AUTH_RECURRENT,
|
||||
|
||||
GENERATE_TOKEN_AUTH,
|
||||
GENERATE_TOKEN_FINISH_THREE_DS,
|
||||
GENERATE_TOKEN_CAPTURE,
|
||||
GENERATE_TOKEN_REFUND,
|
||||
GENERATE_TOKEN_CHECK_STATUS,
|
||||
GENERATE_TOKEN_FINISH
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.rbkmoney.adapter.common.enums;
|
||||
|
||||
public enum TargetStatus {
|
||||
|
||||
PROCESSED,
|
||||
CAPTURED,
|
||||
CANCELLED,
|
||||
REFUNDED
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.rbkmoney.adapter.common.exception;
|
||||
|
||||
public class UnknownTargetStatusException extends RuntimeException {
|
||||
|
||||
public UnknownTargetStatusException() {
|
||||
super("Unknown target status!");
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.rbkmoney.adapter.common.exception;
|
||||
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
public class UnsupportedMethodException extends TException {
|
||||
|
||||
public UnsupportedMethodException() {
|
||||
super("Unsupported method");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.rbkmoney.adapter.common.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 com.rbkmoney.adapter.common.handler;
|
||||
|
||||
import com.rbkmoney.adapter.common.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,79 @@
|
||||
package com.rbkmoney.adapter.common.handler;
|
||||
|
||||
import com.rbkmoney.damsel.proxy_provider.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.rbkmoney.java.damsel.utils.extractors.ProxyProviderPackageExtractors.*;
|
||||
import static com.rbkmoney.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 = 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 = context.getTokenInfo().getPaymentTool().getId();
|
||||
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 = extractInvoiceId(context);
|
||||
String invoicePaymentStatus = extractTargetInvoicePaymentStatus(context);
|
||||
log.info("Process payment handle {} start with invoiceId {}", invoicePaymentStatus, invoiceId);
|
||||
try {
|
||||
PaymentProxyResult proxyResult = handler.processPayment(context);
|
||||
log.info("Process payment handle {} finished with invoiceId {} and proxyResult {}",
|
||||
invoicePaymentStatus, invoiceId, proxyResult);
|
||||
return proxyResult;
|
||||
} catch (Exception e) {
|
||||
String message = String.format("Failed handle %s process payment for operation with invoiceId %s",
|
||||
invoicePaymentStatus, invoiceId);
|
||||
logMessage(e, message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaymentCallbackResult handlePaymentCallback(ByteBuffer byteBuffer,
|
||||
PaymentContext context) throws TException {
|
||||
String invoiceId = context.getPaymentInfo().getInvoice().getId();
|
||||
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,9 @@
|
||||
package com.rbkmoney.adapter.common.handler.callback;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface CallbackHandler<T, R> {
|
||||
|
||||
T handleCallback(ByteBuffer byteBuffer, R context);
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.rbkmoney.adapter.common.handler.callback;
|
||||
|
||||
import com.rbkmoney.adapter.common.enums.Step;
|
||||
import com.rbkmoney.adapter.common.model.AdapterContext;
|
||||
import com.rbkmoney.adapter.common.model.Callback;
|
||||
import com.rbkmoney.adapter.common.properties.TimerProperties;
|
||||
import com.rbkmoney.adapter.common.serializer.AdapterSerializer;
|
||||
import com.rbkmoney.adapter.common.serializer.CallbackSerializer;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentCallbackProxyResult;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentCallbackResult;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentContext;
|
||||
import com.rbkmoney.java.damsel.utils.extractors.OptionsExtractors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.rbkmoney.java.damsel.utils.creators.ProxyProviderPackageCreators.createCallbackResult;
|
||||
import static com.rbkmoney.java.damsel.utils.creators.ProxyProviderPackageCreators.createIntentWithSleepIntent;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentCallbackHandler implements CallbackHandler<PaymentCallbackResult, PaymentContext> {
|
||||
|
||||
private final AdapterSerializer adapterSerializer;
|
||||
|
||||
private final CallbackSerializer callbackSerializer;
|
||||
|
||||
private final TimerProperties timerProperties;
|
||||
|
||||
@Override
|
||||
public PaymentCallbackResult handleCallback(ByteBuffer callback, PaymentContext context) {
|
||||
AdapterContext adapterContext = adapterSerializer.getAdapterContext(context);
|
||||
adapterContext.setNextStep(Step.FINISH_THREE_DS);
|
||||
Callback callbackObj = callbackSerializer.read(callback.array());
|
||||
adapterContext.setPaRes(callbackObj.getPaRes());
|
||||
adapterContext.setMd(callbackObj.getMd());
|
||||
|
||||
int timerPollingDelay =
|
||||
OptionsExtractors.extractPollingDelay(context.getOptions(), timerProperties.getPollingDelay());
|
||||
byte[] callbackResponse = new byte[0];
|
||||
return createCallbackResult(
|
||||
callbackResponse,
|
||||
new PaymentCallbackProxyResult()
|
||||
.setIntent(createIntentWithSleepIntent(timerPollingDelay))
|
||||
.setNextState(adapterSerializer.writeByte(adapterContext))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.rbkmoney.adapter.common.handler.callback;
|
||||
|
||||
import com.rbkmoney.adapter.common.enums.Step;
|
||||
import com.rbkmoney.adapter.common.model.AdapterContext;
|
||||
import com.rbkmoney.adapter.common.model.Callback;
|
||||
import com.rbkmoney.adapter.common.properties.TimerProperties;
|
||||
import com.rbkmoney.adapter.common.serializer.AdapterSerializer;
|
||||
import com.rbkmoney.adapter.common.serializer.CallbackSerializer;
|
||||
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenCallbackResult;
|
||||
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenContext;
|
||||
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenIntent;
|
||||
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenProxyResult;
|
||||
import com.rbkmoney.java.damsel.utils.extractors.OptionsExtractors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.rbkmoney.java.damsel.utils.creators.BasePackageCreators.createTimerTimeout;
|
||||
import static com.rbkmoney.java.damsel.utils.creators.ProxyProviderPackageCreators.createRecurrentTokenCallbackResult;
|
||||
import static com.rbkmoney.java.damsel.utils.creators.ProxyProviderPackageCreators.createSleepIntent;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RecurrentTokenCallbackHandler implements CallbackHandler<RecurrentTokenCallbackResult, RecurrentTokenContext> {
|
||||
|
||||
private final AdapterSerializer adapterSerializer;
|
||||
|
||||
private final CallbackSerializer callbackSerializer;
|
||||
|
||||
private final TimerProperties timerProperties;
|
||||
|
||||
@Override
|
||||
public RecurrentTokenCallbackResult handleCallback(ByteBuffer callback, RecurrentTokenContext context) {
|
||||
AdapterContext adapterContext = adapterSerializer.getAdapterContext(context);
|
||||
adapterContext.setNextStep(Step.GENERATE_TOKEN_FINISH_THREE_DS);
|
||||
Callback callbackObj = callbackSerializer.read(callback.array());
|
||||
adapterContext.setPaRes(callbackObj.getPaRes());
|
||||
adapterContext.setMd(callbackObj.getMd());
|
||||
|
||||
int timerPollingDelay =
|
||||
OptionsExtractors.extractPollingDelay(context.getOptions(), timerProperties.getPollingDelay());
|
||||
byte[] callbackResponse = new byte[0];
|
||||
return createRecurrentTokenCallbackResult(
|
||||
callbackResponse,
|
||||
new RecurrentTokenProxyResult()
|
||||
.setIntent(RecurrentTokenIntent.sleep(createSleepIntent(createTimerTimeout(timerPollingDelay))))
|
||||
.setNextState(adapterSerializer.writeByte(adapterContext))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.rbkmoney.adapter.common.mapper;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.error.mapping.ErrorMapping;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleErrorMapping {
|
||||
|
||||
private final Resource filePath;
|
||||
|
||||
private final String patternReason;
|
||||
|
||||
public ErrorMapping getErrorMapping() throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
|
||||
ErrorMapping errorMapping = new ErrorMapping(filePath.getInputStream(), patternReason, mapper);
|
||||
errorMapping.validateMappingFormat();
|
||||
return errorMapping;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.rbkmoney.adapter.common.mapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.adapter.common.mapper.naming.strategy.UpperSnakeCaseStrategy;
|
||||
|
||||
public class SimpleObjectMapper {
|
||||
|
||||
public ObjectMapper getSimpleObjectMapper() {
|
||||
return new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
}
|
||||
|
||||
public ObjectMapper getUpperSnakeCaseStrategyMapper() {
|
||||
return new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.setPropertyNamingStrategy(new UpperSnakeCaseStrategy())
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.rbkmoney.adapter.common.mapper.naming.strategy;
|
||||
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.cfg.MapperConfig;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
|
||||
|
||||
public class UpperSnakeCaseStrategy extends PropertyNamingStrategy {
|
||||
@Override
|
||||
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
|
||||
return convert(field.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
|
||||
return convert(method.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
|
||||
return convert(method.getName());
|
||||
}
|
||||
|
||||
private String convert(String inputString) {
|
||||
if (inputString == null) {
|
||||
return inputString;
|
||||
}
|
||||
String input = inputString.substring(3);
|
||||
|
||||
if (input.length() > 1 && Character.isUpperCase(input.charAt(0)) && Character.isUpperCase(input.charAt(1))) {
|
||||
input = input.substring(0, 1).toLowerCase() + input.substring(1);
|
||||
}
|
||||
int length = input.length();
|
||||
StringBuilder result = new StringBuilder(length * 2);
|
||||
int resultLength = 0;
|
||||
boolean wasPrevTranslated = false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = input.charAt(i);
|
||||
if (i > 0 || c != '_') { // skip first starting underscore
|
||||
if (Character.isUpperCase(c)) {
|
||||
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
|
||||
result.append('_');
|
||||
resultLength++;
|
||||
}
|
||||
c = Character.toLowerCase(c);
|
||||
wasPrevTranslated = true;
|
||||
}
|
||||
else {
|
||||
wasPrevTranslated = false;
|
||||
}
|
||||
result.append(c);
|
||||
resultLength++;
|
||||
}
|
||||
}
|
||||
String finalString = resultLength > 0 ? result.toString() : input;
|
||||
return finalString.toUpperCase();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.rbkmoney.adapter.common.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.rbkmoney.adapter.common.enums.Step;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class AdapterContext {
|
||||
|
||||
private String md;
|
||||
private String paReq;
|
||||
private String paRes;
|
||||
private String acsUrl;
|
||||
private String termUrl;
|
||||
|
||||
private Step nextStep;
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.rbkmoney.adapter.common.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Callback {
|
||||
|
||||
@JsonProperty(value = "MD")
|
||||
private String md;
|
||||
|
||||
@JsonProperty(value = "PaRes")
|
||||
private String paRes;
|
||||
|
||||
@JsonProperty(value = "termination_uri")
|
||||
private String terminationUri;
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.rbkmoney.adapter.common.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RecToken {
|
||||
|
||||
private Map<String, String> recTokenMap;
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.rbkmoney.adapter.common.processor;
|
||||
|
||||
public interface Processor<R, T, E> {
|
||||
|
||||
R process(T response, E context);
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.rbkmoney.adapter.common.properties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class AdapterProperties {
|
||||
|
||||
@NotEmpty
|
||||
private String url;
|
||||
|
||||
@NotEmpty
|
||||
private String callbackUrl;
|
||||
|
||||
@NotEmpty
|
||||
private String pathCallbackUrl;
|
||||
|
||||
@NotEmpty
|
||||
private String pathRecurrentCallbackUrl;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.rbkmoney.adapter.common.properties;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class TimerProperties {
|
||||
|
||||
@NotNull
|
||||
private int redirectTimeout;
|
||||
|
||||
@NotNull
|
||||
private int maxTimePolling;
|
||||
|
||||
@NotNull
|
||||
private int pollingDelay;
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.rbkmoney.adapter.common.serializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.adapter.common.model.AdapterContext;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentContext;
|
||||
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AdapterSerializer extends StateSerializer<AdapterContext> {
|
||||
|
||||
public AdapterSerializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdapterContext read(byte[] data) {
|
||||
if (data == null) {
|
||||
return new AdapterContext();
|
||||
}
|
||||
try {
|
||||
return getMapper().readValue(data, AdapterContext.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public AdapterContext getAdapterContext(Object context) {
|
||||
AdapterContext adapterContext = new AdapterContext();
|
||||
byte[] state = getState(context);
|
||||
if (state != null && state.length > 0) {
|
||||
return this.read(state);
|
||||
}
|
||||
return adapterContext;
|
||||
}
|
||||
|
||||
public static byte[] getState(Object context) {
|
||||
if (context instanceof RecurrentTokenContext) {
|
||||
if (((RecurrentTokenContext) context).getSession() == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
return ((RecurrentTokenContext) context).getSession().getState();
|
||||
}
|
||||
return ((PaymentContext) context).getSession().getState();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.rbkmoney.adapter.common.serializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.adapter.common.model.Callback;
|
||||
|
||||
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;
|
||||
|
||||
public class CallbackSerializer extends StateSerializer<Callback> {
|
||||
|
||||
public CallbackSerializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback read(byte[] data) {
|
||||
if (data == null) {
|
||||
return new Callback();
|
||||
}
|
||||
try {
|
||||
return getMapper().readValue(data, Callback.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Callback read(HttpServletRequest request) {
|
||||
Map<String, String> stringMap = Optional.ofNullable(request.getParameterMap())
|
||||
.orElseGet(HashMap::new)
|
||||
.entrySet().stream()
|
||||
.collect(Collectors.toMap(k -> k.getKey().trim(),
|
||||
v -> v.getValue()[0]));
|
||||
return mapper.convertValue(stringMap, Callback.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.rbkmoney.adapter.common.serializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.adapter.common.model.RecToken;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class RecTokenSerializer extends StateSerializer<RecToken> {
|
||||
|
||||
public RecTokenSerializer(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecToken read(String data) {
|
||||
if (data == null) {
|
||||
return new RecToken();
|
||||
}
|
||||
try {
|
||||
return getMapper().readValue(Base64.getDecoder().decode(data), RecToken.class);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.rbkmoney.adapter.common.serializer;
|
||||
|
||||
public interface Serializer<TState> {
|
||||
|
||||
byte[] writeByte(Object obj);
|
||||
|
||||
String writeString(Object obj);
|
||||
|
||||
TState read(byte[] data);
|
||||
|
||||
TState read(String data);
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.rbkmoney.adapter.common.serializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
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 IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeString(Object obj) {
|
||||
try {
|
||||
return Base64.getEncoder().encodeToString(getMapper().writeValueAsBytes(obj));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(byte[] data) {
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public T read(String data) {
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.rbkmoney.adapter.common.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ThreeDsPropertiesService<T> {
|
||||
|
||||
Map<String, String> initProperties(T stateModel);
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.rbkmoney.adapter.common.utils.converter;
|
||||
|
||||
/** @see "https://github.com/dukky/Base62/blob/master/base62/src/im/duk/base62/Base62.java" */
|
||||
public class Base62 {
|
||||
|
||||
private String characters;
|
||||
|
||||
/**
|
||||
* Constructs a Base62 object with the default charset (0..9a..zA..Z).
|
||||
*/
|
||||
public Base62() {
|
||||
this("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Base62 object with a custom charset.
|
||||
*
|
||||
* @param characters
|
||||
* the charset to use. Must be 62 characters.
|
||||
* @throws <code>IllegalArgumentException<code> if the supplied charset is not 62 characters long.
|
||||
*/
|
||||
public Base62(String characters) {
|
||||
if (!(characters.length() == 62)) {
|
||||
throw new IllegalArgumentException("Invalid string length, must be 62.");
|
||||
}
|
||||
this.characters = characters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a decimal value to a Base62 <code>String</code>.
|
||||
*
|
||||
* @param b10
|
||||
* the decimal value to encode, must be nonnegative.
|
||||
* @return the number encoded as a Base62 <code>String</code>.
|
||||
*/
|
||||
public String encodeBase10(Long b10) {
|
||||
if (b10 < 0) {
|
||||
throw new IllegalArgumentException("b10 must be nonnegative");
|
||||
}
|
||||
String ret = "";
|
||||
while (b10 > 0) {
|
||||
ret = characters.charAt((int) (b10 % 62)) + ret;
|
||||
b10 /= 62;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a Base62 <code>String</code> returning a <code>long</code>.
|
||||
*
|
||||
* @param b62
|
||||
* the Base62 <code>String</code> to decode.
|
||||
* @return the decoded number as a <code>long</code>.
|
||||
* @throws IllegalArgumentException
|
||||
* if the given <code>String</code> contains characters not
|
||||
* specified in the constructor.
|
||||
*/
|
||||
public long decodeBase62(String b62) {
|
||||
for (char character : b62.toCharArray()) {
|
||||
if (!characters.contains(String.valueOf(character))) {
|
||||
throw new IllegalArgumentException("Invalid character(s) in string: " + character);
|
||||
}
|
||||
}
|
||||
long ret = 0;
|
||||
b62 = new StringBuffer(b62).reverse().toString();
|
||||
long count = 1;
|
||||
for (char character : b62.toCharArray()) {
|
||||
ret += characters.indexOf(character) * count;
|
||||
count *= 62;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.rbkmoney.adapter.common.utils.converter;
|
||||
|
||||
import com.rbkmoney.damsel.cds.ExpDate;
|
||||
import com.rbkmoney.damsel.cds.SessionData;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class CardDataUtils {
|
||||
|
||||
public static String getYearFromExpDate(Short year) {
|
||||
return String.format("%1$02d", year % 100);
|
||||
}
|
||||
|
||||
public static String getYearFromExpDate(ExpDate expDate) {
|
||||
return getYearFromExpDate(expDate.getYear());
|
||||
}
|
||||
|
||||
public static String getFullDateFromExpDate(ExpDate expDate) {
|
||||
int correctYear = expDate.getYear() / 100 == 0 ? expDate.getYear() + 2000 : expDate.getYear();
|
||||
return String.format("%1$04d%2$02d%3$02d", correctYear, expDate.getMonth(), getDayOfMonth(expDate));
|
||||
}
|
||||
|
||||
public static Integer getDayOfMonth(ExpDate expDate) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(expDate.getYear(), expDate.getMonth(), -1);
|
||||
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
public static String getMonthFromExpDate(Byte month) {
|
||||
return String.format("%1$02d", month);
|
||||
}
|
||||
|
||||
public static String getMonthFromExpDate(ExpDate expDate) {
|
||||
return String.format("%02d", expDate.getMonth());
|
||||
}
|
||||
|
||||
public static String getMonthFromByte(byte month) {
|
||||
return String.format("%02d", month);
|
||||
}
|
||||
|
||||
public static String getCvv2(SessionData sessionData) {
|
||||
if (sessionData == null
|
||||
|| sessionData.getAuthData() == null
|
||||
|| !sessionData.getAuthData().isSetCardSecurityCode()) {
|
||||
return null;
|
||||
}
|
||||
return sessionData.getAuthData().getCardSecurityCode().getValue();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.rbkmoney.adapter.common.utils.converter;
|
||||
|
||||
import com.rbkmoney.adapter.common.enums.TargetStatus;
|
||||
import com.rbkmoney.damsel.domain.TargetInvoicePaymentStatus;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentContext;
|
||||
import com.rbkmoney.damsel.proxy_provider.PaymentInfo;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Random;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class PaymentDataConverterUtils {
|
||||
|
||||
private static final String DEFAULT_ID = "1";
|
||||
|
||||
public static String makeHex16length() {
|
||||
return String.format("%016X", new Random().nextLong()).toUpperCase();
|
||||
}
|
||||
|
||||
public static BigDecimal getFormattedAmount(long amount) {
|
||||
return new BigDecimal(amount).movePointLeft(2);
|
||||
}
|
||||
|
||||
public static String prepareOrder(String invoiceId) {
|
||||
return prepareOrder(invoiceId, DEFAULT_ID);
|
||||
}
|
||||
|
||||
public static String prepareOrder(PaymentInfo paymentInfo) {
|
||||
return prepareOrder(paymentInfo.getInvoice().getId(), paymentInfo.getPayment().getId());
|
||||
}
|
||||
|
||||
public static String prepareOrder(String invoiceId, String paymentId) {
|
||||
return Long.toString(new Base62().decodeBase62(invoiceId) ^ Integer.parseInt(paymentId));
|
||||
}
|
||||
|
||||
public static String extractEmail(PaymentInfo paymentInfo) {
|
||||
if (paymentInfo == null
|
||||
|| paymentInfo.getPayment() == null
|
||||
|| paymentInfo.getPayment().getContactInfo() == null
|
||||
|| paymentInfo.getPayment().getContactInfo().getEmail() == null) {
|
||||
return "";
|
||||
} else {
|
||||
return paymentInfo.getPayment().getContactInfo().getEmail();
|
||||
}
|
||||
}
|
||||
|
||||
public static TargetStatus getTargetStatus(PaymentContext paymentContext) {
|
||||
if (paymentContext == null) {
|
||||
throw new IllegalArgumentException("PaymentContext cannot be empty");
|
||||
} else if (paymentContext.getSession() == null) {
|
||||
throw new IllegalArgumentException("Payment context session cannot be empty");
|
||||
} else {
|
||||
return getTargetStatus(paymentContext.getSession().getTarget());
|
||||
}
|
||||
}
|
||||
|
||||
public static TargetStatus getTargetStatus(TargetInvoicePaymentStatus targetInvoicePaymentStatus) {
|
||||
if (targetInvoicePaymentStatus == null) {
|
||||
throw new IllegalArgumentException("Argument targetInvoicePaymentStatus cannot be empty");
|
||||
}
|
||||
TargetStatus targetStatus;
|
||||
if (targetInvoicePaymentStatus.isSetProcessed()) {
|
||||
targetStatus = TargetStatus.PROCESSED;
|
||||
} else if (targetInvoicePaymentStatus.isSetCancelled()) {
|
||||
targetStatus = TargetStatus.CANCELLED;
|
||||
} else if (targetInvoicePaymentStatus.isSetCaptured()) {
|
||||
targetStatus = TargetStatus.CAPTURED;
|
||||
} else if (targetInvoicePaymentStatus.isSetRefunded()) {
|
||||
targetStatus = TargetStatus.REFUNDED;
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown target status: " + targetInvoicePaymentStatus);
|
||||
}
|
||||
return targetStatus;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.rbkmoney.adapter.common.utils.converter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
@Slf4j
|
||||
public class RedirectUtils {
|
||||
|
||||
public static String getCallbackUrl(String callbackUrl, String path) {
|
||||
return UriComponentsBuilder.fromUriString(callbackUrl)
|
||||
.path(path)
|
||||
.build()
|
||||
.toUriString();
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.rbkmoney.adapter.common.utils.encryption;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class HmacEncryption {
|
||||
|
||||
public static final String HMAC_SHA1 = "HmacSHA1";
|
||||
|
||||
public static final String HMAC_SHA256 = "HmacSHA256";
|
||||
|
||||
public static String calculateHMacSha1(String data, String key) {
|
||||
return calculateHMAC(data, key, HMAC_SHA1);
|
||||
}
|
||||
|
||||
public static String calculateHMacSha256(String data, String key) {
|
||||
return calculateHMAC(data, key, HMAC_SHA256);
|
||||
}
|
||||
|
||||
public static String calculateHMAC(String data, String key, String algorithm) {
|
||||
|
||||
try {
|
||||
byte[] decodedKey = Hex.decodeHex(key.toCharArray());
|
||||
SecretKeySpec keySpec = new SecretKeySpec(decodedKey, algorithm);
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
mac.init(keySpec);
|
||||
|
||||
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] signatureBytes = mac.doFinal(dataBytes);
|
||||
|
||||
return new String(new Hex().encode(signatureBytes));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static String prepareDataForHmac(String[] fields, MultiValueMap<String, String> params) {
|
||||
StringBuilder dataHmac = new StringBuilder();
|
||||
Arrays.asList(fields)
|
||||
.forEach(field -> {
|
||||
if (params.get(field) != null
|
||||
&& !params.get(field).isEmpty()
|
||||
&& params.get(field).get(0) != null
|
||||
&& !params.get(field).get(0).isEmpty()
|
||||
) {
|
||||
dataHmac.append(params.get(field).get(0).length());
|
||||
dataHmac.append(params.get(field).get(0));
|
||||
} else {
|
||||
dataHmac.append("-");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return dataHmac.toString();
|
||||
}
|
||||
|
||||
public static String sign(String[] fieldsForSign,
|
||||
MultiValueMap<String, String> params,
|
||||
String key,
|
||||
String algorithm) {
|
||||
String dataHmac = prepareDataForHmac(fieldsForSign, params);
|
||||
String pSign = calculateHMAC(dataHmac, key, algorithm);
|
||||
return pSign.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.rbkmoney.adapter.common.utils.encryption;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.rbkmoney.adapter.common.utils.encryption.HmacEncryption.calculateHMacSha256;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class HmacEncryptionTest {
|
||||
|
||||
private String message = "511.483USD677144616IT Books. Qty: 217Books Online Inc." +
|
||||
"14www.sample.com1512345678901234589999999919pgw@mail.sample.com11--" +
|
||||
"142003010515302116F2B2DD7E603A7ADA33https://www.sample.com/shop/reply";
|
||||
private String secret = "00112233445566778899AABBCCDDEEFF";
|
||||
|
||||
@Test
|
||||
public void testHMacSha256() {
|
||||
String expectedHMac = "3fd9ee801d3aeda5d33af83580279ef920ad78e8883a0b9bc3942c74129db2f0";
|
||||
String hmac = calculateHMacSha256(message, secret);
|
||||
assertEquals("HMAC SHA256 is not equal expected", expectedHMac, hmac);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user