Skip to content
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<fraudo.version>1.0.7</fraudo.version>
<clickhouse-jdbc.version>0.3.1</clickhouse-jdbc.version>
<fraudbusters-proto.version>1.112-e9d16e0</fraudbusters-proto.version>
<fraudbusters-proto.version>1.128-79c32f2</fraudbusters-proto.version>
<trusted.tokens.proto.version>1.7-6d29f86</trusted.tokens.proto.version>
<wb.list.proto.version>1.44-5dbd6d4</wb.list.proto.version>
<dgraph4j.version>21.12.0</dgraph4j.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public DgraphWithdrawal convert(Withdrawal withdrawal) {
Account account = withdrawal.getAccount();
if (account != null) {
dgraphWithdrawal.setAccountId(account.getId());
dgraphWithdrawal.setAccountIdentity(account.getIdentity());
dgraphWithdrawal.setAccountCurrency(
account.getCurrency() == null
? null : createDgraphCurrency(account.getCurrency().getSymbolicCode()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public class WithdrawalBatchPreparedStatementSetter implements BatchPreparedStat

public static final String FIELDS = """
timestamp, eventTimeHour, eventTime, id, amount, currency, bin, lastDigits, cardToken, paymentSystem,
terminal, providerId, bankCountry, identityId, accountId, accountCurrency, status, errorCode,
terminal, providerId, bankCountry, accountId, accountCurrency, status, errorCode,
errorReason
""";

public static final String FIELDS_MARK = "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?";
public static final String FIELDS_MARK = "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?";

private final List<Withdrawal> batch;

Expand Down Expand Up @@ -60,7 +60,6 @@ public void setValues(PreparedStatement ps, int i) throws SQLException {
: UNKNOWN
);

ps.setString(l++, withdrawal.getAccount().getIdentity());
ps.setString(l++, withdrawal.getAccount().getId());
ps.setString(l++, withdrawal.getAccount().getCurrency().getSymbolicCode());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.vality.fraudbusters.resource.payment;

import dev.vality.damsel.fraudbusters.InspectorServiceSrv;
import dev.vality.woody.thrift.impl.http.THServiceBuilder;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import lombok.RequiredArgsConstructor;

import java.io.IOException;

@WebServlet("/inspector/v1/")
@RequiredArgsConstructor
public class InspectorServiceServlet extends GenericServlet {

private final InspectorServiceSrv.Iface inspectorServiceHandler;
private Servlet thriftServlet;

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
thriftServlet = new THServiceBuilder()
.build(InspectorServiceSrv.Iface.class, inspectorServiceHandler);
}

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
thriftServlet.service(req, res);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,10 @@
import dev.vality.fraudbusters.domain.FraudResult;
import dev.vality.fraudbusters.fraud.model.PaymentModel;
import dev.vality.fraudbusters.stream.TemplateVisitor;
import dev.vality.fraudbusters.util.PaymentModelFactory;
import dev.vality.fraudbusters.util.UserCacheKeyUtil;
import dev.vality.fraudo.constant.ResultStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.util.CollectionUtils;

import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
Expand Down Expand Up @@ -73,43 +63,7 @@ public boolean isBlacklisted(BlackListContext blackListContext) throws InvalidRe
}

@Override
@Cacheable(
cacheManager = "inspectUserCacheManager",
cacheNames = "inspectUser",
key = "#root.target.buildInspectUserCacheKey(#context)"
)
public BlockedShops inspectUser(InspectUserContext context) throws InvalidRequest, TException {
if (CollectionUtils.isEmpty(context.getShopList())) {
log.warn("FraudInspectorHandler inspectUser with empty shopList: {}", context);
return new BlockedShops().setShopList(Collections.emptyList());
}
try {
List<ShopContext> blockedShops = context.getShopList().stream()
.map(shopContext -> {
PaymentModel paymentModel = PaymentModelFactory.buildPaymentModel(context, shopContext);
CheckedResultModel result = templateVisitor.visit(paymentModel);
return new AbstractMap.SimpleEntry<>(shopContext, result);
})
.filter(entry -> isDeclineResult(entry.getValue()))
.map(AbstractMap.SimpleEntry::getKey)
.collect(Collectors.toList());
log.debug("FraudInspectorHandler inspectUser result blockedShops: {}", blockedShops);
return new BlockedShops().setShopList(blockedShops);
} catch (Exception e) {
log.warn("FraudInspectorHandler error when inspectUser e: ", e);
return new BlockedShops().setShopList(Collections.emptyList());
}
throw new UnsupportedOperationException();
}

public String buildInspectUserCacheKey(InspectUserContext context) {
return UserCacheKeyUtil.buildInspectUserCacheKey(context);
}

private static boolean isDeclineResult(CheckedResultModel result) {
return result != null
&& result.getResultModel() != null
&& (ResultStatus.DECLINE.equals(result.getResultModel().getResultStatus())
|| ResultStatus.DECLINE_AND_NOTIFY.equals(result.getResultModel().getResultStatus()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package dev.vality.fraudbusters.resource.payment.handler;

import dev.vality.damsel.base.InvalidRequest;
import dev.vality.damsel.fraudbusters.BlockedShops;
import dev.vality.damsel.fraudbusters.InspectUserContext;
import dev.vality.damsel.fraudbusters.InspectorServiceSrv;
import dev.vality.damsel.fraudbusters.ShopContext;
import dev.vality.fraudbusters.domain.CheckedResultModel;
import dev.vality.fraudbusters.fraud.model.PaymentModel;
import dev.vality.fraudbusters.stream.TemplateVisitor;
import dev.vality.fraudbusters.util.PaymentModelFactory;
import dev.vality.fraudo.constant.ResultStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class InspectorHandler implements InspectorServiceSrv.Iface {

private final TemplateVisitor<PaymentModel, CheckedResultModel> templateVisitor;

@Override
@Cacheable(
cacheManager = "inspectUserCacheManager",
cacheNames = "inspectUser",
key = "#root.target.buildInspectUserCacheKey(#context)"
)
public BlockedShops inspectUserShops(InspectUserContext context) throws InvalidRequest, TException {
if (CollectionUtils.isEmpty(context.getShopList())) {
log.warn("InspectorHandler inspectUser with empty shopList: {}", context);
return new BlockedShops().setShopList(Collections.emptyList());
}
try {
List<ShopContext> blockedShops = context.getShopList().stream()
.map(shopContext -> {
PaymentModel paymentModel = PaymentModelFactory.buildPaymentModel(context, shopContext);
CheckedResultModel result = templateVisitor.visit(paymentModel);
return new AbstractMap.SimpleEntry<>(shopContext, result);
})
.filter(entry -> isDeclineResult(entry.getValue()))
.map(AbstractMap.SimpleEntry::getKey)
.collect(Collectors.toList());
log.debug("InspectorHandler inspectUser result blockedShops: {}", blockedShops);
return new BlockedShops().setShopList(blockedShops);
} catch (Exception e) {
log.warn("InspectorHandler error when inspectUser e: ", e);
return new BlockedShops().setShopList(Collections.emptyList());
}
}

private static boolean isDeclineResult(CheckedResultModel result) {
return result != null
&& result.getResultModel() != null
&& (ResultStatus.DECLINE.equals(result.getResultModel().getResultStatus())
|| ResultStatus.DECLINE_AND_NOTIFY.equals(result.getResultModel().getResultStatus()));
}


}
24 changes: 10 additions & 14 deletions src/main/java/dev/vality/fraudbusters/util/PaymentModelFactory.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
package dev.vality.fraudbusters.util;

import dev.vality.damsel.proxy_inspector.InspectUserContext;
import dev.vality.damsel.proxy_inspector.ShopContext;
import dev.vality.damsel.fraudbusters.InspectUserContext;
import dev.vality.damsel.fraudbusters.ShopContext;
import dev.vality.fraudbusters.constant.ClickhouseUtilsValue;
import dev.vality.fraudbusters.fraud.model.PaymentModel;
import org.springframework.util.StringUtils;

public class PaymentModelFactory {

public static PaymentModel buildPaymentModel(InspectUserContext context, ShopContext shopContext) {
PaymentModel paymentModel = new PaymentModel();
paymentModel.setPartyId(shopContext.getParty().getPartyRef().getId());
paymentModel.setShopId(shopContext.getShop().getShopRef().getId());
paymentModel.setPartyId(shopContext.getPartyId());
paymentModel.setShopId(shopContext.getShopId());
paymentModel.setTimestamp(System.currentTimeMillis());
if (context.getUserInfo() != null) {
paymentModel.setEmail(
context.getUserInfo().isSetEmail() && StringUtils.hasLength(context.getUserInfo().getEmail())
? context.getUserInfo().getEmail().toLowerCase()
: ClickhouseUtilsValue.UNKNOWN);
paymentModel.setPhone(
context.getUserInfo().isSetPhoneNumber()
&& StringUtils.hasLength(context.getUserInfo().getPhoneNumber())
? context.getUserInfo().getPhoneNumber()
: ClickhouseUtilsValue.UNKNOWN
paymentModel.setEmail(context.getUserInfo().isSetEmail()
? context.getUserInfo().getEmail().toLowerCase()
: ClickhouseUtilsValue.UNKNOWN);
paymentModel.setPhone(context.getUserInfo().isSetPhone()
? context.getUserInfo().getPhone()
: ClickhouseUtilsValue.UNKNOWN
);
} else {
paymentModel.setEmail(ClickhouseUtilsValue.UNKNOWN);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package dev.vality.fraudbusters.factory;

import dev.vality.damsel.domain.*;
import dev.vality.damsel.fraudbusters.*;
import dev.vality.damsel.fraudbusters.ClientInfo;
import dev.vality.damsel.fraudbusters.CryptoWallet;
import dev.vality.damsel.fraudbusters.DigitalWallet;
import dev.vality.damsel.fraudbusters.Error;
import dev.vality.damsel.fraudbusters.*;
import dev.vality.fraudbusters.domain.dgraph.common.*;
import dev.vality.fraudbusters.domain.dgraph.side.*;
import dev.vality.fraudbusters.factory.properties.OperationProperties;
Expand Down Expand Up @@ -419,8 +419,7 @@ public static Withdrawal generateWithdrawal(int idx,
withdrawal.setAccount(
new Account()
.setId("AID-1")
.setCurrency(new CurrencyRef().setSymbolicCode("RUB"))
.setIdentity("IDX_P_1"));
.setCurrency(new CurrencyRef().setSymbolicCode("RUB")));
withdrawal.setError(
new Error()
.setErrorCode("CODE-1")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
package dev.vality.fraudbusters.resource.payment.handler;

import dev.vality.damsel.domain.Category;
import dev.vality.damsel.domain.ContactInfo;
import dev.vality.damsel.domain.PartyConfigRef;
import dev.vality.damsel.domain.ShopConfigRef;
import dev.vality.damsel.proxy_inspector.BlackListContext;
import dev.vality.damsel.proxy_inspector.BlockedShops;
import dev.vality.damsel.proxy_inspector.InspectUserContext;
import dev.vality.damsel.proxy_inspector.InspectorProxySrv;
import dev.vality.damsel.proxy_inspector.Party;
import dev.vality.damsel.proxy_inspector.Shop;
import dev.vality.damsel.proxy_inspector.ShopContext;
import dev.vality.damsel.domain.ShopLocation;
import dev.vality.damsel.wb_list.ListNotFound;
import dev.vality.damsel.wb_list.WbListServiceSrv;
import dev.vality.fraudbusters.converter.CheckedResultToRiskScoreConverter;
Expand All @@ -23,27 +12,15 @@
import dev.vality.fraudbusters.stream.TemplateVisitor;
import dev.vality.fraudo.constant.ResultStatus;
import org.apache.thrift.TException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith({MockitoExtension.class, SpringExtension.class})
Expand Down Expand Up @@ -84,39 +61,6 @@ void isExistInBlackList() throws TException {
assertEquals(false, existInBlackList);
}

@Test
void inspectUserShopsBlocked() throws TException {
FraudInspectorHandler fraudInspectorHandler = new FraudInspectorHandler(
"test",
checkedResultToRiskScoreConverter,
requestConverter,
templateVisitor,
kafkaFraudResultTemplate,
wbListServiceSrv
);

when(templateVisitor.visit(any())).thenAnswer(invocation -> {
PaymentModel model = invocation.getArgument(0);
if (model != null && "shop_1".equals(model.getShopId())) {
return createCheckedResult(ResultStatus.DECLINE);
}
return createCheckedResult(ResultStatus.THREE_DS);
});

BlockedShops blockedShops = fraudInspectorHandler.inspectUser(createInspectUserContext());

assertEquals(1, blockedShops.getShopListSize());
assertEquals("shop_1", blockedShops.getShopList().get(0).getShop().getShopRef().getId());

ArgumentCaptor<PaymentModel> captor = ArgumentCaptor.forClass(PaymentModel.class);
verify(templateVisitor, times(2)).visit(captor.capture());
for (PaymentModel model : captor.getAllValues()) {
assertEquals("party_1", model.getPartyId());
assertEquals("user@email.com", model.getEmail());
assertEquals("79990001122", model.getPhone());
}
}

private BlackListContext createBlackListContext() {
return new BlackListContext()
.setValue("test")
Expand All @@ -125,31 +69,6 @@ private BlackListContext createBlackListContext() {
.setSecondId("test_sec_id");
}

private InspectUserContext createInspectUserContext() {
ContactInfo contactInfo = new ContactInfo();
contactInfo.setEmail("User@Email.Com");
contactInfo.setPhoneNumber("79990001122");
return new InspectUserContext()
.setUserInfo(contactInfo)
.setShopList(List.of(
createShopContext("party_1", "shop_1"),
createShopContext("party_1", "shop_2")
));
}

private ShopContext createShopContext(String partyId, String shopId) {
ShopLocation location = new ShopLocation();
location.setUrl("http://example.com");
return new ShopContext()
.setParty(new Party(new PartyConfigRef(partyId)))
.setShop(new Shop(
new ShopConfigRef(shopId),
new Category("category", "category"),
"shop-name",
location
));
}

private CheckedResultModel createCheckedResult(ResultStatus status) {
CheckedResultModel checkedResultModel = new CheckedResultModel();
checkedResultModel.setResultModel(new ConcreteResultModel(status, null, null));
Expand Down
Loading
Loading