GP-2: GooglePay Implementation

This commit is contained in:
Vladimir Pankrashkin 2018-05-29 20:12:32 +03:00
parent 6801102d06
commit 7b29b97253
29 changed files with 1451 additions and 0 deletions

119
.gitignore vendored Normal file
View File

@ -0,0 +1,119 @@
# Created by .ignore support plugin (hsz.mobi)
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
!/.mvn/wrapper/maven-wrapper.jar
### Java template
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# 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/**/workspace.xml
.idea/**/tasks.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
cmake-build-release/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
*.ipr
*.iml
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests

183
pom.xml Normal file
View File

@ -0,0 +1,183 @@
<?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>2.0.1.RELEASE</version>
</parent>
<groupId>com.rbkmoney.provider</groupId>
<artifactId>googlepay</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<project.maintainer>Vladimir Pankrashkin &lt;v.pankrashkin@rbkmoney.com&gt;</project.maintainer>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<dockerfile.base.service.tag>22c57470c4fc47161894f036b7cf9d70f42b75f5</dockerfile.base.service.tag>
<damsel.version>1.236-1510cd7</damsel.version>
<shared.resources.version>0.3.2</shared.resources.version>
<server.port>8022</server.port>
<exposed.ports>${server.port}</exposed.ports>
</properties>
<dependencies>
<!--RBK libs-->
<dependency>
<groupId>com.rbkmoney.woody</groupId>
<artifactId>woody-thrift</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>common</artifactId>
<version>0.6.9</version>
</dependency>
<dependency>
<groupId>com.rbkmoney.logback</groupId>
<artifactId>nop-rolling</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>damsel</artifactId>
<version>${damsel.version}</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>shared-resources</artifactId>
<version>${shared.resources.version}</version>
</dependency>
<!--Thridparty libs-->
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>apps-paymentmethodtoken</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>24.1-jre</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Test libs-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${project.build.directory}/maven-shared-archive-resources</directory>
<targetPath>${project.build.directory}</targetPath>
<includes>
<include>Dockerfile</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>${project.build.directory}/maven-shared-archive-resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>Dockerfile</exclude>
</excludes>
</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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-remote-resources-plugin</artifactId>
<version>1.5</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-filtering</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<configuration>
<resourceBundles>
<resourceBundle>com.rbkmoney:shared-resources:${shared.resources.version}</resourceBundle>
</resourceBundles>
<attachToMain>false</attachToMain>
<attachToTest>false</attachToTest>
</configuration>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,17 @@
package com.rbkmoney.provider.googlepay;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
/**
* Created by vpankrashkin on 29.05.18.
*/
@SpringBootApplication(scanBasePackages = "com.rbkmoney.provider.googlepay")
@ServletComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,42 @@
package com.rbkmoney.provider.googlepay.config;
import com.google.crypto.tink.apps.paymentmethodtoken.GooglePaymentsPublicKeysManager;
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenRecipient;
import com.rbkmoney.damsel.payment_tool_provider.PaymentToolProviderSrv;
import com.rbkmoney.provider.googlepay.iface.decrypt.ProviderHandler;
import com.rbkmoney.provider.googlepay.service.DecryptionService;
import com.rbkmoney.provider.googlepay.service.GPKeyStore;
import com.rbkmoney.provider.googlepay.service.ValidationService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.security.GeneralSecurityException;
/**
* Created by vpankrashkin on 29.05.18.
*/
@Configuration
public class AppConfig {
@Bean
public DecryptionService decryptionService(@Value("${google.test}") boolean testMode, @Value("${google.gateway_id}") String gatewayId, @Value("${google.keys_path}")Resource keys) throws GeneralSecurityException, IOException {
PaymentMethodTokenRecipient.Builder builder = new PaymentMethodTokenRecipient.Builder();
builder.fetchSenderVerifyingKeysWith(
testMode ? GooglePaymentsPublicKeysManager.INSTANCE_TEST : GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
.recipientId("gateway:"+gatewayId);
for (String key: new GPKeyStore(keys.getURI()).getKeys()) {
builder.addRecipientPrivateKey(key);
}
return new DecryptionService(builder.build());
}
@Bean
public PaymentToolProviderSrv.Iface decryptHandler(DecryptionService decryptionService, @Value("${google.use_validation}") boolean enableValidation) {
return new ProviderHandler(new ValidationService(), decryptionService, enableValidation);
}
}

View File

@ -0,0 +1,8 @@
package com.rbkmoney.provider.googlepay.domain;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class Auth {
}

View File

@ -0,0 +1,49 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Auth3DS extends Auth {
private String cryptogram;
private String eci;
@JsonCreator
public Auth3DS(
@JsonProperty(value = "3dsCryptogram", required = true) String cryptogram,
@JsonProperty(value = "3dsEciIndicator") String eci) {
this.cryptogram = cryptogram;
this.eci = eci;
}
public Auth3DS() {
}
public String getCryptogram() {
return cryptogram;
}
public void setCryptogram(String cryptogram) {
this.cryptogram = cryptogram;
}
public String getEci() {
return eci;
}
public void setEci(String eci) {
this.eci = eci;
}
@Override
public String toString() {
return "Auth3DS{" +
"cryptogram='" + (cryptogram == null ? null : "***") + '\'' +
", eci='" + (eci == null ? null : "***") + '\'' +
"}";
}
}

View File

@ -0,0 +1,11 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
public enum AuthType {
@JsonProperty("3DS")
AUTH_3DS
}

View File

@ -0,0 +1,59 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class Card extends PaymentCredential {
private String pan;
private int expirationMonth;
private int expirationYear;
@JsonCreator
public Card(
@JsonProperty(value = "pan", required = true) String pan,
@JsonProperty(value = "expirationMonth", required = true) int expirationMonth,
@JsonProperty(value = "expirationYear", required = true) int expirationYear) {
this.pan = pan;
this.expirationMonth = expirationMonth;
this.expirationYear = expirationYear;
}
public Card() {
}
public String getPan() {
return pan;
}
public void setPan(String pan) {
this.pan = pan;
}
public int getExpirationMonth() {
return expirationMonth;
}
public void setExpirationMonth(int expirationMonth) {
this.expirationMonth = expirationMonth;
}
public int getExpirationYear() {
return expirationYear;
}
public void setExpirationYear(int expirationYear) {
this.expirationYear = expirationYear;
}
@Override
public String toString() {
return "Card{" +
"pan='" + (pan == null ? null: "***") + '\'' +
", expirationMonth=" + "**" +
", expirationYear=" + "****" +
'}';
}
}

View File

@ -0,0 +1,73 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CardInfo {
private String cardDescription;
private String cardClass;
private String last4digits;
private String cardNetwork;
@JsonCreator
public CardInfo(
@JsonProperty(value = "cardDescription", required = true) String cardDescription,
@JsonProperty(value = "cardClass", required = true) String cardClass,
@JsonProperty(value = "cardDetails", required = true) String last4digits,
@JsonProperty(value = "cardNetwork", required = true) String cardNetwork) {
this.cardDescription = cardDescription;
this.cardClass = cardClass;
this.last4digits = last4digits;
this.cardNetwork = cardNetwork;
}
public CardInfo() {
}
public String getCardDescription() {
return cardDescription;
}
public void setCardDescription(String cardDescription) {
this.cardDescription = cardDescription;
}
public String getCardNetwork() {
return cardNetwork;
}
public void setCardNetwork(String cardNetwork) {
this.cardNetwork = cardNetwork;
}
public String getCardClass() {
return cardClass;
}
public void setCardClass(String cardClass) {
this.cardClass = cardClass;
}
public String getLast4digits() {
return last4digits;
}
public void setLast4digits(String last4digits) {
this.last4digits = last4digits;
}
@Override
public String toString() {
return "CardInfo{" +
"cardDescription='" + cardDescription + '\'' +
", cardClass='" + cardClass + '\'' +
", last4digits='" + last4digits + '\'' +
", cardNetwork='" + cardNetwork + '\'' +
'}';
}
}

View File

@ -0,0 +1,100 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.rbkmoney.provider.googlepay.service.DecryptedMessageDeserializer;
import java.time.Instant;
import java.util.Map;
/**
* Created by vpankrashkin on 28.05.18.
*/
@JsonDeserialize(using = DecryptedMessageDeserializer.class)
public class DecryptedMessage {
private Instant expiration;
private String messageId;
private String gatewayMerchantId;
private PaymentMethod paymentMethod;
private PaymentCredential paymentCredential;
private Map<String, Object> paymentCredentialMap;
public DecryptedMessage() {
}
@JsonCreator
public DecryptedMessage(
@JsonProperty(value = "messageExpiration", required = true) Instant expiration,
@JsonProperty(value = "messageId", required = true) String messageId,
@JsonProperty(value = "gatewayMerchantId", required = true) String gatewayMerchantId,
@JsonProperty(value = "paymentMethod", required = true) PaymentMethod paymentMethod,
@JsonProperty(value = "paymentMethodDetails", required = true) Map<String, Object> paymentCredentialMap) {
this.expiration = expiration;
this.messageId = messageId;
this.gatewayMerchantId = gatewayMerchantId;
this.paymentMethod = paymentMethod;
this.paymentCredentialMap = paymentCredentialMap;
}
public Instant getExpiration() {
return expiration;
}
public void setExpiration(Instant expiration) {
this.expiration = expiration;
}
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public String getGatewayMerchantId() {
return gatewayMerchantId;
}
public void setGatewayMerchantId(String gatewayMerchantId) {
this.gatewayMerchantId = gatewayMerchantId;
}
public PaymentMethod getPaymentMethod() {
return paymentMethod;
}
public void setPaymentMethod(PaymentMethod paymentMethod) {
this.paymentMethod = paymentMethod;
}
public PaymentCredential getPaymentCredential() {
return paymentCredential;
}
public void setPaymentCredential(PaymentCredential paymentCredential) {
this.paymentCredential = paymentCredential;
}
public Map<String, Object> getPaymentCredentialMap() {
return paymentCredentialMap;
}
public void setPaymentCredentialMap(Map<String, Object> paymentCredentialMap) {
this.paymentCredentialMap = paymentCredentialMap;
}
@Override
public String toString() {
return "DecryptedMessage{" +
"expiration=" + expiration +
", messageId='" + messageId + '\'' +
", gatewayMerchantId='" + gatewayMerchantId + '\'' +
", paymentMethod=" + paymentMethod +
", paymentCredential=" + paymentCredential +
", paymentCredentialMap=" + paymentCredentialMap.size() +
'}';
}
}

View File

@ -0,0 +1,8 @@
package com.rbkmoney.provider.googlepay.domain;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class PaymentCredential {
}

View File

@ -0,0 +1,49 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PaymentData {
private CardInfo cardInfo;
private PaymentToken paymentMethodToken;
@JsonCreator
public PaymentData(
@JsonProperty(value = "paymentMethodToken", required = true) PaymentToken paymentMethodToken,
@JsonProperty(value = "cardInfo", required = true) CardInfo cardInfo) {
this.paymentMethodToken = paymentMethodToken;
this.cardInfo = cardInfo;
}
public PaymentData() {
}
public PaymentToken getPaymentMethodToken() {
return paymentMethodToken;
}
public void setPaymentMethodToken(PaymentToken paymentMethodToken) {
this.paymentMethodToken = paymentMethodToken;
}
public CardInfo getCardInfo() {
return cardInfo;
}
public void setCardInfo(CardInfo cardInfo) {
this.cardInfo = cardInfo;
}
@Override
public String toString() {
return "PaymentData{" +
"cardInfo=" + cardInfo +
", paymentMethodToken=" + paymentMethodToken +
'}';
}
}

View File

@ -0,0 +1,13 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
public enum PaymentMethod {
@JsonProperty("TOKENIZED_CARD")
TOKENIZED,
@JsonProperty("CARD")
CARD
}

View File

@ -0,0 +1,47 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class PaymentToken {
private String token;
private TokenizationType tokenizationType;
@JsonCreator
public PaymentToken(
@JsonProperty(value = "tokenizationType", required = true) TokenizationType tokenizationType,
@JsonProperty(value = "token", required = true) String token) {
this.tokenizationType = tokenizationType;
this.token = token;
}
public PaymentToken() {
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public TokenizationType getTokenizationType() {
return tokenizationType;
}
public void setTokenizationType(TokenizationType tokenizationType) {
this.tokenizationType = tokenizationType;
}
@Override
public String toString() {
return "PaymentToken{" +
"token='" + token + '\'' +
", tokenizationType=" + tokenizationType +
'}';
}
}

View File

@ -0,0 +1,13 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Created by vpankrashkin on 28.05.18.
*/
public enum TokenizationType {
@JsonProperty("PAYMENT_GATEWAY")
GATEWAY,
//@JsonProperty("DIRECT") - not supported
//DIRECT
}

View File

@ -0,0 +1,86 @@
package com.rbkmoney.provider.googlepay.domain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.rbkmoney.provider.googlepay.service.TokenizedCardDeserializer;
/**
* Created by vpankrashkin on 28.05.18.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonDeserialize(using = TokenizedCardDeserializer.class)
public class TokenizedCard extends PaymentCredential {
private String dpan;
private int expirationMonth;
private int expirationYear;
private AuthType authType;
private Auth auth;
@JsonCreator
public TokenizedCard(
@JsonProperty(value = "dpan", required = true) String dpan,
@JsonProperty(value = "expirationMonth", required = true) int expirationMonth,
@JsonProperty(value = "expirationYear", required = true) int expirationYear,
@JsonProperty(value = "authMethod", required = true) AuthType authType) {
this.dpan = dpan;
this.expirationMonth = expirationMonth;
this.expirationYear = expirationYear;
this.authType = authType;
}
public TokenizedCard() {
}
public String getDpan() {
return dpan;
}
public void setDpan(String dpan) {
this.dpan = dpan;
}
public int getExpirationMonth() {
return expirationMonth;
}
public void setExpirationMonth(int expirationMonth) {
this.expirationMonth = expirationMonth;
}
public int getExpirationYear() {
return expirationYear;
}
public void setExpirationYear(int expirationYear) {
this.expirationYear = expirationYear;
}
public AuthType getAuthType() {
return authType;
}
public void setAuthType(AuthType authType) {
this.authType = authType;
}
public Auth getAuth() {
return auth;
}
public void setAuth(Auth auth) {
this.auth = auth;
}
@Override
public String toString() {
return "TokenizedCard{" +
"dpan='" + (dpan == null ? null : "***") + '\'' +
", expirationMonth=" + "**" +
", expirationYear=" + "****" +
", authType=" + authType +
", auth=" + auth +
"} ";
}
}

View File

@ -0,0 +1,144 @@
package com.rbkmoney.provider.googlepay.iface.decrypt;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.rbkmoney.damsel.base.Content;
import com.rbkmoney.damsel.base.InvalidRequest;
import com.rbkmoney.damsel.domain.BankCardPaymentSystem;
import com.rbkmoney.damsel.payment_tool_provider.*;
import com.rbkmoney.geck.common.util.TypeUtil;
import com.rbkmoney.provider.googlepay.domain.DecryptedMessage;
import com.rbkmoney.provider.googlepay.domain.PaymentData;
import com.rbkmoney.provider.googlepay.domain.PaymentToken;
import com.rbkmoney.provider.googlepay.service.CryptoException;
import com.rbkmoney.provider.googlepay.service.DecryptionService;
import com.rbkmoney.provider.googlepay.service.ValidationException;
import com.rbkmoney.provider.googlepay.service.ValidationService;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;
/**
* Created by vpankrashkin on 03.04.18.
*/
public class ProviderHandler implements PaymentToolProviderSrv.Iface {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final ValidationService validator;
private final DecryptionService decryptor;
private final ObjectReader srcReader = new ObjectMapper().readerFor(PaymentData.class);
private final ObjectReader resReader = new ObjectMapper().readerFor(DecryptedMessage.class);
private final boolean enableValidation;
public ProviderHandler(ValidationService validator, DecryptionService decryptor, boolean enableValidation) {
this.validator = validator;
this.decryptor = decryptor;
this.enableValidation = enableValidation;
}
@Override
public UnwrappedPaymentTool unwrap(WrappedPaymentTool payment_tool) throws InvalidRequest, TException {
log.info("New unwrap request: {}", payment_tool);
Content content = payment_tool.getRequest().getGoogle().getPaymentToken();
if (!content.getType().equalsIgnoreCase("application/json")) {
throw new InvalidRequest(Arrays.asList("Wrong content type"));
}
try {
PaymentData paymentData = srcReader.readValue(content.getData());
PaymentToken paymentToken = paymentData.getPaymentMethodToken();
String tokenData = decryptor.decryptToken(paymentToken.getToken());
log.info("Payment data decrypted");
DecryptedMessage decryptedMessage = resReader.readValue(tokenData);
log.info("Decryption result: {}", decryptedMessage);
if (enableValidation) {
validator.validate(payment_tool.getRequest().getGoogle().getGatewayMerchantId(), decryptedMessage);
log.info("Request successfully validated");
} else {
log.info("Request validation skipped");
}
UnwrappedPaymentTool result = new UnwrappedPaymentTool();
result.setCardInfo(extractCardInfo(paymentData.getCardInfo()));
result.setPaymentData(extractPaymentData(decryptedMessage));
result.setDetails(extractPaymentDetails(decryptedMessage));
UnwrappedPaymentTool logResult = new UnwrappedPaymentTool(result);
if (logResult.getPaymentData().isSetCard()) {
logResult.getPaymentData().setCard(new Card());
} else {
logResult.getPaymentData().setTokenizedCard(new TokenizedCard());
}
log.info("Unwrap partial result: {}", logResult);
return result;
} catch (IOException e) {
log.error("Failed to read json data: {}", filterPan(e.getMessage()));
throw new InvalidRequest(Arrays.asList("Failed to read json data"));
} catch (ValidationException e) {
log.error("Failed to validate request", e);
throw new InvalidRequest(Arrays.asList(e.getMessage()));
} catch (CryptoException e) {
log.error("Decryption error", e);
throw new RuntimeException(e);
}
}
private String filterPan(String src) {
return src.replaceAll("([^\\d])\\d{8,19}([^\\d])", "$1***$2");
}
private CardInfo extractCardInfo(com.rbkmoney.provider.googlepay.domain.CardInfo srcCardInfo) {
CardInfo cardInfo = new CardInfo();
cardInfo.setDisplayName(srcCardInfo.getCardDescription());
cardInfo.setLast4Digits(srcCardInfo.getLast4digits());
cardInfo.setCardClass(TypeUtil.toEnumField(
Optional.of(srcCardInfo.getCardClass()).map(s -> s.toLowerCase()).orElse(null),
CardClass.class, CardClass.unknown));
cardInfo.setPaymentSystem(TypeUtil.toEnumField(
Optional.ofNullable(srcCardInfo.getCardNetwork()).map(s -> s.toLowerCase()).orElse(null),
BankCardPaymentSystem.class, null));
return cardInfo;
}
private CardPaymentData extractPaymentData(DecryptedMessage decryptedMessage) {
CardPaymentData cardPaymentData = new CardPaymentData();
switch (decryptedMessage.getPaymentMethod()) {
case CARD:
com.rbkmoney.provider.googlepay.domain.Card srcCard = (com.rbkmoney.provider.googlepay.domain.Card) decryptedMessage.getPaymentCredential();
Card card = new Card(srcCard.getPan(), new ExpDate((byte) srcCard.getExpirationMonth(), (short) srcCard.getExpirationYear()));
cardPaymentData.setCard(card);
break;
case TOKENIZED:
com.rbkmoney.provider.googlepay.domain.TokenizedCard srcToken = (com.rbkmoney.provider.googlepay.domain.TokenizedCard) decryptedMessage.getPaymentCredential();
AuthData authData;
switch (srcToken.getAuthType()) {
case AUTH_3DS:
com.rbkmoney.provider.googlepay.domain.Auth3DS srcAuth3DS = (com.rbkmoney.provider.googlepay.domain.Auth3DS) srcToken.getAuth();
Auth3DS auth3DS = new Auth3DS(srcAuth3DS.getCryptogram());
auth3DS.setEci(srcAuth3DS.getEci());
authData = AuthData.auth_3ds(auth3DS);
break;
default:
throw new ValidationException("Unknown type:" + srcToken.getAuthType());
}
TokenizedCard tokenizedCard = new TokenizedCard(
srcToken.getDpan(),
new ExpDate((byte) srcToken.getExpirationMonth(), (short) srcToken.getExpirationYear()),
authData);
cardPaymentData.setTokenizedCard(tokenizedCard);
break;
default:
throw new ValidationException("Unknown type:" + decryptedMessage.getPaymentMethod());
}
return cardPaymentData;
}
private PaymentDetails extractPaymentDetails(DecryptedMessage decryptedMessage) {
return PaymentDetails.google(new GooglePayDetails(decryptedMessage.getMessageId(), TypeUtil.temporalToString(decryptedMessage.getExpiration())));
}
}

View File

@ -0,0 +1,27 @@
package com.rbkmoney.provider.googlepay.iface.decrypt;
import com.rbkmoney.damsel.payment_tool_provider.PaymentToolProviderSrv;
import com.rbkmoney.woody.thrift.impl.http.THServiceBuilder;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
/**
* Created by vpankrashkin on 29.05.18.
*/
@WebServlet("/provider/google")
public class ProviderServlet extends GenericServlet {
private final Servlet handlerServlet;
public ProviderServlet(PaymentToolProviderSrv.Iface handler) {
this.handlerServlet = new THServiceBuilder()
.build(PaymentToolProviderSrv.Iface.class, handler);
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
handlerServlet.service(req, res);
}
}

View File

@ -0,0 +1,25 @@
package com.rbkmoney.provider.googlepay.service;
/**
* Created by vpankrashkin on 12.04.18.
*/
public class CryptoException extends Exception {
public CryptoException() {
}
public CryptoException(String message) {
super(message);
}
public CryptoException(String message, Throwable cause) {
super(message, cause);
}
public CryptoException(Throwable cause) {
super(cause);
}
public CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,59 @@
package com.rbkmoney.provider.googlepay.service;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.rbkmoney.provider.googlepay.domain.Card;
import com.rbkmoney.provider.googlepay.domain.DecryptedMessage;
import com.rbkmoney.provider.googlepay.domain.TokenizedCard;
import java.io.IOException;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class DecryptedMessageDeserializer extends StdDeserializer<DecryptedMessage> {
private final ObjectMapper mapper;
public DecryptedMessageDeserializer() {
super(DecryptedMessage.class);
JacksonAnnotationIntrospector introspector = new JacksonAnnotationIntrospector() {
@Override
public Object findDeserializer(Annotated a) {
if (a.getRawType() == DecryptedMessage.class)
return null;
return super.findDeserializer(a);
}
};
mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
mapper.setAnnotationIntrospector(introspector);
}
@Override
public DecryptedMessage deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
TreeNode root = parser.readValueAsTree();
DecryptedMessage message = mapper.treeToValue(root, DecryptedMessage.class);
switch (message.getPaymentMethod()) {
case CARD:
message.setPaymentCredential(mapper.convertValue(message.getPaymentCredentialMap(), Card.class));
break;
case TOKENIZED:
message.setPaymentCredential(mapper.convertValue(message.getPaymentCredentialMap(), TokenizedCard.class));
break;
}
return message;
}
}

View File

@ -0,0 +1,24 @@
package com.rbkmoney.provider.googlepay.service;
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenRecipient;
import java.security.GeneralSecurityException;
/**
* Created by vpankrashkin on 29.05.18.
*/
public class DecryptionService {
private final PaymentMethodTokenRecipient decryptor;
public DecryptionService(PaymentMethodTokenRecipient decryptor) {
this.decryptor = decryptor;
}
public String decryptToken(String paymentToken) throws CryptoException {
try {
return decryptor.unseal(paymentToken);
} catch (GeneralSecurityException e) {
throw new CryptoException("Message decryption failed", e);
}
}
}

View File

@ -0,0 +1,22 @@
package com.rbkmoney.provider.googlepay.service;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
/**
* Created by vpankrashkin on 29.05.18.
*/
public class GPKeyStore {
private final URI path;
public GPKeyStore(URI path) {
this.path = path;
}
public List<String> getKeys() throws IOException {
return Files.readAllLines(Paths.get(path));
}
}

View File

@ -0,0 +1,48 @@
package com.rbkmoney.provider.googlepay.service;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.rbkmoney.provider.googlepay.domain.Auth3DS;
import com.rbkmoney.provider.googlepay.domain.TokenizedCard;
import java.io.IOException;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class TokenizedCardDeserializer extends JsonDeserializer<TokenizedCard> {
private final ObjectMapper mapper;
public TokenizedCardDeserializer() {
JacksonAnnotationIntrospector introspector = new JacksonAnnotationIntrospector() {
@Override
public Object findDeserializer(Annotated a) {
if (a.getRawType() == TokenizedCard.class)
return null;
return super.findDeserializer(a);
}
};
mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(introspector);
}
@Override
public TokenizedCard deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
TreeNode root = parser.readValueAsTree();
TokenizedCard tokenizedCard = mapper.treeToValue(root, TokenizedCard.class);
switch (tokenizedCard.getAuthType()) {
case AUTH_3DS:
tokenizedCard.setAuth(mapper.reader().treeToValue(root, Auth3DS.class));
break;
}
return tokenizedCard;
}
}

View File

@ -0,0 +1,25 @@
package com.rbkmoney.provider.googlepay.service;
/**
* Created by vpankrashkin on 28.05.18.
*/
public class ValidationException extends RuntimeException {
public ValidationException() {
}
public ValidationException(String message) {
super(message);
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(Throwable cause) {
super(cause);
}
public ValidationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,20 @@
package com.rbkmoney.provider.googlepay.service;
import com.rbkmoney.provider.googlepay.domain.DecryptedMessage;
import java.time.Instant;
/**
* Created by vpankrashkin on 29.05.18.
*/
public class ValidationService {
public void validate(String gMerchantId, DecryptedMessage message) throws ValidationException {
if (!gMerchantId.equals(message.getGatewayMerchantId())) {
throw new ValidationException(String.format("Merchant ID: '%s' doesn't match decrypted one: '%s'", gMerchantId, message.getGatewayMerchantId()));
}
if (Instant.now().isAfter(message.getExpiration())) {
throw new ValidationException("Message expired: "+message.getExpiration());
}
}
}

View File

@ -0,0 +1,11 @@
server.port=@server.port@
spring.application.name=@project.name@
info.version=@project.version@
info.damsel.version=@damsel.version@
google.test=true
google.gateway_id=rbkmoney
google.keys_path=classpath:keys.txt
google.use_validation=true

View File

@ -0,0 +1,82 @@
package com.rbkmoney.provider.googlepay;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.crypto.tink.apps.paymentmethodtoken.GooglePaymentsPublicKeysManager;
import com.google.crypto.tink.apps.paymentmethodtoken.PaymentMethodTokenRecipient;
import com.rbkmoney.provider.googlepay.domain.DecryptedMessage;
import com.rbkmoney.provider.googlepay.domain.PaymentData;
import com.rbkmoney.provider.googlepay.service.GPKeyStore;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
/**
* Created by vpankrashkin on 25.05.18.Ø
*/
public class DataTest {
static {
GooglePaymentsPublicKeysManager.INSTANCE_TEST.refreshInBackground();
}
@org.junit.Test
public void test() throws GeneralSecurityException, IOException, URISyntaxException {
String paymentMethodToken = "{\"signature\":\"MEUCIQDiT2/dI+H7LiiP0Yu/p8Z62Laumnk/js9cQrTUpYufIAIgN+e3tDoBDnqk6pt+MEwsVWD3oq/DfbbcLPJgdo3AMfM\\u003d\",\"protocolVersion\":\"ECv1\",\"signedMessage\":\"{\\\"encryptedMessage\\\":\\\"k84ZAjkWfAibZmWxJfBJ8+3lhNiVvPrMYvLsZQwSt5dEmvO1S7davQ5rq04MNLKjwRezz301L6kDxv6abe2M7Z2KIM/tlmgtsAlV2xbOxS2piChvEeB0RyVbAxjLKfL5vnOlxivLgoPbCUrm7/hSwxsPhjVvyTXMCBbo744ZnIINuk8W3aROtFnGBqdzWSEjmlrhUBHoTJ1IEMuowoklUyI2Qd8pCQHA4it8mvITPE/KDQNt4J4HmNAx1GkW/Va9SEHZXP/J5xNjFeijshDZgd9oam1JB2yg3sgIh3e2JmgkVChqHoilaEdjKTLJc95FlJfbZyxytmh4E7jxvJ//vB5ySGV1pyVVRT8IX6K2oIXjsJ+JAZQmr7/MNo7W/cq6guz07nFHMqk8FCJbsWA75krj4Y9Ls8QkDyI7ccayapE2N9/QP5koLglASxWFfUEgWyWPWA\\\\u003d\\\\u003d\\\",\\\"ephemeralPublicKey\\\":\\\"BLhY0C4bLP+Cv6AKH7Hz2YKICO2QqFZYxFDOWZ07S0LfEAwCmiusTIW/gz377AveU3Fw5JNKZPNjHkBG4OkEnKk\\\\u003d\\\",\\\"tag\\\":\\\"JvdgTyndj5fDugbG3rEoN52adgA/x24aGmRAKyoqxrk\\\\u003d\\\"}\"}";
PaymentMethodTokenRecipient.Builder builder = new PaymentMethodTokenRecipient.Builder();
builder.fetchSenderVerifyingKeysWith(
GooglePaymentsPublicKeysManager.INSTANCE_TEST)
.recipientId("gateway:rbkmoney");
for (String key: new GPKeyStore(this.getClass().getClassLoader().getResource("keys.txt").toURI()).getKeys()) {
builder.addRecipientPrivateKey(key);
}
String decryptedMessage = builder.build()
.unseal(paymentMethodToken);
System.out.println(decryptedMessage);
}
@org.junit.Test
public void testDeserializationEncryptedMsg() throws IOException {
String encCard = "{\n" +
" \"cardInfo\": {\n" +
" \"cardNetwork\": \"VISA\",\n" +
" \"cardDetails\": \"9391\",\n" +
" \"cardImageUri\": \"https://lh6.ggpht.com/NvYf_33MleY1waJfW6O98wb3KU6XeinwiahmvUIyu46LcWeQdTMGm7WYe81uZYWLUbkjvz0E\",\n" +
" \"cardDescription\": \"Visa••••9391\",\n" +
" \"cardClass\": \"CREDIT\"\n" +
" },\n" +
" \"paymentMethodToken\": {\n" +
" \"tokenizationType\": \"PAYMENT_GATEWAY\",\n" +
" \"token\": \"{\\\"signature\\\":\\\"MEUCIQCBj5ClFzpJTg3UB4yKdXRP5O12vqpcTCM+x51/99ugYAIgPnCmr8k2G4oKWQqxmAawPgTd42vX1rx91eyqIHgSju4\\\\u003d\\\",\\\"protocolVersion\\\":\\\"ECv1\\\",\\\"signedMessage\\\":\\\"{\\\\\\\"encryptedMessage\\\\\\\":\\\\\\\"tSj1k3qfKoVg5rcZg8uOzMaGrk31qTIUkjYehgjV7BVAC/SGVfPE40q9PHppoZJqFsRwM1xMKKpoZatHa/fct/2YlVwxySxNR5uu3PHR4ZzuWb7vh/Nnb8BWlSiUMMELLaHFA1+FR2zbLpgEG9QdmeQRpyq07SETaH43WF0MLTDbKr9Con3SLZVCnoNS6CZZsPVTYNm0IjLWABAf66O5VlMWAPwke+kMaiJmq5ttPaxn6zGVJq4/JT7T7iKuc/T0rWDh7BzzVEzi/wyvaYDl2SkOljqFcjNYMTEr76GHlsgpAjtzCCPJkPJ9m7wkd3v+mgdQgazJ+ZwFvKTk8Ru7wZ4eywgb1G36/+IBpH/7936PvkayOTuXgtQkjts027mnH9/SyRA3unoJrNXRpUPytxwVO048edVUBKB9Q4ZZYadeGErH4xRTg64fPS9KiHSswBeY\\\\\\\",\\\\\\\"ephemeralPublicKey\\\\\\\":\\\\\\\"BLeiRaJ1+9DkK9x8rPK1377KKgQVv3kSbomJZxoTgqaOugBa8PAR3QDIH3VfqscW8zfMbrNrA736Nm4AahbogaU\\\\\\\\u003d\\\\\\\",\\\\\\\"tag\\\\\\\":\\\\\\\"oBPVR6dw8t4GM0ZC3QLd45hcx6oeag5D98BSdIsV0No\\\\\\\\u003d\\\\\\\"}\\\"}\"\n" +
" },\n" +
" \"email\": \"keinasylum@gmail.com\"\n" +
"}";
PaymentData paymentData = new ObjectMapper().readerFor(PaymentData.class).readValue(encCard);
System.out.println(paymentData);
}
@org.junit.Test
public void testDeserializationDecryptedMsg() throws IOException {
String decCard = "{\"gatewayMerchantId\":\"fadsfasdf\",\"messageExpiration\":\"1527861216388\",\"messageId\":\"AH2EjteSBP3heqxYKyKkh88NzU95u3sIc6Kup-vwgbF9k3_SzwWBylswvYvHSEdjwzYorh-tQ1VGEvTGht4c2CJdb0Pm9eDzfKtiIjstdl0QcbUWcNQM83ujxFhBmKcHG809dgnsWuOB\",\"paymentMethod\":\"CARD\",\"paymentMethodDetails\":{\"expirationYear\":2023,\"expirationMonth\":12,\"pan\":\"4111111111111111\"}}";
DecryptedMessage decCardMsg = new ObjectMapper().readerFor(DecryptedMessage.class).readValue(decCard);
System.out.println(decCardMsg);
String decToken = "{\n" +
"\"gatewayMerchantId\":\"fadsfasdf\","+
" \"paymentMethod\": \"TOKENIZED_CARD\",\n" +
" \"paymentMethodDetails\": {\n" +
" \"dpan\": \"4444444444444444\",\n" +
" \"expirationMonth\": 10,\n" +
" \"expirationYear\": 2020,\n" +
" \"authMethod\": \"3DS\",\n" +
" \"3dsCryptogram\": \"AAAAAA...\",\n" +
" \"3dsEciIndicator\": \"eci indicator\"\n" +
" },\n" +
" \n" +
" \"messageId\": \"some-message-id\",\n" +
" \"messageExpiration\": \"1520836260646\"\n" +
"}";
DecryptedMessage decTokenMsg = new ObjectMapper().readerFor(DecryptedMessage.class).readValue(decToken);
System.out.println(decTokenMsg);
}
}

View File

@ -0,0 +1,86 @@
package com.rbkmoney.provider.googlepay;
import com.rbkmoney.damsel.base.Content;
import com.rbkmoney.damsel.base.InvalidRequest;
import com.rbkmoney.damsel.domain.BankCardPaymentSystem;
import com.rbkmoney.damsel.payment_tool_provider.*;
import com.rbkmoney.woody.thrift.impl.http.THClientBuilder;
import org.apache.thrift.TException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.time.Instant;
import static org.junit.Assert.*;
/**
* Created by vpankrashkin on 29.05.18.
*/
@RunWith(SpringRunner.class)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT
)
public class IntegrationTest {
@Value("http://127.0.0.1:${server.port}/provider/google")
private String url;
private PaymentToolProviderSrv.Iface client;
private String encMsg = "{\n" +
" \"cardInfo\": {\n" +
" \"cardNetwork\": \"VISA\",\n" +
" \"cardDetails\": \"9391\",\n" +
" \"cardImageUri\": \"https://lh6.ggpht.com/NvYf_33MleY1waJfW6O98wb3KU6XeinwiahmvUIyu46LcWeQdTMGm7WYe81uZYWLUbkjvz0E\",\n" +
" \"cardDescription\": \"Visa••••9391\",\n" +
" \"cardClass\": \"CREDIT\"\n" +
" },\n" +
" \"paymentMethodToken\": {\n" +
" \"tokenizationType\": \"PAYMENT_GATEWAY\",\n" +
" \"token\": \"{\\\"signature\\\":\\\"MEUCIQCBj5ClFzpJTg3UB4yKdXRP5O12vqpcTCM+x51/99ugYAIgPnCmr8k2G4oKWQqxmAawPgTd42vX1rx91eyqIHgSju4\\\\u003d\\\",\\\"protocolVersion\\\":\\\"ECv1\\\",\\\"signedMessage\\\":\\\"{\\\\\\\"encryptedMessage\\\\\\\":\\\\\\\"tSj1k3qfKoVg5rcZg8uOzMaGrk31qTIUkjYehgjV7BVAC/SGVfPE40q9PHppoZJqFsRwM1xMKKpoZatHa/fct/2YlVwxySxNR5uu3PHR4ZzuWb7vh/Nnb8BWlSiUMMELLaHFA1+FR2zbLpgEG9QdmeQRpyq07SETaH43WF0MLTDbKr9Con3SLZVCnoNS6CZZsPVTYNm0IjLWABAf66O5VlMWAPwke+kMaiJmq5ttPaxn6zGVJq4/JT7T7iKuc/T0rWDh7BzzVEzi/wyvaYDl2SkOljqFcjNYMTEr76GHlsgpAjtzCCPJkPJ9m7wkd3v+mgdQgazJ+ZwFvKTk8Ru7wZ4eywgb1G36/+IBpH/7936PvkayOTuXgtQkjts027mnH9/SyRA3unoJrNXRpUPytxwVO048edVUBKB9Q4ZZYadeGErH4xRTg64fPS9KiHSswBeY\\\\\\\",\\\\\\\"ephemeralPublicKey\\\\\\\":\\\\\\\"BLeiRaJ1+9DkK9x8rPK1377KKgQVv3kSbomJZxoTgqaOugBa8PAR3QDIH3VfqscW8zfMbrNrA736Nm4AahbogaU\\\\\\\\u003d\\\\\\\",\\\\\\\"tag\\\\\\\":\\\\\\\"oBPVR6dw8t4GM0ZC3QLd45hcx6oeag5D98BSdIsV0No\\\\\\\\u003d\\\\\\\"}\\\"}\"\n" +
" },\n" +
" \"email\": \"keinasylum@gmail.com\"\n" +
"}";
private String encMechId = "rbkmoney";
@Before
public void setUp() throws URISyntaxException {
client = new THClientBuilder().withAddress(new URI(url)).build(PaymentToolProviderSrv.Iface.class);
}
@Test
public void testCard() throws TException {
UnwrappedPaymentTool res = client.unwrap(new WrappedPaymentTool(PaymentRequest.google(new GooglePayRequest(encMechId, toContent(encMsg)))));
assertEquals("4111111111111111", res.getPaymentData().getCard().getPan());
assertEquals(new ExpDate((byte) 12, (short) 2023), res.getPaymentData().getCard().getExpDate());
assertEquals(CardClass.credit, res.getCardInfo().getCardClass());
assertEquals(BankCardPaymentSystem.visa, res.getCardInfo().getPaymentSystem());
assertEquals("Visa••••9391", res.getCardInfo().getDisplayName());
assertEquals("9391", res.getCardInfo().getLast4Digits());
assertEquals("2018-06-05T12:36:44.549Z", res.getDetails().getGoogle().getMessageExpiration());
assertEquals("AH2EjtfIuPojiNSCv4SarecQOmML6hX1x-0yOZImfiPubV_vd6vVc0vScpQV0ExK1eJ2C9_D1KSeg4T45QyurCF6wTCJMoIgJR4zYcak0cAZ6XXOUk_hs72b45NLHbHEvAziG7ZJmLhv", res.getDetails().getGoogle().getMessageId());
}
@Test(expected = InvalidRequest.class)
public void testWrongMerchantId() throws TException {
client.unwrap(new WrappedPaymentTool(PaymentRequest.google(new GooglePayRequest(encMechId+"1", toContent(encMsg)))));
}
private Content toContent(String data) {
return new Content("application/json", ByteBuffer.wrap(data.getBytes()));
}
}

View File

@ -0,0 +1 @@
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgAx+oQpiveM7fhYBq8KTOdfzD3VUn1RfB4t7DCViMtIuhRANCAAQqmrw+gIJIqjpRLurRl7RjQ3LTJUrT8O3ZNG6/xxRPhjue4xcvBPFtRg0BnVB8sxSNjhGD+t2s4soIIp6YRYf/