mirror of
https://github.com/valitydev/adapter-flow-lib.git
synced 2024-11-06 00:05:22 +00:00
Add flow for work with qr code (#69)
This commit is contained in:
parent
23c1665f0e
commit
94418ab4ea
2
pom.xml
2
pom.xml
@ -12,7 +12,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>adapter-flow-lib</artifactId>
|
<artifactId>adapter-flow-lib</artifactId>
|
||||||
<version>0.1.25</version>
|
<version>0.2.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>adapter-flow-lib</name>
|
<name>adapter-flow-lib</name>
|
||||||
|
@ -4,14 +4,17 @@ import dev.vality.adapter.common.cds.CdsStorageClient;
|
|||||||
import dev.vality.adapter.common.cds.model.CardDataProxyModel;
|
import dev.vality.adapter.common.cds.model.CardDataProxyModel;
|
||||||
import dev.vality.adapter.common.damsel.ProxyProviderPackageCreators;
|
import dev.vality.adapter.common.damsel.ProxyProviderPackageCreators;
|
||||||
import dev.vality.adapter.common.damsel.ProxyProviderPackageExtractors;
|
import dev.vality.adapter.common.damsel.ProxyProviderPackageExtractors;
|
||||||
import dev.vality.adapter.flow.lib.constant.*;
|
import dev.vality.adapter.flow.lib.constant.MetaData;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.OptionFields;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Step;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.TargetStatus;
|
||||||
import dev.vality.adapter.flow.lib.model.*;
|
import dev.vality.adapter.flow.lib.model.*;
|
||||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
||||||
|
import dev.vality.adapter.flow.lib.service.CallbackUrlExtractor;
|
||||||
import dev.vality.adapter.flow.lib.service.CardDataService;
|
import dev.vality.adapter.flow.lib.service.CardDataService;
|
||||||
import dev.vality.adapter.flow.lib.service.IdGenerator;
|
import dev.vality.adapter.flow.lib.service.IdGenerator;
|
||||||
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
import dev.vality.adapter.flow.lib.service.TemporaryContextService;
|
||||||
import dev.vality.adapter.flow.lib.utils.AdapterProperties;
|
import dev.vality.adapter.flow.lib.utils.AdapterProperties;
|
||||||
import dev.vality.adapter.flow.lib.service.CallbackUrlExtractor;
|
|
||||||
import dev.vality.adapter.flow.lib.utils.CardDataUtils;
|
import dev.vality.adapter.flow.lib.utils.CardDataUtils;
|
||||||
import dev.vality.adapter.flow.lib.utils.TargetStatusResolver;
|
import dev.vality.adapter.flow.lib.utils.TargetStatusResolver;
|
||||||
import dev.vality.cds.storage.Auth3DS;
|
import dev.vality.cds.storage.Auth3DS;
|
||||||
@ -55,7 +58,8 @@ public class CtxToEntryModelConverter implements Converter<PaymentContext, Entry
|
|||||||
MobilePaymentData mobilePaymentData = null;
|
MobilePaymentData mobilePaymentData = null;
|
||||||
if (paymentResource.isSetDisposablePaymentResource()
|
if (paymentResource.isSetDisposablePaymentResource()
|
||||||
&& currentStep == null
|
&& currentStep == null
|
||||||
&& targetStatus == TargetStatus.PROCESSED) {
|
&& targetStatus == TargetStatus.PROCESSED
|
||||||
|
&& paymentResource.getDisposablePaymentResource().getPaymentTool().isSetBankCard()) {
|
||||||
SessionData sessionData = cdsStorageClient.getSessionData(context);
|
SessionData sessionData = cdsStorageClient.getSessionData(context);
|
||||||
cardData = initCardData(context, paymentResource, sessionData);
|
cardData = initCardData(context, paymentResource, sessionData);
|
||||||
mobilePaymentData = initMobilePaymentData(sessionData);
|
mobilePaymentData = initMobilePaymentData(sessionData);
|
||||||
@ -119,7 +123,7 @@ public class CtxToEntryModelConverter implements Converter<PaymentContext, Entry
|
|||||||
return StringUtils.hasText(redirectUrl)
|
return StringUtils.hasText(redirectUrl)
|
||||||
? redirectUrl
|
? redirectUrl
|
||||||
: adapterConfigurations.getOrDefault(
|
: adapterConfigurations.getOrDefault(
|
||||||
OptionFields.FAILED_URL.name(), adapterProperties.getFailedRedirectUrl());
|
OptionFields.FAILED_URL.name(), adapterProperties.getFailedRedirectUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
private dev.vality.adapter.flow.lib.model.CardData initCardData(PaymentContext context,
|
private dev.vality.adapter.flow.lib.model.CardData initCardData(PaymentContext context,
|
||||||
@ -149,7 +153,7 @@ public class CtxToEntryModelConverter implements Converter<PaymentContext, Entry
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMobilePay(SessionData sessionData) {
|
private boolean isMobilePay(SessionData sessionData) {
|
||||||
return sessionData.isSetAuthData() && sessionData.getAuthData().isSetAuth3ds();
|
return sessionData != null && sessionData.isSetAuthData() && sessionData.getAuthData().isSetAuth3ds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RefundData initRefundData(PaymentInfo paymentInfo) {
|
private RefundData initRefundData(PaymentInfo paymentInfo) {
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.flow.simple;
|
||||||
|
|
||||||
|
import dev.vality.adapter.flow.lib.flow.RecurrentResultIntentResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.service.factory.SimpleRecurrentIntentResultFactory;
|
||||||
|
import dev.vality.damsel.proxy_provider.RecurrentTokenIntent;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UnsupportedGenerateTokenResultIntentResolver implements RecurrentResultIntentResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecurrentTokenIntent initIntentByStep(ExitStateModel exitStateModel) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.flow.simple;
|
||||||
|
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Step;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.AbstractGenerateTokenStepResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||||
|
|
||||||
|
public class UnsupportedGenerateTokenStepResolverImpl extends
|
||||||
|
AbstractGenerateTokenStepResolver<EntryStateModel, ExitStateModel> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Step resolveCurrentStep(EntryStateModel stateModel) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Step resolveNextStep(ExitStateModel exitStateModel) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
@ -1,43 +1,11 @@
|
|||||||
package dev.vality.adapter.flow.lib.handler;
|
package dev.vality.adapter.flow.lib.handler;
|
||||||
|
|
||||||
import dev.vality.adapter.flow.lib.exception.UnknownHandlerForStepException;
|
|
||||||
import dev.vality.adapter.flow.lib.flow.StepResolver;
|
|
||||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
|
||||||
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.thrift.TException;
|
import org.apache.thrift.TException;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
|
|
||||||
import java.util.List;
|
public interface ServerFlowHandler<T, R> {
|
||||||
|
|
||||||
@Slf4j
|
default R handle(T context) throws TException {
|
||||||
@RequiredArgsConstructor
|
throw new UnsupportedOperationException();
|
||||||
public class ServerFlowHandler<T, R> {
|
|
||||||
|
|
||||||
private final List<CommonHandler<ExitStateModel, EntryStateModel>> handlers;
|
|
||||||
private final StepResolver<EntryStateModel, ExitStateModel> stepResolver;
|
|
||||||
private final Converter<T, EntryStateModel> entryConverter;
|
|
||||||
private final Converter<ExitStateModel, R> exitConverter;
|
|
||||||
|
|
||||||
public R handle(T context) throws TException {
|
|
||||||
var entryStateModel = prepareEntryState(entryConverter, context);
|
|
||||||
log.info("EntryStateModel: {}", entryStateModel);
|
|
||||||
var exitStateModel = handlers.stream()
|
|
||||||
.filter(handler -> handler.isHandle(entryStateModel))
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new UnknownHandlerForStepException("Can't find handler to data: " + entryStateModel))
|
|
||||||
.handle(entryStateModel);
|
|
||||||
log.info("ExitStateModel: {}", exitStateModel);
|
|
||||||
exitStateModel.setEntryStateModel(entryStateModel);
|
|
||||||
exitStateModel.setNextStep(stepResolver.resolveNextStep(exitStateModel));
|
|
||||||
log.info("Step changing: {} -> {}", entryStateModel.getCurrentStep(), exitStateModel.getNextStep());
|
|
||||||
return exitConverter.convert(exitStateModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntryStateModel prepareEntryState(Converter<T, EntryStateModel> entryConverter, T context) {
|
|
||||||
EntryStateModel entryStateModel = entryConverter.convert(context);
|
|
||||||
entryStateModel.setCurrentStep(stepResolver.resolveCurrentStep(entryStateModel));
|
|
||||||
return entryStateModel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.handler;
|
||||||
|
|
||||||
|
import dev.vality.adapter.flow.lib.exception.UnknownHandlerForStepException;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.StepResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ServerFlowHandlerImpl<T, R> implements ServerFlowHandler<T, R> {
|
||||||
|
|
||||||
|
private final List<CommonHandler<ExitStateModel, EntryStateModel>> handlers;
|
||||||
|
private final StepResolver<EntryStateModel, ExitStateModel> stepResolver;
|
||||||
|
private final Converter<T, EntryStateModel> entryConverter;
|
||||||
|
private final Converter<ExitStateModel, R> exitConverter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R handle(T context) throws TException {
|
||||||
|
var entryStateModel = prepareEntryState(entryConverter, context);
|
||||||
|
log.info("EntryStateModel: {}", entryStateModel);
|
||||||
|
var exitStateModel = handlers.stream()
|
||||||
|
.filter(handler -> handler.isHandle(entryStateModel))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new UnknownHandlerForStepException("Can't find handler to data: " + entryStateModel))
|
||||||
|
.handle(entryStateModel);
|
||||||
|
log.info("ExitStateModel: {}", exitStateModel);
|
||||||
|
exitStateModel.setEntryStateModel(entryStateModel);
|
||||||
|
exitStateModel.setNextStep(stepResolver.resolveNextStep(exitStateModel));
|
||||||
|
log.info("Step changing: {} -> {}", entryStateModel.getCurrentStep(), exitStateModel.getNextStep());
|
||||||
|
return exitConverter.convert(exitStateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntryStateModel prepareEntryState(Converter<T, EntryStateModel> entryConverter, T context) {
|
||||||
|
EntryStateModel entryStateModel = entryConverter.convert(context);
|
||||||
|
entryStateModel.setCurrentStep(stepResolver.resolveCurrentStep(entryStateModel));
|
||||||
|
return entryStateModel;
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,10 @@ public class BaseResponseModel {
|
|||||||
* Data for choose 3ds flow and parameters for redirects.
|
* Data for choose 3ds flow and parameters for redirects.
|
||||||
*/
|
*/
|
||||||
private ThreeDsData threeDsData;
|
private ThreeDsData threeDsData;
|
||||||
|
/**
|
||||||
|
* Data for display qr code.
|
||||||
|
*/
|
||||||
|
private QrDisplayData qrDisplayData;
|
||||||
/**
|
/**
|
||||||
* Data for support about transactions.
|
* Data for support about transactions.
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,7 @@ public class ExitStateModel {
|
|||||||
private Map<String, String> trxExtra;
|
private Map<String, String> trxExtra;
|
||||||
private PollingInfo pollingInfo;
|
private PollingInfo pollingInfo;
|
||||||
private ThreeDsData threeDsData;
|
private ThreeDsData threeDsData;
|
||||||
|
private QrDisplayData qrDisplayData;
|
||||||
private AdditionalTrxInfo additionalTrxInfo;
|
private AdditionalTrxInfo additionalTrxInfo;
|
||||||
|
|
||||||
private String recToken;
|
private String recToken;
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class QrDisplayData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url for get qr code.
|
||||||
|
*/
|
||||||
|
private String qrUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id for generate tag
|
||||||
|
*/
|
||||||
|
private String tagId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.processor;
|
||||||
|
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Status;
|
||||||
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.utils.ErrorUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class QrDisplayProcessor implements Processor<ExitStateModel, BaseResponseModel, EntryStateModel> {
|
||||||
|
|
||||||
|
private final Processor<ExitStateModel, BaseResponseModel, EntryStateModel> nextProcessor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExitStateModel process(BaseResponseModel response, EntryStateModel entryStateModel) {
|
||||||
|
if (response.getStatus() == Status.NEED_REDIRECT
|
||||||
|
&& !ErrorUtils.isError(response)
|
||||||
|
&& response.getQrDisplayData() != null
|
||||||
|
&& response.getQrDisplayData().getQrUrl() != null) {
|
||||||
|
log.debug("Start qr display process response: {} entryStateModel: {}", response, entryStateModel);
|
||||||
|
ExitStateModel exitStateModel = new ExitStateModel();
|
||||||
|
exitStateModel.setQrDisplayData(response.getQrDisplayData());
|
||||||
|
exitStateModel.setLastOperationStatus(response.getStatus());
|
||||||
|
exitStateModel.setProviderTrxId(response.getProviderTrxId());
|
||||||
|
exitStateModel.setTrxExtra(response.getSaveData());
|
||||||
|
exitStateModel.setAdditionalTrxInfo(response.getAdditionalTrxInfo());
|
||||||
|
exitStateModel.setCustomContext(response.getCustomContext());
|
||||||
|
log.debug("Finish qr display process response: {} entryStateModel: {}", response, entryStateModel);
|
||||||
|
return exitStateModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextProcessor != null) {
|
||||||
|
return nextProcessor.process(response, entryStateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Processor didn't match for response " + response);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,12 @@ public class TagManagementService {
|
|||||||
Optional<String> first = adapterProperties.getTagGeneratorFieldNames().stream()
|
Optional<String> first = adapterProperties.getTagGeneratorFieldNames().stream()
|
||||||
.filter(s -> StringUtils.hasText(parameters.get(s)))
|
.filter(s -> StringUtils.hasText(parameters.get(s)))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
return adapterProperties.getTagPrefix() + parameters.get(first.get());
|
String tagId = parameters.get(first.get());
|
||||||
|
return initTag(tagId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String initTag(String tagId) {
|
||||||
|
return adapterProperties.getTagPrefix() + tagId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,131 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.service.factory;
|
||||||
|
|
||||||
|
import dev.vality.adapter.common.mapper.ErrorMapping;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.HttpMethod;
|
||||||
|
import dev.vality.adapter.flow.lib.model.*;
|
||||||
|
import dev.vality.adapter.flow.lib.serde.ParametersSerializer;
|
||||||
|
import dev.vality.adapter.flow.lib.service.ExponentialBackOffPollingService;
|
||||||
|
import dev.vality.adapter.flow.lib.service.PollingInfoService;
|
||||||
|
import dev.vality.adapter.flow.lib.service.TagManagementService;
|
||||||
|
import dev.vality.adapter.flow.lib.utils.TimeoutUtils;
|
||||||
|
import dev.vality.adapter.flow.lib.utils.TimerProperties;
|
||||||
|
import dev.vality.damsel.base.Timer;
|
||||||
|
import dev.vality.damsel.proxy_provider.*;
|
||||||
|
import dev.vality.damsel.timeout_behaviour.TimeoutBehaviour;
|
||||||
|
import dev.vality.damsel.user_interaction.QrCode;
|
||||||
|
import dev.vality.damsel.user_interaction.QrCodeDisplayRequest;
|
||||||
|
import dev.vality.damsel.user_interaction.UserInteraction;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static dev.vality.adapter.common.damsel.OptionsExtractors.extractRedirectTimeout;
|
||||||
|
import static dev.vality.adapter.common.damsel.ProxyProviderPackageCreators.*;
|
||||||
|
import static dev.vality.adapter.flow.lib.utils.ThreeDsDataInitializer.TAG;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IntentResultQrPaymentFactory implements IntentResultFactory {
|
||||||
|
|
||||||
|
private final TimerProperties timerProperties;
|
||||||
|
private final TagManagementService tagManagementService;
|
||||||
|
private final ParametersSerializer parametersSerializer;
|
||||||
|
private final PollingInfoService pollingInfoService;
|
||||||
|
private final ErrorMapping errorMapping;
|
||||||
|
private final ExponentialBackOffPollingService exponentialBackOffPollingService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createFinishIntentSuccessWithCheckToken(ExitStateModel exitStateModel) {
|
||||||
|
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||||
|
if (entryStateModel.getBaseRequestModel().getRecurrentPaymentData() != null
|
||||||
|
&& entryStateModel.getBaseRequestModel().getRecurrentPaymentData().isMakeRecurrent()) {
|
||||||
|
return createFinishIntentSuccessWithToken(exitStateModel.getRecToken());
|
||||||
|
}
|
||||||
|
return createFinishIntentSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createSuspendIntentWithFailedAfterTimeout(ExitStateModel exitStateModel) {
|
||||||
|
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||||
|
QrDisplayData qrDisplayData = exitStateModel.getQrDisplayData();
|
||||||
|
Map<String, String> adapterConfigurations = entryStateModel.getBaseRequestModel().getAdapterConfigurations();
|
||||||
|
int timerRedirectTimeoutMin = extractRedirectTimeout(
|
||||||
|
adapterConfigurations,
|
||||||
|
timerProperties.getRedirectTimeoutMin());
|
||||||
|
return Intent.suspend(
|
||||||
|
new SuspendIntent(
|
||||||
|
tagManagementService.initTag(qrDisplayData.getTagId()),
|
||||||
|
Timer.timeout(TimeoutUtils.toSeconds(timerRedirectTimeoutMin))
|
||||||
|
).setUserInteraction(
|
||||||
|
UserInteraction.qr_code_display_request(new QrCodeDisplayRequest()
|
||||||
|
.setQrCode(new QrCode()
|
||||||
|
.setPayload(qrDisplayData.getQrUrl().getBytes())))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createSuspendIntentWithCallbackAfterTimeout(ExitStateModel exitStateModel) {
|
||||||
|
QrDisplayData qrDisplayData = exitStateModel.getQrDisplayData();
|
||||||
|
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||||
|
|
||||||
|
PollingInfo pollingInfo = pollingInfoService.initPollingInfo(entryStateModel);
|
||||||
|
if (pollingInfoService.isDeadline(pollingInfo)) {
|
||||||
|
return createFinishIntentFailed("Sleep timeout", "Max time pool limit reached");
|
||||||
|
}
|
||||||
|
exitStateModel.setPollingInfo(pollingInfo);
|
||||||
|
Map<String, String> adapterConfigurations = entryStateModel.getBaseRequestModel().getAdapterConfigurations();
|
||||||
|
int timerRedirectTimeoutMin = extractRedirectTimeout(
|
||||||
|
adapterConfigurations,
|
||||||
|
timerProperties.getRedirectTimeoutMin());
|
||||||
|
|
||||||
|
|
||||||
|
String tag = tagManagementService.initTag(qrDisplayData.getTagId());
|
||||||
|
return Intent.suspend(
|
||||||
|
new SuspendIntent(
|
||||||
|
tag,
|
||||||
|
Timer.timeout(TimeoutUtils.toSeconds(timerRedirectTimeoutMin)))
|
||||||
|
.setTimeoutBehaviour(TimeoutBehaviour.callback(
|
||||||
|
ByteBuffer.wrap(
|
||||||
|
parametersSerializer.writeByte(Map.of(TAG, tag))))
|
||||||
|
).setUserInteraction(
|
||||||
|
UserInteraction.qr_code_display_request(new QrCodeDisplayRequest()
|
||||||
|
.setQrCode(new QrCode()
|
||||||
|
.setPayload(qrDisplayData.getQrUrl().getBytes()))))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createFinishIntentSuccess() {
|
||||||
|
return Intent.finish(new FinishIntent(FinishStatus.success(new Success())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createSleepIntentWithExponentialPolling(ExitStateModel exitStateModel) {
|
||||||
|
EntryStateModel entryStateModel = exitStateModel.getEntryStateModel();
|
||||||
|
PollingInfo pollingInfo = pollingInfoService.initPollingInfo(entryStateModel);
|
||||||
|
if (pollingInfoService.isDeadline(pollingInfo)) {
|
||||||
|
return createFinishIntentFailed("Sleep timeout", "Max time pool limit reached");
|
||||||
|
}
|
||||||
|
exitStateModel.setPollingInfo(pollingInfo);
|
||||||
|
|
||||||
|
Map<String, String> adapterConfigurations = entryStateModel.getBaseRequestModel().getAdapterConfigurations();
|
||||||
|
int nextTimeoutSec =
|
||||||
|
exponentialBackOffPollingService.prepareNextPollingInterval(pollingInfo, adapterConfigurations);
|
||||||
|
return Intent.sleep(new SleepIntent(Timer.timeout(nextTimeoutSec)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createFinishIntentFailed(ExitStateModel exitStateModel) {
|
||||||
|
return Intent.finish(new FinishIntent(FinishStatus.failure(
|
||||||
|
errorMapping.mapFailure(exitStateModel.getErrorCode(),
|
||||||
|
exitStateModel.getErrorMessage()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createFinishIntentFailed(String errorCode, String errorMessage) {
|
||||||
|
return Intent.finish(new FinishIntent(FinishStatus.failure(
|
||||||
|
errorMapping.mapFailure(errorCode, errorMessage))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@ import dev.vality.adapter.flow.lib.serde.ParametersSerializer;
|
|||||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
import dev.vality.adapter.flow.lib.serde.TemporaryContextDeserializer;
|
||||||
import dev.vality.adapter.flow.lib.serde.TemporaryContextSerializer;
|
import dev.vality.adapter.flow.lib.serde.TemporaryContextSerializer;
|
||||||
import dev.vality.adapter.flow.lib.service.*;
|
import dev.vality.adapter.flow.lib.service.*;
|
||||||
|
import dev.vality.adapter.flow.lib.service.factory.IntentResultFactory;
|
||||||
import dev.vality.adapter.flow.lib.service.factory.SimpleIntentResultFactory;
|
import dev.vality.adapter.flow.lib.service.factory.SimpleIntentResultFactory;
|
||||||
import dev.vality.adapter.flow.lib.service.factory.SimpleRecurrentIntentResultFactory;
|
import dev.vality.adapter.flow.lib.service.factory.SimpleRecurrentIntentResultFactory;
|
||||||
import dev.vality.adapter.flow.lib.utils.AdapterProperties;
|
import dev.vality.adapter.flow.lib.utils.AdapterProperties;
|
||||||
@ -187,7 +188,7 @@ public class HandlerConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ExitModelToProxyResultConverter exitModelToProxyResultConverter(
|
public ExitModelToProxyResultConverter exitModelToProxyResultConverter(
|
||||||
SimpleIntentResultFactory intentResultFactory,
|
IntentResultFactory intentResultFactory,
|
||||||
TemporaryContextSerializer temporaryContextSerializer,
|
TemporaryContextSerializer temporaryContextSerializer,
|
||||||
ResultIntentResolver resultIntentResolver,
|
ResultIntentResolver resultIntentResolver,
|
||||||
ExitStateModelToTemporaryContextConverter exitStateModelToTemporaryContextConverter) {
|
ExitStateModelToTemporaryContextConverter exitStateModelToTemporaryContextConverter) {
|
||||||
|
@ -15,6 +15,7 @@ import dev.vality.adapter.flow.lib.flow.full.GenerateTokenResultIntentResolverIm
|
|||||||
import dev.vality.adapter.flow.lib.flow.full.ResultIntentResolverImpl;
|
import dev.vality.adapter.flow.lib.flow.full.ResultIntentResolverImpl;
|
||||||
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
||||||
import dev.vality.adapter.flow.lib.handler.ServerFlowHandler;
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandler;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandlerImpl;
|
||||||
import dev.vality.adapter.flow.lib.handler.payment.*;
|
import dev.vality.adapter.flow.lib.handler.payment.*;
|
||||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
@ -53,7 +54,7 @@ public class FullThreeDsFlowConfig {
|
|||||||
StepResolver<EntryStateModel, ExitStateModel> fullThreeDsAllVersionsStepResolverImpl,
|
StepResolver<EntryStateModel, ExitStateModel> fullThreeDsAllVersionsStepResolverImpl,
|
||||||
CtxToEntryModelConverter ctxToEntryModelConverter,
|
CtxToEntryModelConverter ctxToEntryModelConverter,
|
||||||
ExitModelToProxyResultConverter exitModelToProxyResultConverter) {
|
ExitModelToProxyResultConverter exitModelToProxyResultConverter) {
|
||||||
return new ServerFlowHandler<>(
|
return new ServerFlowHandlerImpl<>(
|
||||||
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
fullThreeDsAllVersionsStepResolverImpl,
|
fullThreeDsAllVersionsStepResolverImpl,
|
||||||
ctxToEntryModelConverter,
|
ctxToEntryModelConverter,
|
||||||
@ -69,7 +70,7 @@ public class FullThreeDsFlowConfig {
|
|||||||
RecCtxToEntryModelConverter recCtxToEntryStateModelConverter,
|
RecCtxToEntryModelConverter recCtxToEntryStateModelConverter,
|
||||||
ExitModelToRecTokenProxyResultConverter exitModelToRecTokenProxyResultConverter
|
ExitModelToRecTokenProxyResultConverter exitModelToRecTokenProxyResultConverter
|
||||||
) {
|
) {
|
||||||
return new ServerFlowHandler<>(
|
return new ServerFlowHandlerImpl<>(
|
||||||
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
generateTokenFullThreeDsAllVersionsStepResolverImpl,
|
generateTokenFullThreeDsAllVersionsStepResolverImpl,
|
||||||
recCtxToEntryStateModelConverter,
|
recCtxToEntryStateModelConverter,
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.flow.qr;
|
||||||
|
|
||||||
|
import dev.vality.adapter.flow.lib.flow.AbstractPaymentTest;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.qr.config.QrRedirectWithPollingDsFlowConfig;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.utils.BeanUtils;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.utils.MockUtil;
|
||||||
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentContext;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentProxyResult;
|
||||||
|
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.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@ContextConfiguration(classes = QrRedirectWithPollingDsFlowConfig.class)
|
||||||
|
@TestPropertySource(properties = {"server.rest.port=8083",
|
||||||
|
"error-mapping.file=classpath:fixture/errors.json"})
|
||||||
|
public class ErrorPaymentQrTest extends AbstractPaymentTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() throws TException {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
MockUtil.mockAllWithout3Ds(cdsStorageClient, benderClient);
|
||||||
|
|
||||||
|
BaseResponseModel baseResponseModel = BeanUtils.createBaseResponseModel();
|
||||||
|
baseResponseModel.setErrorCode("rem_error_21");
|
||||||
|
baseResponseModel.setErrorMessage("Remote service error!");
|
||||||
|
|
||||||
|
Mockito.when(client.auth(any())).thenReturn(baseResponseModel);
|
||||||
|
Mockito.when(client.pay(any())).thenReturn(baseResponseModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErrorPayment() throws TException {
|
||||||
|
// pay
|
||||||
|
PaymentContext paymentContext = MockUtil.buildPaymentContext(String.valueOf(new Date().getTime()),
|
||||||
|
MockUtil.buildOptionsOneStage());
|
||||||
|
|
||||||
|
PaymentProxyResult paymentProxyResult = serverHandlerLogDecorator.processPayment(paymentContext);
|
||||||
|
assertTrue(paymentProxyResult.getIntent().isSetFinish());
|
||||||
|
assertTrue(paymentProxyResult.getIntent().getFinish().getStatus().isSetFailure());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.flow.qr;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.OptionFields;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Stage;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Status;
|
||||||
|
import dev.vality.adapter.flow.lib.constant.Step;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.AbstractPaymentTest;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.qr.config.QrRedirectWithPollingDsFlowConfig;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.utils.BeanUtils;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.utils.MockUtil;
|
||||||
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentContext;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentProxyResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@ContextConfiguration(classes = QrRedirectWithPollingDsFlowConfig.class)
|
||||||
|
@TestPropertySource(properties = {"error-mapping.file=classpath:fixture/errors.json",
|
||||||
|
"adapter.callbackUrl=http://localhost:8080/test",
|
||||||
|
"server.rest.endpoint=adapter",
|
||||||
|
"server.rest.port=8083"})
|
||||||
|
public class PaymentSuccessQrTest extends AbstractPaymentTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() throws TException {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
|
||||||
|
MockUtil.mockIdGenerator(benderClient);
|
||||||
|
|
||||||
|
BaseResponseModel baseResponseModel = BeanUtils.createBaseResponseModel();
|
||||||
|
baseResponseModel.setQrDisplayData(BeanUtils.createQrDisplayData());
|
||||||
|
baseResponseModel.setStatus(Status.NEED_REDIRECT);
|
||||||
|
|
||||||
|
Mockito.when(client.auth(any())).thenReturn(baseResponseModel);
|
||||||
|
Mockito.when(client.pay(any())).thenReturn(baseResponseModel);
|
||||||
|
|
||||||
|
BaseResponseModel successResponseModel = BeanUtils.createBaseResponseModel();
|
||||||
|
Mockito.when(client.capture(any())).thenReturn(successResponseModel);
|
||||||
|
Mockito.when(client.refund(any())).thenReturn(successResponseModel);
|
||||||
|
Mockito.when(client.status(any())).thenReturn(successResponseModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOneStage() throws TException, IOException {
|
||||||
|
// auth
|
||||||
|
Map<String, String> options = MockUtil.buildOptionsOneStage();
|
||||||
|
testQr(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTwoStage() throws TException, IOException {
|
||||||
|
// auth
|
||||||
|
Map<String, String> options = MockUtil.buildOptionsTwoStage();
|
||||||
|
testQr(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testQr(Map<String, String> options) throws TException, JsonProcessingException {
|
||||||
|
PaymentContext paymentContext = MockUtil.buildPaymentContextPaymentTerminal("invoice_id", options);
|
||||||
|
|
||||||
|
PaymentProxyResult paymentProxyResult = serverHandlerLogDecorator.processPayment(paymentContext);
|
||||||
|
assertTrue(paymentProxyResult.getIntent().getSuspend().getUserInteraction().isSetQrCodeDisplayRequest());
|
||||||
|
assertEquals(Step.CHECK_STATUS,
|
||||||
|
temporaryContextDeserializer.read(paymentProxyResult.getNextState()).getNextStep());
|
||||||
|
|
||||||
|
//checkStatus
|
||||||
|
paymentProxyResult = processWithDoNothingSuccessResult(paymentContext, paymentProxyResult);
|
||||||
|
|
||||||
|
//capture
|
||||||
|
if (Stage.ONE.equals(options.get(OptionFields.STAGE.name()))) {
|
||||||
|
paymentProxyResult = checkSuccessCapture(paymentContext, paymentProxyResult, new byte[] {});
|
||||||
|
processWithDoNothingSuccessResult(paymentContext, paymentProxyResult);
|
||||||
|
} else {
|
||||||
|
paymentProxyResult = processCaptureWithCheckStatusResult(paymentContext, paymentProxyResult, new byte[] {});
|
||||||
|
processWithCheckStatusResult(paymentContext, paymentProxyResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
//refund
|
||||||
|
checkSuccessRefund(1100L, paymentContext, paymentProxyResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package dev.vality.adapter.flow.lib.flow.qr.config;
|
||||||
|
|
||||||
|
import dev.vality.adapter.common.mapper.ErrorMapping;
|
||||||
|
import dev.vality.adapter.flow.lib.client.RemoteClient;
|
||||||
|
import dev.vality.adapter.flow.lib.converter.base.EntryModelToBaseRequestModelConverter;
|
||||||
|
import dev.vality.adapter.flow.lib.converter.entry.CtxToEntryModelConverter;
|
||||||
|
import dev.vality.adapter.flow.lib.converter.exit.ExitModelToProxyResultConverter;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.RecurrentResultIntentResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.ResultIntentResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.StepResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.simple.SimpleRedirectWithPollingResultIntentResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.simple.SimpleRedirectWithPollingStepResolverImpl;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.simple.UnsupportedGenerateTokenResultIntentResolver;
|
||||||
|
import dev.vality.adapter.flow.lib.flow.simple.UnsupportedGenerateTokenStepResolverImpl;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandler;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandlerImpl;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.payment.*;
|
||||||
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.ExitStateModel;
|
||||||
|
import dev.vality.adapter.flow.lib.processor.*;
|
||||||
|
import dev.vality.adapter.flow.lib.serde.ParametersSerializer;
|
||||||
|
import dev.vality.adapter.flow.lib.service.ExponentialBackOffPollingService;
|
||||||
|
import dev.vality.adapter.flow.lib.service.PollingInfoService;
|
||||||
|
import dev.vality.adapter.flow.lib.service.TagManagementService;
|
||||||
|
import dev.vality.adapter.flow.lib.service.factory.IntentResultQrPaymentFactory;
|
||||||
|
import dev.vality.adapter.flow.lib.utils.TimerProperties;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentContext;
|
||||||
|
import dev.vality.damsel.proxy_provider.PaymentProxyResult;
|
||||||
|
import dev.vality.damsel.proxy_provider.RecurrentTokenContext;
|
||||||
|
import dev.vality.damsel.proxy_provider.RecurrentTokenProxyResult;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class QrRedirectWithPollingDsFlowConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StepResolver<EntryStateModel, ExitStateModel> stepResolverImpl() {
|
||||||
|
return new SimpleRedirectWithPollingStepResolverImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StepResolver<EntryStateModel, ExitStateModel> generateTokenStepResolverImpl() {
|
||||||
|
return new UnsupportedGenerateTokenStepResolverImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServerFlowHandler<PaymentContext, PaymentProxyResult> serverFlowHandler(
|
||||||
|
RemoteClient client,
|
||||||
|
EntryModelToBaseRequestModelConverter entryModelToBaseRequestModelConverter,
|
||||||
|
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> baseProcessor,
|
||||||
|
StepResolver<EntryStateModel, ExitStateModel> stepResolverImpl,
|
||||||
|
CtxToEntryModelConverter ctxToEntryModelConverter,
|
||||||
|
ExitModelToProxyResultConverter exitModelToProxyResultConverter) {
|
||||||
|
return new ServerFlowHandlerImpl<>(
|
||||||
|
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
stepResolverImpl,
|
||||||
|
ctxToEntryModelConverter,
|
||||||
|
exitModelToProxyResultConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public Processor<ExitStateModel, BaseResponseModel, EntryStateModel> baseProcessor() {
|
||||||
|
ErrorProcessor errorProcessor = new ErrorProcessor();
|
||||||
|
SuccessFinishProcessor baseProcessor = new SuccessFinishProcessor(errorProcessor);
|
||||||
|
QrDisplayProcessor qrDisplayProcessor = new QrDisplayProcessor(baseProcessor);
|
||||||
|
return new RetryProcessor(qrDisplayProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RecurrentResultIntentResolver recurrentResultIntentResolver() {
|
||||||
|
return new UnsupportedGenerateTokenResultIntentResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ResultIntentResolver resultIntentResolver(IntentResultQrPaymentFactory intentResultFactory) {
|
||||||
|
return new SimpleRedirectWithPollingResultIntentResolver(intentResultFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServerFlowHandler<RecurrentTokenContext, RecurrentTokenProxyResult> generateTokenFlowHandler() {
|
||||||
|
return new ServerFlowHandler<>() {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IntentResultQrPaymentFactory intentResultFactory(
|
||||||
|
TimerProperties timerProperties,
|
||||||
|
TagManagementService tagManagementService,
|
||||||
|
ParametersSerializer parametersSerializer,
|
||||||
|
PollingInfoService pollingInfoService,
|
||||||
|
ErrorMapping errorMapping,
|
||||||
|
ExponentialBackOffPollingService exponentialBackOffPollingService) {
|
||||||
|
return new IntentResultQrPaymentFactory(timerProperties, tagManagementService,
|
||||||
|
parametersSerializer, pollingInfoService, errorMapping, exponentialBackOffPollingService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CommonHandler<ExitStateModel, EntryStateModel>> getHandlers(
|
||||||
|
RemoteClient client,
|
||||||
|
EntryModelToBaseRequestModelConverter entryModelToBaseRequestModelConverter,
|
||||||
|
Processor<ExitStateModel, BaseResponseModel, EntryStateModel> baseProcessor) {
|
||||||
|
return List.of(new AuthHandler(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
new CancelHandler(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
new CaptureHandler(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
new StatusHandler(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
new DoNothingHandler(),
|
||||||
|
new PaymentHandler(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
|
new RefundHandler(client, entryModelToBaseRequestModelConverter, baseProcessor));
|
||||||
|
}
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
package dev.vality.adapter.flow.lib.flow.simple.redirect;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import dev.vality.adapter.flow.lib.constant.Status;
|
|
||||||
import dev.vality.adapter.flow.lib.constant.Step;
|
|
||||||
import dev.vality.adapter.flow.lib.flow.AbstractGenerateTokenTest;
|
|
||||||
import dev.vality.adapter.flow.lib.flow.simple.redirect.config.SimpleRedirectWithPollingDsFlowConfig;
|
|
||||||
import dev.vality.adapter.flow.lib.flow.utils.BeanUtils;
|
|
||||||
import dev.vality.adapter.flow.lib.flow.utils.MockUtil;
|
|
||||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
|
||||||
import dev.vality.damsel.proxy_provider.RecurrentTokenContext;
|
|
||||||
import dev.vality.damsel.proxy_provider.RecurrentTokenProxyResult;
|
|
||||||
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.Mockito;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
|
||||||
import org.springframework.test.context.TestPropertySource;
|
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static dev.vality.adapter.flow.lib.flow.full.three.ds.ForwardRecurrentPaymentNon3dsTest.RECURRENT_TOKEN;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
|
||||||
@ContextConfiguration(classes = SimpleRedirectWithPollingDsFlowConfig.class)
|
|
||||||
@TestPropertySource(properties = {"server.rest.port=8083",
|
|
||||||
"error-mapping.file=classpath:fixture/errors.json",
|
|
||||||
"service.secret.enabled=true"})
|
|
||||||
public class GenerateToken3ds1Test extends AbstractGenerateTokenTest {
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() throws TException {
|
|
||||||
MockitoAnnotations.openMocks(this);
|
|
||||||
MockUtil.mockAllWithout3Ds(cdsStorageClient, benderClient);
|
|
||||||
|
|
||||||
BaseResponseModel baseResponseModel = BeanUtils.createBaseResponseModel();
|
|
||||||
baseResponseModel.setThreeDsData(BeanUtils.create3Ds1(baseResponseModel));
|
|
||||||
baseResponseModel.setStatus(Status.NEED_REDIRECT);
|
|
||||||
|
|
||||||
BaseResponseModel baseResponseModelRec = BeanUtils.createBaseResponseModel();
|
|
||||||
baseResponseModelRec.setRecurrentToken(RECURRENT_TOKEN);
|
|
||||||
|
|
||||||
Mockito.when(client.generateToken(any())).thenReturn(baseResponseModel);
|
|
||||||
Mockito.when(client.status(any())).thenReturn(baseResponseModelRec);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPaymentOneStage() throws TException, JsonProcessingException {
|
|
||||||
// pay
|
|
||||||
Map<String, String> options = MockUtil.buildOptionsOneStage();
|
|
||||||
|
|
||||||
RecurrentTokenContext paymentContext =
|
|
||||||
MockUtil.buildRecurrentTokenContext(String.valueOf(new Date().getTime()),
|
|
||||||
options);
|
|
||||||
RecurrentTokenProxyResult paymentProxyResult = serverHandlerLogDecorator.generateToken(paymentContext);
|
|
||||||
assertTrue(paymentProxyResult.getIntent().getSuspend().getUserInteraction().isSetRedirect());
|
|
||||||
assertEquals(Step.CHECK_STATUS,
|
|
||||||
temporaryContextDeserializer.read(paymentProxyResult.getNextState()).getNextStep());
|
|
||||||
|
|
||||||
//checkStatus
|
|
||||||
paymentProxyResult = processWithDoNothingSuccessResult(paymentContext, paymentProxyResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -15,6 +15,7 @@ import dev.vality.adapter.flow.lib.flow.simple.SimpleRedirectWithPollingResultIn
|
|||||||
import dev.vality.adapter.flow.lib.flow.simple.SimpleRedirectWithPollingStepResolverImpl;
|
import dev.vality.adapter.flow.lib.flow.simple.SimpleRedirectWithPollingStepResolverImpl;
|
||||||
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
import dev.vality.adapter.flow.lib.handler.CommonHandler;
|
||||||
import dev.vality.adapter.flow.lib.handler.ServerFlowHandler;
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandler;
|
||||||
|
import dev.vality.adapter.flow.lib.handler.ServerFlowHandlerImpl;
|
||||||
import dev.vality.adapter.flow.lib.handler.payment.*;
|
import dev.vality.adapter.flow.lib.handler.payment.*;
|
||||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
import dev.vality.adapter.flow.lib.model.EntryStateModel;
|
||||||
@ -52,7 +53,7 @@ public class SimpleRedirectWithPollingDsFlowConfig {
|
|||||||
StepResolver<EntryStateModel, ExitStateModel> stepResolverImpl,
|
StepResolver<EntryStateModel, ExitStateModel> stepResolverImpl,
|
||||||
CtxToEntryModelConverter ctxToEntryModelConverter,
|
CtxToEntryModelConverter ctxToEntryModelConverter,
|
||||||
ExitModelToProxyResultConverter exitModelToProxyResultConverter) {
|
ExitModelToProxyResultConverter exitModelToProxyResultConverter) {
|
||||||
return new ServerFlowHandler<>(
|
return new ServerFlowHandlerImpl<>(
|
||||||
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
stepResolverImpl,
|
stepResolverImpl,
|
||||||
ctxToEntryModelConverter,
|
ctxToEntryModelConverter,
|
||||||
@ -78,7 +79,7 @@ public class SimpleRedirectWithPollingDsFlowConfig {
|
|||||||
StepResolver<EntryStateModel, ExitStateModel> generateTokenStepResolverImpl,
|
StepResolver<EntryStateModel, ExitStateModel> generateTokenStepResolverImpl,
|
||||||
RecCtxToEntryModelConverter recCtxToEntryStateModelConverter,
|
RecCtxToEntryModelConverter recCtxToEntryStateModelConverter,
|
||||||
ExitModelToRecTokenProxyResultConverter exitModelToRecTokenProxyResultConverter) {
|
ExitModelToRecTokenProxyResultConverter exitModelToRecTokenProxyResultConverter) {
|
||||||
return new ServerFlowHandler<>(
|
return new ServerFlowHandlerImpl<>(
|
||||||
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
getHandlers(client, entryModelToBaseRequestModelConverter, baseProcessor),
|
||||||
generateTokenStepResolverImpl,
|
generateTokenStepResolverImpl,
|
||||||
recCtxToEntryStateModelConverter,
|
recCtxToEntryStateModelConverter,
|
||||||
|
@ -5,6 +5,7 @@ import dev.vality.adapter.common.utils.CommonConverter;
|
|||||||
import dev.vality.adapter.flow.lib.constant.Status;
|
import dev.vality.adapter.flow.lib.constant.Status;
|
||||||
import dev.vality.adapter.flow.lib.constant.ThreeDsType;
|
import dev.vality.adapter.flow.lib.constant.ThreeDsType;
|
||||||
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
import dev.vality.adapter.flow.lib.model.BaseResponseModel;
|
||||||
|
import dev.vality.adapter.flow.lib.model.QrDisplayData;
|
||||||
import dev.vality.adapter.flow.lib.model.ThreeDsData;
|
import dev.vality.adapter.flow.lib.model.ThreeDsData;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -41,6 +42,13 @@ public class BeanUtils {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static QrDisplayData createQrDisplayData() {
|
||||||
|
return QrDisplayData.builder()
|
||||||
|
.tagId("testTagId")
|
||||||
|
.qrUrl("http://localhost/3ds")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
public static ThreeDsData create3Ds2FullCheck() {
|
public static ThreeDsData create3Ds2FullCheck() {
|
||||||
HashMap<String, String> parameters = new HashMap<>();
|
HashMap<String, String> parameters = new HashMap<>();
|
||||||
parameters.put(THREE_DS_METHOD_DATA, "test_threeDsMethodData");
|
parameters.put(THREE_DS_METHOD_DATA, "test_threeDsMethodData");
|
||||||
|
@ -171,6 +171,38 @@ public class MockUtil {
|
|||||||
.setOptions(options);
|
.setOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PaymentContext buildPaymentContextPaymentTerminal(String invoiceId, Map<String, String> options) {
|
||||||
|
return new PaymentContext()
|
||||||
|
.setSession(new Session()
|
||||||
|
.setTarget(TargetInvoicePaymentStatus.processed(
|
||||||
|
new InvoicePaymentProcessed())))
|
||||||
|
.setPaymentInfo(new PaymentInfo()
|
||||||
|
.setInvoice(new Invoice()
|
||||||
|
.setId(invoiceId)
|
||||||
|
.setDetails(new InvoiceDetails()
|
||||||
|
.setDescription("details")))
|
||||||
|
.setPayment(new InvoicePayment()
|
||||||
|
.setId("payment_id")
|
||||||
|
.setCreatedAt("2016-03-22T06:12:27Z")
|
||||||
|
.setPaymentResource(PaymentResource.disposable_payment_resource(
|
||||||
|
new DisposablePaymentResource()
|
||||||
|
.setClientInfo(new ClientInfo()
|
||||||
|
.setIpAddress("185.31.132.50"))
|
||||||
|
.setPaymentTool(buildPaymentToolPaymentTerminal())))
|
||||||
|
.setCost(new Cash()
|
||||||
|
.setAmount(1200)
|
||||||
|
.setCurrency(new Currency()
|
||||||
|
.setSymbolicCode("RUB")
|
||||||
|
.setNumericCode((short) 643)))
|
||||||
|
.setContactInfo(new ContactInfo()
|
||||||
|
.setEmail("kkkk@kkk.ru")
|
||||||
|
.setPhoneNumber("89037772299"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
.setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<String, String> buildOptionsOneStage() {
|
public static Map<String, String> buildOptionsOneStage() {
|
||||||
return Map.of(OptionFields.STAGE.name(), Stage.ONE);
|
return Map.of(OptionFields.STAGE.name(), Stage.ONE);
|
||||||
}
|
}
|
||||||
@ -190,6 +222,12 @@ public class MockUtil {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PaymentTool buildPaymentToolPaymentTerminal() {
|
||||||
|
return PaymentTool.payment_terminal(
|
||||||
|
new PaymentTerminal()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static PaymentContext buildRecurrentPaymentContext(String invoiceId, String token) {
|
public static PaymentContext buildRecurrentPaymentContext(String invoiceId, String token) {
|
||||||
PaymentContext paymentContext = MockUtil.buildPaymentContext(invoiceId,
|
PaymentContext paymentContext = MockUtil.buildPaymentContext(invoiceId,
|
||||||
MockUtil.buildOptionsOneStage());
|
MockUtil.buildOptionsOneStage());
|
||||||
|
Loading…
Reference in New Issue
Block a user