PROX-34: Made a sketch of the skeleton of the application (#1)

* PROX-34: Made a sketch of the skeleton of the application

* PROX-34: added build_utils

* PROX-34: removed snapshot
This commit is contained in:
Anatoly Cherkasov 2016-12-15 13:13:44 +03:00 committed by GitHub
parent 6faebd6d86
commit a98161cc29
20 changed files with 670 additions and 1 deletions

53
.gitignore vendored Normal file
View File

@ -0,0 +1,53 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.DS_Store
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# 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
# Target folder
target

4
.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "build_utils"]
path = build_utils
url = git@github.com:rbkmoney/build_utils.git
branch = master

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM dr.rbkmoney.com/rbkmoney/service-java:@dockerfile.base.service.tag@
MAINTAINER Anatoly Cherkasov <a.cherkasov@rbkmoney.com>
COPY @artifactId@-@version@.jar /opt/@artifactId@/@artifactId@.jar
COPY containerpilot.json /etc/containerpilot.json
ENTRYPOINT ["/bin/containerpilot", "-config", "file:///etc/containerpilot.json", "java"]
CMD ["-Xmx512m", "-jar","/opt/@artifactId@/@artifactId@.jar"]
EXPOSE @server.port@
EXPOSE 8080
LABEL com.rbkmoney.@artifactId@.parent=service_java \
com.rbkmoney.@artifactId@.parent_tag=@dockerfile.base.service.tag@ \
com.rbkmoney.@artifactId@.build_img=build \
com.rbkmoney.@artifactId@.build_img_tag=@dockerfile.build.container.tag@ \
com.rbkmoney.@artifactId@.commit_id=@git.revision@ \
com.rbkmoney.@artifactId@.commit_number=@git.commitsCount@ \
com.rbkmoney.@artifactId@.branch=@git.branch@
WORKDIR /opt/@artifactId@

16
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,16 @@
#!groovy
build('proxy-test-mpi', 'docker-host') {
checkoutRepo()
loadBuildUtils()
def pipeJavaService
runStage('load Java Service pipeline') {
pipeJavaService = load('build_utils/jenkins_lib/pipeJavaService.groovy')
}
def serviceName = "proxy-test-mpi"
def baseImageTag = "f26fcc19d1941ab74f1c72dd8a408be17a769333"
def buildImageTag = "80c38dc638c0879687f6661f4e16e8de9fc0d2c6"
def dbHostName = null
def mvnArgs = '-DjvmArgs="-Xmx256m"'
pipeJavaService(serviceName, baseImageTag, buildImageTag, dbHostName, mvnArgs)
}

View File

@ -1 +1,9 @@
# proxy-test-mpi
# proxy-test-mpi
Сервис предназначен для эмулирования работы с 3DS MPI
### Developers
- [Anatoly Cherkasov](https://github.com/avcherkasov)

1
build_utils Submodule

@ -0,0 +1 @@
Subproject commit 0a57c5f10795d77ecf121d509fde7c654175c3c1

13
containerpilot.json Normal file
View File

@ -0,0 +1,13 @@
{
"consul": "{{ .CONSUL_ADDR }}",
"services": [
{
"name": "{{ .SERVICE_NAME }}",
"port": "@server.port@",
"health": "/usr/bin/curl --silent --fail --show-error --output /dev/null localhost:@server.port@/health",
"poll": 1,
"ttl": 2,
"interfaces": ["inte6", "inet"]
}
]
}

101
pom.xml Normal file
View File

@ -0,0 +1,101 @@
<?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>com.rbkmoney</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
<relativePath/>
</parent>
<artifactId>proxy-test-mpi</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>proxy-test-mpi</name>
<description>Проксик для тестирования и эмуляции запросов к 3DS</description>
<properties>
<server.port>8080</server.port>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--Thrirdparty libs-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.6</version>
</dependency>
<!--RBK libs-->
<dependency>
<groupId>com.rbkmoney.logback</groupId>
<artifactId>nop-rolling</artifactId>
<version>1.0.0</version>
</dependency>
<!--Test libs-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${project.basedir}</directory>
<targetPath>${project.build.directory}</targetPath>
<includes>
<include>Dockerfile</include>
<include>containerpilot.json</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,13 @@
package com.rbkmoney.proxy.test.mpi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication(scanBasePackages = {"com.rbkmoney.proxy.test.mpi"})
public class ProxyTestMpiApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ProxyTestMpiApplication.class, args);
}
}

View File

@ -0,0 +1,114 @@
package com.rbkmoney.proxy.test.mpi.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.proxy.test.mpi.model.Card;
import com.rbkmoney.proxy.test.mpi.utils.CardUtils;
import com.rbkmoney.proxy.test.mpi.utils.MpiAction;
import com.rbkmoney.proxy.test.mpi.utils.constant.MpiCavvAlgorithm;
import com.rbkmoney.proxy.test.mpi.utils.constant.MpiEnrollmentStatus;
import com.rbkmoney.proxy.test.mpi.utils.constant.MpiTransactionStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
@RestController
@RequestMapping(value = "/mpi")
public class ProxyTestMpiController {
public final static String DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
@Value("${fixture.cards}")
private Resource fixtureCards;
@Value("${proxy-test-mpi.callbackUrl}")
private Resource proxyTestMpiCallbackUrl;
List<Card> cardList;
@PostConstruct
public void init() throws IOException {
cardList = CardUtils.getCardListFromFile(fixtureCards.getInputStream());
}
@RequestMapping(value = "verifyEnrollment", method = RequestMethod.POST)
public String verifyEnrollment(
@RequestParam(value = "pan", required = true) String pan,
@RequestParam(value = "year", required = true) String year,
@RequestParam(value = "month", required = true) String month
) throws IOException {
CardUtils cardUtils = new CardUtils(cardList);
Optional<Card> card = cardUtils.getCardByPan(pan);
Map<String, String> map = new HashMap<>();
map.put("enrolled", MpiEnrollmentStatus.CARDHOLDER_NOT_PARTICIPATING);
if (cardUtils.isEnrolled(card)) {
map.put("enrolled", MpiEnrollmentStatus.AUTHENTICATION_AVAILABLE);
map.put("paReq", "paReq");
map.put("acsUrl", proxyTestMpiCallbackUrl + "/mpi/acs");
}
return new ObjectMapper().writeValueAsString(map);
}
@RequestMapping(value = "validatePaRes", method = RequestMethod.POST)
public String validatePaRes(
@RequestParam(value = "pan", required = true) String pan,
@RequestParam(value = "paRes", required = true) String paRes
) throws IOException {
CardUtils cardUtils = new CardUtils(cardList);
Optional<Card> card = cardUtils.getCardByPan(pan);
Map<String, String> map = new HashMap<>();
if (card.isPresent()) {
MpiAction action = MpiAction.findByValue(card.get().getAction());
switch (action) {
case THREE_D_SECURE_SUCCESS:
map.put("transactionStatus", MpiTransactionStatus.AUTHENTICATION_SUCCESSFUL);
map.put("eci", "1");
map.put("cavv", "3");
map.put("cavvAlgorithm", MpiCavvAlgorithm.CVV_WITH_ATN);
map.put("txId", UUID.randomUUID().toString());
map.put("txTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)));
break;
case THREE_D_SECURE_FAILURE:
map.put("transactionStatus", MpiTransactionStatus.AUTHENTICATION_FAILED);
break;
default:
map.put("transactionStatus", MpiTransactionStatus.ATTEMPTS_PROCESSING_PERFORMED);
}
} else {
map.put("transactionStatus", MpiTransactionStatus.AUTHENTICATION_COULD_NOT_BE_PERFORMED);
}
return new ObjectMapper().writeValueAsString(map);
}
@RequestMapping(value = "acs", method = RequestMethod.POST)
public ModelAndView formAcs(
@RequestParam(value = "PaReq", required = true) String paReq,
@RequestParam(value = "MD", required = true) String md,
@RequestParam(value = "TermUrl", required = true) String termUrl
) {
ModelAndView model = new ModelAndView();
model.setViewName("acs_form");
model.addObject("action", termUrl);
model.addObject("pan", "XXXX XXXX XXXX XXXX");
model.addObject("PaRes", "PaRes");
model.addObject("MD", md);
return model;
}
}

View File

@ -0,0 +1,42 @@
package com.rbkmoney.proxy.test.mpi.model;
public class Card {
private String pan;
private String action;
private String paymentSystem;
public Card(String pan, String action, String paymentSystem) {
this.pan = pan;
this.action = action;
this.paymentSystem = paymentSystem;
}
public String getPan() {
return pan;
}
public void setPan(String pan) {
this.pan = pan;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getPaymentSystem() {
return paymentSystem;
}
public void setPaymentSystem(String paymentSystem) {
this.paymentSystem = paymentSystem;
}
}

View File

@ -0,0 +1,52 @@
package com.rbkmoney.proxy.test.mpi.utils;
import com.rbkmoney.proxy.test.mpi.model.Card;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CardUtils {
List<Card> cardList = Collections.emptyList();
public CardUtils(List<Card> cardList) {
this.cardList = cardList;
}
public static List<Card> getCardListFromFile(InputStream is) {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
return br.lines().skip(1).map(mapToCard).collect(Collectors.toList());
}
private static Function<String, Card> mapToCard = (line) -> {
String[] p = line.split(", ");
return new Card(p[0], p[1], p[2]);
};
public Optional<Card> getCardByPan(String pan) {
return cardList.stream().filter(card -> card.getPan().equals(pan)).findFirst();
}
public boolean isEnrolled(Optional<Card> card) {
boolean result = false;
if (card.isPresent()) {
MpiAction action = MpiAction.findByValue(card.get().getAction());
switch (action) {
case THREE_D_SECURE_FAILURE:
case THREE_D_SECURE_TIMEOUT:
case THREE_D_SECURE_SUCCESS:
result = true;
break;
default:
result = false;
}
}
return result;
}
}

View File

@ -0,0 +1,35 @@
package com.rbkmoney.proxy.test.mpi.utils;
import java.util.Arrays;
public enum MpiAction {
UNKNOWN("Unknown"),
SUCCESS("Success"),
THREE_D_SECURE_SUCCESS("3-D Secure Success"),
THREE_D_SECURE_FAILURE("3-D Secure Failure"),
THREE_D_SECURE_TIMEOUT("3-D Secure Timeout"),
INCUFFICIENT_FUNDS("Incufficient Funds"),
INVALID_CARD("Invalid Card"),
CVV_MATCH_FAIL("CVV Match Fail"),
EXPIRED_CARD("Expired Card"),
UNKNOWN_FAILURE("Unknown Failure");
private final String action;
MpiAction(String action) {
this.action = action;
}
public String getAction() {
return action;
}
public static MpiAction findByValue(String value) {
return Arrays.stream(values()).filter((action) -> action.getAction().equals(value))
.findFirst()
.orElse(UNKNOWN);
}
}

View File

@ -0,0 +1,28 @@
package com.rbkmoney.proxy.test.mpi.utils.constant;
/**
* Indicates the algorithm used to generate the Cardholder Authentication Verification Value.
*/
public class MpiCavvAlgorithm {
/**
* 0: HMAC (as per SET TransStain) (no longer in use for version 1.0.2)
*/
public final static String HMAC_AS_PER_SET_TRANS_STAIN = "0";
/**
* 1: CVV (no longer in use for version 1.0.2).
*/
public final static String CVV_NO_LONGER = "1";
/**
* 2: CVV with ATN.
*/
public final static String CVV_WITH_ATN = "2";
/**
* 3: MasterCard SPA algorithm.
*/
public final static String MASTERCARD_SPA_ALGORITHM = "3";
}

View File

@ -0,0 +1,26 @@
package com.rbkmoney.proxy.test.mpi.utils.constant;
/**
* 3-D Secure enrollment status
*/
public class MpiEnrollmentStatus {
/**
* Authentication Available Cardholder is enrolled, Activation During Shopping is
* supported, or proof of attempted authentication available. The merchant uses the
* URL of issuer ACS included in VERes to create the Payer Authentication Request.
*/
public final static String AUTHENTICATION_AVAILABLE = "Y";
/**
* Cardholder Not Participating Cardholder is not enrolled.
*/
public final static String CARDHOLDER_NOT_PARTICIPATING = "N";
/**
* Unable to Authenticate or Card Not Eligible for Attempts
* (such as a Commercial or anonymous Prepaid card).
*/
public final static String UNABLE_TO_AUTHENTICATE = "U";
}

View File

@ -0,0 +1,32 @@
package com.rbkmoney.proxy.test.mpi.utils.constant;
/**
* Transaction Status
*/
public class MpiTransactionStatus {
/**
* The merchant submits an authorization request including the
* ECI and CAVV supplied in the PARes.
*/
public final static String AUTHENTICATION_SUCCESSFUL = "Y";
/**
* The merchant must not submit a failed authentication for
* authorization.
*/
public final static String AUTHENTICATION_FAILED = "N";
/**
* The merchant may process an authorization request using the
* appropriate ECI.
*/
public final static String AUTHENTICATION_COULD_NOT_BE_PERFORMED = "U";
/**
* The merchant submits an authorization request including the
* ECI and CAVV supplied in the PARes.
*/
public final static String ATTEMPTS_PROCESSING_PERFORMED = "A";
}

View File

@ -0,0 +1,22 @@
spring:
application:
name: name
description: description
---
info:
version: @version@
responsible: Anatoly Cherkasov
stage: dev
---
endpoints:
health:
sensitive: false
---
server:
port: @server.port@
---
proxy-test-mpi:
callbackUrl: http://127.0.0.1:8018
---
fixture:
cards: classpath:fixture/cards.csv

View File

@ -0,0 +1,20 @@
cardPan, action, paymentSystem
4242424242424242, Success, Visa
5555555555554444, Success, MasterCard
586824160825533338, Success, Maestro
4012888888881881, 3-D Secure Success, Visa
5169147129584558, 3-D Secure Success, MasterCard
4987654321098769, 3-D Secure Failure, Visa
5123456789012346, 3-D Secure Failure, MasterCard
4342561111111118, 3-D Secure Timeout, Visa
5112000200000002, 3-D Secure Timeout, MasterCard
4000000000000002, Incufficient Funds, Visa
5100000000000412, Incufficient Funds, MasterCard
4222222222222220, Invalid Card, Visa
5100000000000511, Invalid Card, MasterCard
4003830171874018, CVV Match Fail, Visa
5496198584584769, CVV Match Fail, MasterCard
4000000000000069, Expired Card, Visa
5105105105105100, Expired Card, MasterCard
4111110000000112, Unknown Failure, Visa
5124990000000002, Unknown Failure, MasterCard
1 cardPan action paymentSystem
2 4242424242424242 Success Visa
3 5555555555554444 Success MasterCard
4 586824160825533338 Success Maestro
5 4012888888881881 3-D Secure Success Visa
6 5169147129584558 3-D Secure Success MasterCard
7 4987654321098769 3-D Secure Failure Visa
8 5123456789012346 3-D Secure Failure MasterCard
9 4342561111111118 3-D Secure Timeout Visa
10 5112000200000002 3-D Secure Timeout MasterCard
11 4000000000000002 Incufficient Funds Visa
12 5100000000000412 Incufficient Funds MasterCard
13 4222222222222220 Invalid Card Visa
14 5100000000000511 Invalid Card MasterCard
15 4003830171874018 CVV Match Fail Visa
16 5496198584584769 CVV Match Fail MasterCard
17 4000000000000069 Expired Card Visa
18 5105105105105100 Expired Card MasterCard
19 4111110000000112 Unknown Failure Visa
20 5124990000000002 Unknown Failure MasterCard

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="LOG_FILE"
value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}/@artifactId@.json}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="com.rbkmoney.log.appender.NoopRollingPolicy"/>
<triggeringPolicy class="com.rbkmoney.log.appender.RotationBasedTriggeringPolicy">
<checkCachePeriod>5000</checkCachePeriod>
</triggeringPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<mdc/>
<threadName/>
<message/>
<version/>
<loggerName/>
<context/>
<pattern>
<pattern>
{
"@timestamp": "%date{yyy-MM-dd'T'HH:mm:ss.SSSXXX, UTC}",
"@severity": "%level",
"application": "@artifactId@",
"application_version": "@version@"
}
</pattern>
</pattern>
<stackTrace>
<throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
<shortenedClassNameLength>20</shortenedClassNameLength>
<rootCauseFirst>true</rootCauseFirst>
</throwableConverter>
</stackTrace>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<logger name="com.rbkmoney" level="ALL"/>
<jmxConfigurator/>
</configuration>

View File

@ -0,0 +1,19 @@
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Acs form</title>
</head>
<body>
<form action="#" th:action="${action}" method="post">
<p th:text="'Маскированная карта: ' + ${pan} "></p>
<input type="hidden" name="PaRes" th:value="${PaRes}" />
<input type="hidden" name="MD" th:value="${MD}" />
Введите код SMS: <input type='text' name='' value='' /> <br />
<input type='submit' value='Подтвердить'/>
</form>
</body>
</html>