Skip to content

Commit 5530f32

Browse files
authored
Merge pull request #20 from maxmind/greg/cc-token
Add support for /credit_card/token input
2 parents 6cfc18b + 894265b commit 5530f32

7 files changed

Lines changed: 81 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
1.2.0
5+
------------------
6+
7+
* Added `/credit_card/token` input. Use the `token(String)` method on
8+
`com.maxmind.minfraud.request.CreditCard.Builder` to set it.
9+
* All validation regular expressions are now pre-compiled.
10+
411
1.1.1 (2016-10-12)
512
------------------
613

src/main/java/com/maxmind/minfraud/request/AbstractLocation.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.fasterxml.jackson.annotation.JsonProperty;
44
import com.maxmind.minfraud.AbstractModel;
55

6+
import java.util.regex.Pattern;
7+
68
/**
79
* This class represents the shared location behavior between
810
* Billing and Shipping.
@@ -40,6 +42,8 @@ protected AbstractLocation(AbstractLocation.Builder builder) {
4042
*/
4143
@SuppressWarnings("unchecked")
4244
abstract static class Builder<T extends AbstractLocation.Builder> {
45+
private static final Pattern COUNTRY_CODE_PATTERN = Pattern.compile("^[A-Z]{2}$");
46+
4347
String firstName;
4448
String lastName;
4549
String company;
@@ -124,7 +128,7 @@ public final T region(String code) {
124128
* country code.
125129
*/
126130
public final T country(String code) {
127-
if (!code.matches("[A-Z]{2}")) {
131+
if (!COUNTRY_CODE_PATTERN.matcher(code).matches()) {
128132
throw new IllegalArgumentException("Expected two-letter country code in the ISO 3166-1 alpha-2 format");
129133
}
130134
country = code;

src/main/java/com/maxmind/minfraud/request/CreditCard.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.fasterxml.jackson.annotation.JsonProperty;
44
import com.maxmind.minfraud.AbstractModel;
55

6+
import java.util.regex.Pattern;
7+
68
/**
79
* The credit card information for the transaction.
810
*/
@@ -14,6 +16,7 @@ public final class CreditCard extends AbstractModel {
1416
private final String bankPhoneNumber;
1517
private final Character avsResult;
1618
private final Character cvvResult;
19+
private final String token;
1720

1821
private CreditCard(CreditCard.Builder builder) {
1922
issuerIdNumber = builder.issuerIdNumber;
@@ -23,18 +26,24 @@ private CreditCard(CreditCard.Builder builder) {
2326
bankPhoneNumber = builder.bankPhoneNumber;
2427
avsResult = builder.avsResult;
2528
cvvResult = builder.cvvResult;
29+
token = builder.token;
2630
}
2731

2832
/**
2933
* {@code Builder} creates instances of the parent {@code CreditCard}
3034
* from values set by the builder's methods.
3135
*/
3236
public static final class Builder {
37+
private static final Pattern IIN_PATTERN = Pattern.compile("^[0-9]{6}$");
38+
private static final Pattern LAST_4_PATTERN = Pattern.compile("^[0-9]{4}$");
39+
private static final Pattern TOKEN_PATTERN = Pattern.compile("^(?![0-9]{1,19}$)[\\x21-\\x7E]{1,255}$");
40+
3341
String issuerIdNumber;
3442
String last4Digits;
3543
String bankName;
3644
String bankPhoneCountryCode;
3745
String bankPhoneNumber;
46+
String token;
3847
Character avsResult;
3948
Character cvvResult;
4049

@@ -47,7 +56,7 @@ public static final class Builder {
4756
* string.
4857
*/
4958
public CreditCard.Builder issuerIdNumber(String number) {
50-
if (!number.matches("[0-9]{6}")) {
59+
if (!IIN_PATTERN.matcher(number).matches()) {
5160
throw new IllegalArgumentException("The issuer ID number " + number + " is of the wrong format.");
5261
}
5362
issuerIdNumber = number;
@@ -61,7 +70,7 @@ public CreditCard.Builder issuerIdNumber(String number) {
6170
* string.
6271
*/
6372
public CreditCard.Builder last4Digits(String digits) {
64-
if (!digits.matches("[0-9]{4}")) {
73+
if (!LAST_4_PATTERN.matcher(digits).matches()) {
6574
throw new IllegalArgumentException("The last 4 credit card digits " + digits + " are of the wrong format.");
6675
}
6776
last4Digits = digits;
@@ -117,6 +126,23 @@ public CreditCard.Builder cvvResult(Character code) {
117126
return this;
118127
}
119128

129+
/**
130+
* @param token A token uniquely identifying the card. This should not be
131+
* the actual credit card number.
132+
* @return The builder object.
133+
* @throws IllegalArgumentException when the token is invalid.
134+
*
135+
*/
136+
public CreditCard.Builder token(String token) {
137+
if (!TOKEN_PATTERN.matcher(token).matches()) {
138+
throw new IllegalArgumentException("The credit card token was invalid. "
139+
+ "Tokens must be non-space ASCII printable characters. If the "
140+
+ "token consists of all digits, it must be more than 19 digits.");
141+
}
142+
this.token = token;
143+
return this;
144+
}
145+
120146
/**
121147
* @return An instance of {@code CreditCard} created from the
122148
* fields set on this builder.
@@ -186,4 +212,12 @@ public Character getAvsResult() {
186212
public Character getCvvResult() {
187213
return cvvResult;
188214
}
215+
216+
/**
217+
* @return A credit card token uniquely identifying the card.
218+
*/
219+
@JsonProperty("token")
220+
public String getToken() {
221+
return token;
222+
}
189223
}

src/main/java/com/maxmind/minfraud/request/Order.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.math.BigDecimal;
77
import java.net.URI;
8+
import java.util.regex.Pattern;
89

910
/**
1011
* The order information for the transaction.
@@ -35,6 +36,8 @@ private Order(Order.Builder builder) {
3536
* from values set by the builder's methods.
3637
*/
3738
public static final class Builder {
39+
private static final Pattern CURRENCY_CODE_PATTERN = Pattern.compile("^[A-Z]{3}$");
40+
3841
BigDecimal amount;
3942
String currency;
4043
String discountCode;
@@ -70,7 +73,7 @@ public Order.Builder amount(Double amount) {
7073
* three-letter currency code.
7174
*/
7275
public Order.Builder currency(String code) {
73-
if (!code.matches("[A-Z]{3}")) {
76+
if (!CURRENCY_CODE_PATTERN.matcher(code).matches()) {
7477
throw new IllegalArgumentException("The currency code " + code + " is invalid.");
7578
}
7679
currency = code;

src/test/java/com/maxmind/minfraud/request/CreditCardTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.maxmind.minfraud.request;
22

33
import com.maxmind.minfraud.request.CreditCard.Builder;
4+
import junitparams.JUnitParamsRunner;
5+
import junitparams.Parameters;
46
import org.junit.Test;
7+
import org.junit.runner.RunWith;
58

69
import static org.junit.Assert.assertEquals;
710

11+
@RunWith(JUnitParamsRunner.class)
812
public class CreditCardTest {
913

1014
@Test
@@ -79,4 +83,26 @@ public void testCvvResult() throws Exception {
7983
CreditCard cc = new Builder().cvvResult('N').build();
8084
assertEquals(Character.valueOf('N'), cc.getCvvResult());
8185
}
86+
87+
@Test(expected = IllegalArgumentException.class)
88+
@Parameters({"4485921507912924",
89+
"432312",
90+
"this is invalid",
91+
"",
92+
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
93+
})
94+
public void testInvalidToken(String token) throws Exception {
95+
new Builder().token(token).build();
96+
}
97+
98+
@Test
99+
@Parameters({"t4485921507912924",
100+
"a7f6%gf83fhAu",
101+
"valid_token"
102+
})
103+
public void testValidToken(String token) throws Exception {
104+
CreditCard cc = new Builder().token(token).build();
105+
assertEquals(token, cc.getToken());
106+
}
107+
82108
}

src/test/java/com/maxmind/minfraud/request/RequestTestHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public static Transaction fullTransaction() throws Exception {
8787
.avsResult('Y')
8888
.cvvResult('N')
8989
.last4Digits("7643")
90+
.token("123456abc1234")
9091
.build()
9192
).order(
9293
new Order.Builder()

src/test/resources/test-data/full-request.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"bank_phone_country_code": "1",
5353
"bank_phone_number": "800-342-1232",
5454
"avs_result": "Y",
55-
"cvv_result": "N"
55+
"cvv_result": "N",
56+
"token": "123456abc1234"
5657
},
5758
"order": {
5859
"amount": 323.21,

0 commit comments

Comments
 (0)