mirror of
https://github.com/valitydev/three-ds-server-storage.git
synced 2024-11-06 08:25:16 +00:00
BJ-917: Provide tests for PreparationFlowService
This commit is contained in:
parent
6e01f45257
commit
468984cfe4
6
pom.xml
6
pom.xml
@ -135,6 +135,12 @@
|
||||
<version>1.8.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock</artifactId>
|
||||
<version>2.18.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -4,10 +4,15 @@ import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationReque
|
||||
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClientResponseException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Service
|
||||
@EnableRetry
|
||||
@RequiredArgsConstructor
|
||||
public class ThreeDsServerClient {
|
||||
|
||||
@ -16,6 +21,10 @@ public class ThreeDsServerClient {
|
||||
@Value("${client.three-ds-server.url}")
|
||||
private String url;
|
||||
|
||||
@Retryable(
|
||||
value = RestClientResponseException.class,
|
||||
backoff = @Backoff(delay = 60_000L),
|
||||
maxAttempts = Integer.MAX_VALUE)
|
||||
public RBKMoneyPreparationResponse preparationFlow(RBKMoneyPreparationRequest request) {
|
||||
return restTemplate.postForEntity(
|
||||
url,
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.rbkmoney.threeds.server.storage.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryListener;
|
||||
import org.springframework.retry.listener.RetryListenerSupport;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class RetryListenerConfig {
|
||||
|
||||
@Bean
|
||||
public List<RetryListener> retryListeners() {
|
||||
return List.of(new RetryListenerSupport() {
|
||||
@Override
|
||||
public <T, E extends Throwable> void onError(
|
||||
RetryContext context, RetryCallback<T, E> callback,
|
||||
Throwable throwable) {
|
||||
log.warn("Retrying method={}, count={} because of the exception={}",
|
||||
context.getAttribute("context.name"),
|
||||
context.getRetryCount(),
|
||||
throwable.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import com.rbkmoney.damsel.three_ds_server_storage.*;
|
||||
import com.rbkmoney.threeds.server.storage.entity.CardRangeEntity;
|
||||
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
|
||||
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
|
||||
import com.rbkmoney.threeds.server.storage.service.RBKMoneyPreparationFlowService;
|
||||
import com.rbkmoney.threeds.server.storage.service.PreparationFlowService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -16,7 +16,7 @@ import static java.util.stream.Collectors.toList;
|
||||
@RequiredArgsConstructor
|
||||
public class CardRangesStorageHandler implements CardRangesStorageSrv.Iface {
|
||||
|
||||
private final RBKMoneyPreparationFlowService preparationFlowService;
|
||||
private final PreparationFlowService preparationFlowService;
|
||||
private final CardRangeRepository repository;
|
||||
private final CardRangeMapper mapper;
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
package com.rbkmoney.threeds.server.storage.mapper;
|
||||
|
||||
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SerialNumMapper {
|
||||
|
||||
public SerialNumEntity toEntity(String serialNum, String providerId) {
|
||||
return SerialNumEntity.builder()
|
||||
.providerId(providerId)
|
||||
.serialNum(serialNum)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package com.rbkmoney.threeds.server.storage.service;
|
||||
|
||||
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
|
||||
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
|
||||
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CardRangeUpdater {
|
||||
|
||||
private final CardRangeRepository repository;
|
||||
private final CardRangeMapper mapper;
|
||||
|
||||
public void update(RBKMoneyPreparationResponse response) {
|
||||
String providerId = response.getProviderId();
|
||||
|
||||
repository.saveAll(mapper.toEntities(response.getAddedCardRanges(), providerId));
|
||||
repository.saveAll(mapper.toEntities(response.getModifiedCardRanges(), providerId));
|
||||
repository.deleteAll(mapper.toEntities(response.getDeletedCardRanges(), providerId));
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.rbkmoney.threeds.server.storage.service;
|
||||
|
||||
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
|
||||
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
|
||||
import com.rbkmoney.threeds.server.storage.mapper.CardRangeMapper;
|
||||
import com.rbkmoney.threeds.server.storage.mapper.SerialNumMapper;
|
||||
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
|
||||
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PreparationFlowDataUpdater {
|
||||
|
||||
private final SerialNumRepository serialNumRepository;
|
||||
private final SerialNumMapper serialNumMapper;
|
||||
private final CardRangeRepository cardRangeRepository;
|
||||
private final CardRangeMapper cardRangeMapper;
|
||||
|
||||
public String getCurrentSerialNum(String providerId) {
|
||||
return serialNumRepository
|
||||
.findByProviderId(providerId)
|
||||
.map(SerialNumEntity::getSerialNum)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void update(RBKMoneyPreparationResponse response) {
|
||||
String providerId = response.getProviderId();
|
||||
String serialNum = response.getSerialNum();
|
||||
|
||||
cardRangeRepository.saveAll(cardRangeMapper.toEntities(response.getAddedCardRanges(), providerId));
|
||||
cardRangeRepository.saveAll(cardRangeMapper.toEntities(response.getModifiedCardRanges(), providerId));
|
||||
cardRangeRepository.deleteAll(cardRangeMapper.toEntities(response.getDeletedCardRanges(), providerId));
|
||||
serialNumRepository.save(serialNumMapper.toEntity(serialNum, providerId));
|
||||
}
|
||||
}
|
@ -4,30 +4,19 @@ import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationReque
|
||||
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
|
||||
import com.rbkmoney.threeds.server.storage.client.ThreeDsServerClient;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.EnableRetry;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClientResponseException;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
@Service
|
||||
@EnableRetry
|
||||
@RequiredArgsConstructor
|
||||
public class RBKMoneyPreparationFlowService {
|
||||
public class PreparationFlowService {
|
||||
|
||||
private final ThreeDsServerClient client;
|
||||
private final SerialNumUpdater serialNumUpdater;
|
||||
private final CardRangeUpdater cardRangeUpdater;
|
||||
private final PreparationFlowDataUpdater dataUpdater;
|
||||
|
||||
@Transactional
|
||||
@Retryable(
|
||||
value = RestClientResponseException.class,
|
||||
backoff = @Backoff(delay = 60_000L),
|
||||
maxAttempts = Integer.MAX_VALUE)
|
||||
public void init(String providerId) {
|
||||
String serialNum = serialNumUpdater.getCurrent(providerId);
|
||||
String serialNum = dataUpdater.getCurrentSerialNum(providerId);
|
||||
|
||||
RBKMoneyPreparationRequest request = RBKMoneyPreparationRequest.builder()
|
||||
.providerId(providerId)
|
||||
@ -36,7 +25,6 @@ public class RBKMoneyPreparationFlowService {
|
||||
|
||||
RBKMoneyPreparationResponse response = client.preparationFlow(request);
|
||||
|
||||
serialNumUpdater.update(response);
|
||||
cardRangeUpdater.update(response);
|
||||
dataUpdater.update(response);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.rbkmoney.threeds.server.storage.service;
|
||||
|
||||
import com.rbkmoney.threeds.server.domain.root.rbkmoney.RBKMoneyPreparationResponse;
|
||||
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
|
||||
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SerialNumUpdater {
|
||||
|
||||
private final SerialNumRepository repository;
|
||||
|
||||
public String getCurrent(String providerId) {
|
||||
return repository
|
||||
.findByProviderId(providerId)
|
||||
.map(SerialNumEntity::getSerialNum)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public void update(RBKMoneyPreparationResponse response) {
|
||||
String updatedSerialNum = response.getSerialNum();
|
||||
if (updatedSerialNum == null) return;
|
||||
|
||||
repository.save(SerialNumEntity.builder()
|
||||
.providerId(response.getProviderId())
|
||||
.serialNum(updatedSerialNum)
|
||||
.build());
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.rbkmoney.threeds.server.storage.config;
|
||||
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@TestConfiguration
|
||||
public class TestConfig {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RestTemplate getRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ import java.util.List;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
public class CardRangesRepositoryTest extends AbstractRepositoryTest {
|
||||
public class CardRangesRepositoryTest extends PostgresRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private CardRangeRepository repository;
|
||||
|
@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
public class ChallengeFlowTransactionInfoRepositoryTest extends AbstractRepositoryTest {
|
||||
public class ChallengeFlowTransactionInfoRepositoryTest extends PostgresRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private ChallengeFlowTransactionInfoRepository repository;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.rbkmoney.threeds.server.storage.repository;
|
||||
|
||||
import com.rbkmoney.threeds.server.storage.ThreeDsServerStorageApplication;
|
||||
import com.rbkmoney.threeds.server.storage.config.TestConfig;
|
||||
import org.junit.ClassRule;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
@ -16,8 +17,10 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
@ContextConfiguration(classes = ThreeDsServerStorageApplication.class, initializers = AbstractRepositoryTest.Initializer.class)
|
||||
public class AbstractRepositoryTest {
|
||||
@ContextConfiguration(
|
||||
classes = {ThreeDsServerStorageApplication.class, TestConfig.class},
|
||||
initializers = PostgresRepositoryTest.Initializer.class)
|
||||
public class PostgresRepositoryTest {
|
||||
|
||||
@ClassRule
|
||||
@SuppressWarnings("rawtypes")
|
@ -13,7 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
public class SerialNumRepositoryTest extends AbstractRepositoryTest {
|
||||
public class SerialNumRepositoryTest extends PostgresRepositoryTest {
|
||||
|
||||
@Autowired
|
||||
private SerialNumRepository repository;
|
||||
|
@ -0,0 +1,114 @@
|
||||
package com.rbkmoney.threeds.server.storage.service;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.rbkmoney.threeds.server.storage.entity.CardRangeEntity;
|
||||
import com.rbkmoney.threeds.server.storage.entity.SerialNumEntity;
|
||||
import com.rbkmoney.threeds.server.storage.repository.CardRangeRepository;
|
||||
import com.rbkmoney.threeds.server.storage.repository.PostgresRepositoryTest;
|
||||
import com.rbkmoney.threeds.server.storage.repository.SerialNumRepository;
|
||||
import io.micrometer.core.instrument.util.IOUtils;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(properties = "client.three-ds-server.url=http://127.0.0.1:8089/")
|
||||
public class PreparationFlowServiceIT extends PostgresRepositoryTest {
|
||||
|
||||
@ClassRule
|
||||
public static WireMockRule wireMockRule = new WireMockRule(8089);
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Autowired
|
||||
private SerialNumRepository serialNumRepository;
|
||||
|
||||
@Autowired
|
||||
private CardRangeRepository cardRangeRepository;
|
||||
|
||||
@Autowired
|
||||
private PreparationFlowService preparationFlowService;
|
||||
|
||||
@Test
|
||||
public void shouldSaveUpdatedSerialNumAndCardRanges() throws IOException {
|
||||
// Given
|
||||
stubForOkResponse();
|
||||
|
||||
// When
|
||||
preparationFlowService.init("1");
|
||||
|
||||
// Then
|
||||
Optional<SerialNumEntity> serialNum = serialNumRepository.findByProviderId("1");
|
||||
assertTrue(serialNum.isPresent());
|
||||
assertThat(serialNum.get().getSerialNum()).isEqualTo("20190411083623719000");
|
||||
|
||||
List<CardRangeEntity> cardRanges = cardRangeRepository.findByPkProviderId("1");
|
||||
assertThat(cardRanges).hasSize(20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRetryOnException() throws IOException {
|
||||
// Given
|
||||
stubWithErrorAndThenOkResponse();
|
||||
|
||||
// When
|
||||
preparationFlowService.init("2");
|
||||
|
||||
// Then
|
||||
Optional<SerialNumEntity> serialNum = serialNumRepository.findByProviderId("2");
|
||||
assertTrue(serialNum.isPresent());
|
||||
assertThat(serialNum.get().getSerialNum()).isEqualTo("20190411083623719000");
|
||||
|
||||
List<CardRangeEntity> cardRanges = cardRangeRepository.findByPkProviderId("2");
|
||||
assertThat(cardRanges).hasSize(3);
|
||||
|
||||
}
|
||||
|
||||
private void stubForOkResponse() throws IOException {
|
||||
stubFor(post(urlEqualTo("/"))
|
||||
.willReturn(aResponse()
|
||||
.withStatus(HttpStatus.OK.value())
|
||||
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.withBody(readStringFromFile("preparation-flow-response-1.json"))));
|
||||
}
|
||||
|
||||
private void stubWithErrorAndThenOkResponse() throws IOException {
|
||||
stubFor(post(urlEqualTo("/"))
|
||||
.inScenario("preparation")
|
||||
.whenScenarioStateIs(STARTED)
|
||||
.willReturn(aResponse()
|
||||
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()))
|
||||
.willSetStateTo("OK"));
|
||||
|
||||
stubFor(post(urlEqualTo("/"))
|
||||
.inScenario("preparation")
|
||||
.whenScenarioStateIs("OK")
|
||||
.willReturn(aResponse()
|
||||
.withStatus(HttpStatus.OK.value())
|
||||
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
|
||||
.withBody(readStringFromFile("preparation-flow-response-2.json"))));
|
||||
}
|
||||
|
||||
private String readStringFromFile(String fileName) throws IOException {
|
||||
return IOUtils.toString(
|
||||
resourceLoader.getResource("classpath:__files/" + fileName).getInputStream(),
|
||||
Charsets.UTF_8);
|
||||
}
|
||||
}
|
99
src/test/resources/__files/preparation-flow-response-1.json
Normal file
99
src/test/resources/__files/preparation-flow-response-1.json
Normal file
@ -0,0 +1,99 @@
|
||||
{
|
||||
"messageType": "RBKMONEY_PREPARATION_RESPONSE",
|
||||
"providerId": "1",
|
||||
"serialNum": "20190411083623719000",
|
||||
"addedCardRanges": [
|
||||
{
|
||||
"startRange": "6543200100000",
|
||||
"endRange": "6543200199999"
|
||||
},
|
||||
{
|
||||
"startRange": "65432102000000",
|
||||
"endRange": "65432102999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654310400000000",
|
||||
"endRange": "7654310499999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654340600000000",
|
||||
"endRange": "7654340699999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654350700000000",
|
||||
"endRange": "7654350799999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654360800000000",
|
||||
"endRange": "7654360899999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654370900000000",
|
||||
"endRange": "7654370999999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654381000000000",
|
||||
"endRange": "7654381099999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654391100000000",
|
||||
"endRange": "7654391199999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765411200000000",
|
||||
"endRange": "8765411299999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765431400000000",
|
||||
"endRange": "8765431499999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765441500000000",
|
||||
"endRange": "8765441599999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765451600000000",
|
||||
"endRange": "8765451699999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765461700000000",
|
||||
"endRange": "8765461799999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765471800000000",
|
||||
"endRange": "8765471899999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765481900000000",
|
||||
"endRange": "8765481999999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765492000000000",
|
||||
"endRange": "8765492099999999"
|
||||
},
|
||||
{
|
||||
"startRange": "1876542400000000000",
|
||||
"endRange": "1876542499999999999"
|
||||
},
|
||||
{
|
||||
"startRange": "8765422500000000",
|
||||
"endRange": "8765422599999999"
|
||||
}
|
||||
],
|
||||
"modifiedCardRanges": [
|
||||
{
|
||||
"startRange": "9876512600000000",
|
||||
"endRange": "9876512699999999"
|
||||
},
|
||||
{
|
||||
"startRange": "765430270000000",
|
||||
"endRange": "765430279999999"
|
||||
}
|
||||
],
|
||||
"deletedCardRanges": [
|
||||
{
|
||||
"startRange": "765430270000000",
|
||||
"endRange": "765430279999999"
|
||||
}
|
||||
]
|
||||
}
|
21
src/test/resources/__files/preparation-flow-response-2.json
Normal file
21
src/test/resources/__files/preparation-flow-response-2.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"messageType": "RBKMONEY_PREPARATION_RESPONSE",
|
||||
"providerId": "2",
|
||||
"serialNum": "20190411083623719000",
|
||||
"addedCardRanges": [
|
||||
{
|
||||
"startRange": "6543200100000",
|
||||
"endRange": "6543200199999"
|
||||
},
|
||||
{
|
||||
"startRange": "65432102000000",
|
||||
"endRange": "65432102999999"
|
||||
},
|
||||
{
|
||||
"startRange": "7654310400000000",
|
||||
"endRange": "7654310499999999"
|
||||
}
|
||||
],
|
||||
"modifiedCardRanges": [],
|
||||
"deletedCardRanges": []
|
||||
}
|
Loading…
Reference in New Issue
Block a user