mirror of
https://github.com/valitydev/fraudo.git
synced 2024-11-06 01:45:16 +00:00
Add chargebacks and refunds (#25)
This commit is contained in:
parent
49ae5e5d1e
commit
0d0b51f3c8
@ -17,9 +17,13 @@ expression
|
|||||||
| count #countExpression
|
| count #countExpression
|
||||||
| count_success #countSuccessExpression
|
| count_success #countSuccessExpression
|
||||||
| count_error #countErrorExpression
|
| count_error #countErrorExpression
|
||||||
|
| count_chargeback #countChargebackExpression
|
||||||
|
| count_refund #countRefundExpression
|
||||||
| sum #sumExpression
|
| sum #sumExpression
|
||||||
| sum_success #sumSuccessExpression
|
| sum_success #sumSuccessExpression
|
||||||
| sum_error #sumErrorExpression
|
| sum_error #sumErrorExpression
|
||||||
|
| sum_chargeback #sumChargebackExpression
|
||||||
|
| sum_refund #sumRefundExpression
|
||||||
| unique #uniqueExpression
|
| unique #uniqueExpression
|
||||||
| in #inFunctionExpression
|
| in #inFunctionExpression
|
||||||
| in_white_list #inWhiteListExpression
|
| in_white_list #inWhiteListExpression
|
||||||
@ -68,6 +72,14 @@ count_error
|
|||||||
: 'countError' LPAREN STRING time_window DELIMETER STRING (group_by)? RPAREN
|
: 'countError' LPAREN STRING time_window DELIMETER STRING (group_by)? RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
|
count_chargeback
|
||||||
|
: 'countChargeback' LPAREN STRING time_window (group_by)? RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
count_refund
|
||||||
|
: 'countRefund' LPAREN STRING time_window (group_by)? RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
sum
|
sum
|
||||||
: 'sum' LPAREN STRING time_window (group_by)? RPAREN
|
: 'sum' LPAREN STRING time_window (group_by)? RPAREN
|
||||||
;
|
;
|
||||||
@ -80,6 +92,14 @@ sum_error
|
|||||||
: 'sumError' LPAREN STRING time_window DELIMETER STRING (group_by)? RPAREN
|
: 'sumError' LPAREN STRING time_window DELIMETER STRING (group_by)? RPAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
|
sum_chargeback
|
||||||
|
: 'sumChargeback' LPAREN STRING time_window (group_by)? RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
sum_refund
|
||||||
|
: 'sumRefund' LPAREN STRING time_window (group_by)? RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
unique
|
unique
|
||||||
: 'unique' LPAREN STRING DELIMETER STRING time_window (group_by)? RPAREN
|
: 'unique' LPAREN STRING DELIMETER STRING time_window (group_by)? RPAREN
|
||||||
;
|
;
|
||||||
|
@ -12,4 +12,8 @@ public interface CountAggregator<T, U> {
|
|||||||
|
|
||||||
Integer countError(U checkedField, T model, TimeWindow timeWindow, String errorCode, List<U> fields);
|
Integer countError(U checkedField, T model, TimeWindow timeWindow, String errorCode, List<U> fields);
|
||||||
|
|
||||||
|
Integer countChargeback(U checkedField, T model, TimeWindow timeWindow, List<U> fields);
|
||||||
|
|
||||||
|
Integer countRefund(U checkedField, T model, TimeWindow timeWindow, List<U> fields);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,4 +12,8 @@ public interface SumAggregator<T, U> {
|
|||||||
|
|
||||||
Double sumError(U checkedField, T model, TimeWindow timeWindow, String errorCode, List<U> fields);
|
Double sumError(U checkedField, T model, TimeWindow timeWindow, String errorCode, List<U> fields);
|
||||||
|
|
||||||
|
Double sumChargeback(U checkedField, T model, TimeWindow timeWindow, List<U> fields);
|
||||||
|
|
||||||
|
Double sumRefund(U checkedField, T model, TimeWindow timeWindow, List<U> fields);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,22 @@ public class CountKeyGenerator {
|
|||||||
resolve);
|
resolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> String generateChargebackKey(ParserRuleContext context, Function<String, T> resolve) {
|
||||||
|
FraudoParser.Count_chargebackContext ctx = (FraudoParser.Count_chargebackContext) context;
|
||||||
|
return CommonKeyGenerator.generateKeyGroupedFunction(ctx.STRING(),
|
||||||
|
ctx.children.get(0),
|
||||||
|
ctx.time_window(),
|
||||||
|
ctx.group_by(),
|
||||||
|
resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String generateRefundKey(ParserRuleContext context, Function<String, T> resolve) {
|
||||||
|
FraudoParser.Count_refundContext ctx = (FraudoParser.Count_refundContext) context;
|
||||||
|
return CommonKeyGenerator.generateKeyGroupedFunction(ctx.STRING(),
|
||||||
|
ctx.children.get(0),
|
||||||
|
ctx.time_window(),
|
||||||
|
ctx.group_by(),
|
||||||
|
resolve);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,22 @@ public class SumKeyGenerator {
|
|||||||
resolve);
|
resolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> String generateChargebackKey(ParserRuleContext context, Function<String, T> resolve) {
|
||||||
|
FraudoParser.Sum_chargebackContext ctx = (FraudoParser.Sum_chargebackContext) context;
|
||||||
|
return CommonKeyGenerator.generateKeyGroupedFunction(ctx.STRING(),
|
||||||
|
ctx.children.get(0),
|
||||||
|
ctx.time_window(),
|
||||||
|
ctx.group_by(),
|
||||||
|
resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> String generateRefundKey(ParserRuleContext context, Function<String, T> resolve) {
|
||||||
|
FraudoParser.Sum_refundContext ctx = (FraudoParser.Sum_refundContext) context;
|
||||||
|
return CommonKeyGenerator.generateKeyGroupedFunction(ctx.STRING(),
|
||||||
|
ctx.children.get(0),
|
||||||
|
ctx.time_window(),
|
||||||
|
ctx.group_by(),
|
||||||
|
resolve);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,8 @@ public interface CountVisitor<T> {
|
|||||||
|
|
||||||
Integer visitCountError(FraudoParser.Count_errorContext ctx, T model);
|
Integer visitCountError(FraudoParser.Count_errorContext ctx, T model);
|
||||||
|
|
||||||
|
Integer visitCountChargeback(FraudoParser.Count_chargebackContext ctx, T model);
|
||||||
|
|
||||||
|
Integer visitCountRefund(FraudoParser.Count_refundContext ctx, T model);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,8 @@ public interface SumVisitor<T> {
|
|||||||
|
|
||||||
Double visitSumError(FraudoParser.Sum_errorContext ctx, T model);
|
Double visitSumError(FraudoParser.Sum_errorContext ctx, T model);
|
||||||
|
|
||||||
|
Double visitSumChargeback(FraudoParser.Sum_chargebackContext ctx, T model);
|
||||||
|
|
||||||
|
Double visitSumRefund(FraudoParser.Sum_refundContext ctx, T model);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,4 +51,26 @@ public class CountVisitorImpl<T, U> implements CountVisitor<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitCountChargeback(FraudoParser.Count_chargebackContext ctx, T model) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
return countAggregator.countChargeback(
|
||||||
|
fieldResolver.resolveName(countTarget),
|
||||||
|
model,
|
||||||
|
TimeWindowResolver.resolve(ctx.time_window()),
|
||||||
|
groupFieldsResolver.resolve(ctx.group_by())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer visitCountRefund(FraudoParser.Count_refundContext ctx, T model) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
return countAggregator.countRefund(
|
||||||
|
fieldResolver.resolveName(countTarget),
|
||||||
|
model,
|
||||||
|
TimeWindowResolver.resolve(ctx.time_window()),
|
||||||
|
groupFieldsResolver.resolve(ctx.group_by())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,24 @@ public class FirstFindVisitorImpl<T extends BaseModel, U> extends FraudoBaseVisi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_chargeback(FraudoParser.Count_chargebackContext ctx) {
|
||||||
|
String key = CountKeyGenerator.generateChargebackKey(ctx, fieldResolver::resolveName);
|
||||||
|
return localFuncCache.get().computeIfAbsent(
|
||||||
|
key,
|
||||||
|
s -> Double.valueOf(countVisitor.visitCountChargeback(ctx, threadLocalModel.get()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_refund(FraudoParser.Count_refundContext ctx) {
|
||||||
|
String key = CountKeyGenerator.generateRefundKey(ctx, fieldResolver::resolveName);
|
||||||
|
return localFuncCache.get().computeIfAbsent(
|
||||||
|
key,
|
||||||
|
s -> Double.valueOf(countVisitor.visitCountRefund(ctx, threadLocalModel.get()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitSum(FraudoParser.SumContext ctx) {
|
public Object visitSum(FraudoParser.SumContext ctx) {
|
||||||
String key = SumKeyGenerator.generate(ctx, fieldResolver::resolveName);
|
String key = SumKeyGenerator.generate(ctx, fieldResolver::resolveName);
|
||||||
@ -201,6 +219,24 @@ public class FirstFindVisitorImpl<T extends BaseModel, U> extends FraudoBaseVisi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_chargeback(FraudoParser.Sum_chargebackContext ctx) {
|
||||||
|
String key = SumKeyGenerator.generateChargebackKey(ctx, fieldResolver::resolveName);
|
||||||
|
return localFuncCache.get().computeIfAbsent(
|
||||||
|
key,
|
||||||
|
s -> sumVisitor.visitSumChargeback(ctx, threadLocalModel.get())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_refund(FraudoParser.Sum_refundContext ctx) {
|
||||||
|
String key = SumKeyGenerator.generateRefundKey(ctx, fieldResolver::resolveName);
|
||||||
|
return localFuncCache.get().computeIfAbsent(
|
||||||
|
key,
|
||||||
|
s -> sumVisitor.visitSumRefund(ctx, threadLocalModel.get())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitCountry_by(FraudoParser.Country_byContext ctx) {
|
public Object visitCountry_by(FraudoParser.Country_byContext ctx) {
|
||||||
String key = CountryKeyGenerator.generate(ctx);
|
String key = CountryKeyGenerator.generate(ctx);
|
||||||
|
@ -49,4 +49,26 @@ public class SumVisitorImpl<T, U> implements SumVisitor<T> {
|
|||||||
groupByModelResolver.resolve(ctx.group_by()));
|
groupByModelResolver.resolve(ctx.group_by()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double visitSumChargeback(FraudoParser.Sum_chargebackContext ctx, T model) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
return sumAggregator.sumChargeback(
|
||||||
|
fieldResolver.resolveName(countTarget),
|
||||||
|
model,
|
||||||
|
TimeWindowResolver.resolve(ctx.time_window()),
|
||||||
|
groupByModelResolver.resolve(ctx.group_by())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double visitSumRefund(FraudoParser.Sum_refundContext ctx, T model) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
return sumAggregator.sumRefund(
|
||||||
|
fieldResolver.resolveName(countTarget),
|
||||||
|
model,
|
||||||
|
TimeWindowResolver.resolve(ctx.time_window()),
|
||||||
|
groupByModelResolver.resolve(ctx.group_by())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ public class CountTest extends AbstractPaymentTest {
|
|||||||
when(countAggregator.count(anyObject(), any(), any(), any())).thenReturn(10);
|
when(countAggregator.count(anyObject(), any(), any(), any())).thenReturn(10);
|
||||||
when(countAggregator.countError(anyObject(), any(), any(), anyString(), any())).thenReturn(6);
|
when(countAggregator.countError(anyObject(), any(), any(), anyString(), any())).thenReturn(6);
|
||||||
when(countAggregator.countSuccess(anyObject(), any(), any(), any())).thenReturn(4);
|
when(countAggregator.countSuccess(anyObject(), any(), any(), any())).thenReturn(4);
|
||||||
|
when(countAggregator.countChargeback(anyObject(), any(), any(), any())).thenReturn(0);
|
||||||
|
when(countAggregator.countRefund(anyObject(), any(), any(), any())).thenReturn(1);
|
||||||
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
ResultModel result = invokeParse(parseContext);
|
ResultModel result = invokeParse(parseContext);
|
||||||
assertEquals(ResultStatus.DECLINE, result.getResultStatus());
|
assertEquals(ResultStatus.DECLINE, result.getResultStatus());
|
||||||
@ -38,6 +40,17 @@ public class CountTest extends AbstractPaymentTest {
|
|||||||
assertEquals(ResultStatus.DECLINE, result.getResultStatus());
|
assertEquals(ResultStatus.DECLINE, result.getResultStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void countCargeRefundTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = CountTest.class.getResourceAsStream("/rules/count_chargeback_refund.frd");
|
||||||
|
when(countAggregator.countChargeback(anyObject(), any(), any(), any())).thenReturn(3);
|
||||||
|
when(countAggregator.countRefund(anyObject(), any(), any(), any())).thenReturn(5);
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
ResultModel result = invokeParse(parseContext);
|
||||||
|
assertEquals(ResultStatus.DECLINE, result.getResultStatus());
|
||||||
|
assertEquals("1", result.getRuleChecked());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void countGroupByTest() throws Exception {
|
public void countGroupByTest() throws Exception {
|
||||||
InputStream resourceAsStream = CountTest.class.getResourceAsStream("/rules/countGroupBy.frd");
|
InputStream resourceAsStream = CountTest.class.getResourceAsStream("/rules/countGroupBy.frd");
|
||||||
|
@ -39,6 +39,16 @@ public class SumTest extends AbstractPaymentTest {
|
|||||||
Assert.assertEquals(0, result.getNotificationsRule().size());
|
Assert.assertEquals(0, result.getNotificationsRule().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sumChargeRefundTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = SumTest.class.getResourceAsStream("/rules/sum_chargeback_refund.frd");
|
||||||
|
Mockito.when(sumAggregator.sumChargeback(anyObject(), any(), any(), any())).thenReturn(10000.60);
|
||||||
|
Mockito.when(sumAggregator.sumRefund(anyObject(), any(), any(), any())).thenReturn(10000.60);
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
ResultModel result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sumGroupByTest() throws Exception {
|
public void sumGroupByTest() throws Exception {
|
||||||
InputStream resourceAsStream = SumTest.class.getResourceAsStream("/rules/sumGroupBy.frd");
|
InputStream resourceAsStream = SumTest.class.getResourceAsStream("/rules/sumGroupBy.frd");
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
rule: amount() < 100 AND currency() == "RUB"
|
rule: amount() < 100 AND currency() = "RUB"
|
||||||
-> accept;
|
-> accept;
|
2
src/test/resources/rules/count_chargeback_refund.frd
Normal file
2
src/test/resources/rules/count_chargeback_refund.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: countChargeback("ip", 1444) > 3 OR countRefund("ip", 1444) > 1
|
||||||
|
-> decline;
|
2
src/test/resources/rules/sum_chargeback_refund.frd
Normal file
2
src/test/resources/rules/sum_chargeback_refund.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: sumChargeback("ip", 1444) < 10500.50 AND sumRefund("ip", 1444) < 10500.50
|
||||||
|
-> accept;
|
Loading…
Reference in New Issue
Block a user