nz.co.blink.debit.client.v1.BlinkDebitClient Maven / Gradle / Ivy
Show all versions of blink-debit-api-client-java-spring6 Show documentation
/**
* Copyright (c) 2022 BlinkPay
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package nz.co.blink.debit.client.v1;
import io.github.resilience4j.core.IntervalFunction;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.netty.handler.logging.LogLevel;
import jakarta.validation.Validation;
import lombok.extern.slf4j.Slf4j;
import nz.co.blink.debit.config.BlinkPayProperties;
import nz.co.blink.debit.dto.v1.BankMetadata;
import nz.co.blink.debit.dto.v1.Consent;
import nz.co.blink.debit.dto.v1.CreateConsentResponse;
import nz.co.blink.debit.dto.v1.CreateQuickPaymentResponse;
import nz.co.blink.debit.dto.v1.EnduringConsentRequest;
import nz.co.blink.debit.dto.v1.Payment;
import nz.co.blink.debit.dto.v1.PaymentRequest;
import nz.co.blink.debit.dto.v1.PaymentResponse;
import nz.co.blink.debit.dto.v1.QuickPaymentRequest;
import nz.co.blink.debit.dto.v1.QuickPaymentResponse;
import nz.co.blink.debit.dto.v1.Refund;
import nz.co.blink.debit.dto.v1.RefundDetail;
import nz.co.blink.debit.dto.v1.RefundResponse;
import nz.co.blink.debit.dto.v1.SingleConsentRequest;
import nz.co.blink.debit.enums.BlinkPayProperty;
import nz.co.blink.debit.exception.BlinkConsentRejectedException;
import nz.co.blink.debit.exception.BlinkConsentTimeoutException;
import nz.co.blink.debit.exception.BlinkPaymentRejectedException;
import nz.co.blink.debit.exception.BlinkPaymentTimeoutException;
import nz.co.blink.debit.exception.BlinkRetryableException;
import nz.co.blink.debit.exception.BlinkServiceException;
import nz.co.blink.debit.helpers.AccessTokenHandler;
import nz.co.blink.debit.helpers.DefaultPropertyProvider;
import nz.co.blink.debit.helpers.EnvironmentVariablePropertyProvider;
import nz.co.blink.debit.helpers.FilePropertyProvider;
import nz.co.blink.debit.helpers.PropertyProvider;
import nz.co.blink.debit.helpers.SystemPropertyProvider;
import nz.co.blink.debit.service.ValidationService;
import nz.co.blink.debit.service.impl.JakartaValidationServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.transport.logging.AdvancedByteBufFormat;
import java.net.ConnectException;
import java.time.Duration;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.regex.Pattern;
import static nz.co.blink.debit.dto.v1.Consent.StatusEnum.AUTHORISED;
import static nz.co.blink.debit.dto.v1.Payment.StatusEnum.ACCEPTEDSETTLEMENTCOMPLETED;
/**
* The facade for accessing all client methods from one place.
*/
@Component
@Slf4j
public class BlinkDebitClient {
private static final PropertyProvider PROPERTY_PROVIDER = new EnvironmentVariablePropertyProvider(new SystemPropertyProvider(new FilePropertyProvider(new DefaultPropertyProvider())));
private final SingleConsentsApiClient singleConsentsApiClient;
private final EnduringConsentsApiClient enduringConsentsApiClient;
private final QuickPaymentsApiClient quickPaymentsApiClient;
private final PaymentsApiClient paymentsApiClient;
private final RefundsApiClient refundsApiClient;
private final MetaApiClient metaApiClient;
private final ValidationService validationService;
private final Retry retry;
/**
* Default constructor for Spring-based consumer.
*
* @param singleConsentsApiClient the {@link SingleConsentsApiClient}
* @param enduringConsentsApiClient the {@link EnduringConsentsApiClient}
* @param quickPaymentsApiClient the {@link QuickPaymentsApiClient}
* @param paymentsApiClient the {@link PaymentsApiClient}
* @param refundsApiClient the {@link RefundsApiClient}
* @param metaApiClient the {@link MetaApiClient}
* @param validationService the {@link ValidationService}
* @param retry the {@link Retry}
*/
@Autowired
public BlinkDebitClient(SingleConsentsApiClient singleConsentsApiClient,
EnduringConsentsApiClient enduringConsentsApiClient,
QuickPaymentsApiClient quickPaymentsApiClient, PaymentsApiClient paymentsApiClient,
RefundsApiClient refundsApiClient, MetaApiClient metaApiClient, ValidationService validationService,
Retry retry) {
this.singleConsentsApiClient = singleConsentsApiClient;
this.enduringConsentsApiClient = enduringConsentsApiClient;
this.quickPaymentsApiClient = quickPaymentsApiClient;
this.paymentsApiClient = paymentsApiClient;
this.refundsApiClient = refundsApiClient;
this.metaApiClient = metaApiClient;
this.validationService = validationService;
this.retry = retry;
}
/**
* Constructor for pure Java application.
*
* @param properties the {@link Properties} retrieved from the configuration file
*/
public BlinkDebitClient(Properties properties) {
int maxConnections = Integer.parseInt(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_MAX_CONNECTIONS));
Duration maxIdleTime = Duration.parse(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_MAX_IDLE_TIME));
Duration maxLifeTime = Duration.parse(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_MAX_LIFE_TIME));
Duration pendingAcquireTimeout = Duration.parse(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_PENDING_ACQUIRE_TIMEOUT));
Duration evictionInterval = Duration.parse(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_EVICTION_INTERVAL));
String debitUrl = PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_DEBIT_URL);
String clientId = PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_CLIENT_ID);
String clientSecret = PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_CLIENT_SECRET);
String activeProfile = PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_ACTIVE_PROFILE);
boolean retryEnabled = Boolean.parseBoolean(PROPERTY_PROVIDER.getProperty(properties, BlinkPayProperty.BLINKPAY_RETRY_ENABLED));
BlinkPayProperties blinkPayProperties = new BlinkPayProperties();
blinkPayProperties.getDebit().setUrl(debitUrl);
blinkPayProperties.getClient().setId(clientId);
blinkPayProperties.getClient().setSecret(clientSecret);
blinkPayProperties.getMax().setConnections(maxConnections);
blinkPayProperties.getMax().getIdle().setTime(maxIdleTime);
blinkPayProperties.getMax().getLife().setTime(maxLifeTime);
blinkPayProperties.getPending().getAcquire().setTimeout(pendingAcquireTimeout);
blinkPayProperties.getEviction().setInterval(evictionInterval);
blinkPayProperties.getRetry().setEnabled(retryEnabled);
ConnectionProvider provider = ConnectionProvider.builder("blinkpay-conn-provider")
.maxConnections(maxConnections)
.maxIdleTime(maxIdleTime)
.maxLifeTime(maxLifeTime)
.pendingAcquireTimeout(pendingAcquireTimeout)
.evictInBackground(evictionInterval)
.build();
ReactorClientHttpConnector reactorClientHttpConnector = configureReactorClientHttpConnector(activeProfile,
provider);
validationService = new JakartaValidationServiceImpl(Validation.buildDefaultValidatorFactory().getValidator());
retry = configureRetry(retryEnabled);
OAuthApiClient oauthApiClient = new OAuthApiClient(reactorClientHttpConnector, blinkPayProperties, retry);
AccessTokenHandler accessTokenHandler = new AccessTokenHandler(oauthApiClient);
singleConsentsApiClient = new SingleConsentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
enduringConsentsApiClient = new EnduringConsentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
quickPaymentsApiClient = new QuickPaymentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
paymentsApiClient = new PaymentsApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler,
validationService, retry);
refundsApiClient = new RefundsApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler,
validationService, retry);
metaApiClient = new MetaApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler);
}
/**
* Constructor for pure Java application. Retry is enabled by default.
*
* @param debitUrl the Blink Debit URL
* @param clientId the client ID
* @param clientSecret the client secret
* @param activeProfile the active profile
*/
public BlinkDebitClient(final String debitUrl, final String clientId, final String clientSecret,
final String activeProfile) {
this(debitUrl, clientId, clientSecret, activeProfile, true);
}
/**
* Constructor for pure Java application.
*
* @param debitUrl the Blink Debit URL
* @param clientId the client ID
* @param clientSecret the client secret
* @param activeProfile the active profile
* @param retryEnabled {@code true} if retry is enabled; {@code false otherwise}
*/
public BlinkDebitClient(final String debitUrl, final String clientId, final String clientSecret,
final String activeProfile, final boolean retryEnabled) {
int maxConnections = 10;
Duration maxIdleTime = Duration.parse("PT20S");
Duration maxLifeTime = Duration.parse("PT60S");
Duration pendingAcquireTimeout = Duration.parse("PT10S");
Duration evictionInterval = Duration.parse("PT60S");
BlinkPayProperties blinkPayProperties = new BlinkPayProperties();
blinkPayProperties.getDebit().setUrl(debitUrl);
blinkPayProperties.getClient().setId(clientId);
blinkPayProperties.getClient().setSecret(clientSecret);
blinkPayProperties.getMax().setConnections(maxConnections);
blinkPayProperties.getMax().getIdle().setTime(maxIdleTime);
blinkPayProperties.getMax().getLife().setTime(maxLifeTime);
blinkPayProperties.getPending().getAcquire().setTimeout(pendingAcquireTimeout);
blinkPayProperties.getEviction().setInterval(evictionInterval);
blinkPayProperties.getRetry().setEnabled(retryEnabled);
ConnectionProvider provider = ConnectionProvider.builder("blinkpay-conn-provider")
.maxConnections(maxConnections)
.maxIdleTime(maxIdleTime)
.maxLifeTime(maxLifeTime)
.pendingAcquireTimeout(pendingAcquireTimeout)
.evictInBackground(evictionInterval)
.build();
ReactorClientHttpConnector reactorClientHttpConnector = configureReactorClientHttpConnector(activeProfile,
provider);
validationService = new JakartaValidationServiceImpl(Validation.buildDefaultValidatorFactory().getValidator());
retry = configureRetry(retryEnabled);
OAuthApiClient oauthApiClient = new OAuthApiClient(reactorClientHttpConnector, blinkPayProperties, retry);
AccessTokenHandler accessTokenHandler = new AccessTokenHandler(oauthApiClient);
singleConsentsApiClient = new SingleConsentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
enduringConsentsApiClient = new EnduringConsentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
quickPaymentsApiClient = new QuickPaymentsApiClient(reactorClientHttpConnector, blinkPayProperties,
accessTokenHandler, validationService, retry);
paymentsApiClient = new PaymentsApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler,
validationService, retry);
refundsApiClient = new RefundsApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler,
validationService, retry);
metaApiClient = new MetaApiClient(reactorClientHttpConnector, blinkPayProperties, accessTokenHandler);
}
private static ReactorClientHttpConnector configureReactorClientHttpConnector(String activeProfile,
ConnectionProvider provider) {
HttpClient client;
boolean debugMode = Pattern.compile("local|dev|test").matcher(activeProfile).matches();
if (debugMode) {
client = HttpClient.create(provider)
.wiretap("reactor.netty.http.client.HttpClient", LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL);
} else {
client = HttpClient.create(provider);
}
client.warmup().subscribe();
return new ReactorClientHttpConnector(client);
}
private static Retry configureRetry(boolean retryEnabled) {
Retry retry;
if (Boolean.FALSE.equals(retryEnabled)) {
retry = null;
} else {
RetryConfig retryConfig = RetryConfig.custom()
// allow up to 2 retries after the original request (3 attempts in total)
.maxAttempts(3)
// wait 2 seconds and then 5 seconds (or thereabouts)
.intervalFunction(IntervalFunction
.ofExponentialRandomBackoff(Duration.ofSeconds(2), 2, Duration.ofSeconds(3)))
// retries are triggered for 408 (request timeout) and 5xx exceptions
// and for network errors thrown by WebFlux if the request didn't get to the server at all
.retryExceptions(BlinkRetryableException.class,
ConnectException.class,
WebClientRequestException.class)
// ignore 4xx and 501 (not implemented) exceptions
.ignoreExceptions(BlinkServiceException.class)
.failAfterMaxAttempts(true)
.build();
retry = Retry.of("retry", retryConfig);
}
return retry;
}
/**
* Returns the {@link List} of {@link BankMetadata}.
*
* @return the {@link List} of {@link BankMetadata}
* @throws BlinkServiceException thrown when an exception occurs
*/
public List getMeta() throws BlinkServiceException {
try {
return getMetaAsFlux().collectList().block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Returns the {@link List} of {@link BankMetadata}.
*
* @param requestId the optional correlation ID
* @return the {@link List} of {@link BankMetadata}
* @throws BlinkServiceException thrown when an exception occurs
*/
public List getMeta(final String requestId) throws BlinkServiceException {
try {
return getMetaAsFlux(requestId).collectList().block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Returns the {@link BankMetadata} {@link Flux}.
*
* @return the {@link BankMetadata} {@link Flux}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Flux getMetaAsFlux() throws BlinkServiceException {
return metaApiClient.getMeta();
}
/**
* Returns the {@link BankMetadata} {@link Flux}.
*
* @param requestId the optional correlation ID
* @return the {@link BankMetadata} {@link Flux}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Flux getMetaAsFlux(final String requestId) throws BlinkServiceException {
return metaApiClient.getMeta(requestId);
}
/**
* Creates a single consent.
*
* @param request the {@link SingleConsentRequest}
* @return the {@link CreateConsentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateConsentResponse createSingleConsent(SingleConsentRequest request)
throws BlinkServiceException {
try {
return createSingleConsentAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a single consent.
*
* @param request the {@link SingleConsentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateConsentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateConsentResponse createSingleConsent(SingleConsentRequest request, final String requestId)
throws BlinkServiceException {
try {
return createSingleConsentAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a single consent.
*
* @param request the {@link SingleConsentRequest}
* @return the {@link CreateConsentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createSingleConsentAsMono(SingleConsentRequest request)
throws BlinkServiceException {
return singleConsentsApiClient.createSingleConsent(request);
}
/**
* Creates a single consent with redirect flow.
*
* @param request the {@link SingleConsentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateConsentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createSingleConsentAsMono(SingleConsentRequest request,
final String requestId)
throws BlinkServiceException {
return singleConsentsApiClient.createSingleConsent(request, requestId);
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent getSingleConsent(UUID consentId) throws BlinkServiceException {
try {
return getSingleConsentAsMono(consentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent getSingleConsent(UUID consentId, final String requestId) throws BlinkServiceException {
try {
return getSingleConsentAsMono(consentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @return the {@link Consent} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getSingleConsentAsMono(UUID consentId) throws BlinkServiceException {
return singleConsentsApiClient.getSingleConsent(consentId);
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @return the {@link Consent} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getSingleConsentAsMono(UUID consentId, final String requestId)
throws BlinkServiceException {
return singleConsentsApiClient.getSingleConsent(consentId, requestId);
}
/**
* Retrieves an authorised single consent by ID within the specified time.
* Timeout and other exceptions are wrapped in a {@link RuntimeException}.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent awaitAuthorisedSingleConsent(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono consentMono = getSingleConsentAsMono(consentId);
try {
return consentMono
.flatMap(consent -> {
Consent.StatusEnum status = consent.getStatus();
log.debug("The last status polled was: {} \tfor Single Consent ID: {}", status,
consentId);
if (AUTHORISED == status) {
return consentMono;
}
return Mono.error(new BlinkRetryableException());
}).retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
})
).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an authorised single consent by ID within the specified time.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Consent}
* @throws BlinkServiceException thrown when a Blink Debit service exception occurs
*/
public Consent awaitAuthorisedSingleConsentOrThrowException(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
try {
return awaitAuthorisedSingleConsentAsMono(consentId, maxWaitSeconds).block();
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof BlinkServiceException) {
throw (BlinkServiceException) cause;
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an authorised single consent by ID within the specified time.
* The consent statuses are handled accordingly.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Mono} containing the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono awaitAuthorisedSingleConsentAsMono(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono consentMono = getSingleConsentAsMono(consentId);
return consentMono
.flatMap(consent -> {
Consent.StatusEnum status = consent.getStatus();
log.debug("The last status polled was: {} \tfor Single Consent ID: {}", status,
consentId);
switch (status) {
case AUTHORISED:
case CONSUMED:
break;
case REJECTED:
case REVOKED:
BlinkConsentRejectedException exception1 =
new BlinkConsentRejectedException("Single consent [" + consentId
+ "] has been rejected or revoked");
return Mono.error(exception1);
case GATEWAYTIMEOUT:
BlinkConsentTimeoutException exception2 =
new BlinkConsentTimeoutException("Gateway timed out for single consent ["
+ consentId + "]");
return Mono.error(exception2);
case GATEWAYAWAITINGSUBMISSION:
case AWAITINGAUTHORISATION:
BlinkRetryableException exception3 = new BlinkRetryableException("Single consent ["
+ consentId + "] is waiting for authorisation");
return Mono.error(exception3);
}
log.debug("Single consent completed for ID: {}", consentId);
return consentMono;
})
.retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
}));
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeSingleConsent(UUID consentId) throws BlinkServiceException {
try {
revokeSingleConsentAsMono(consentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeSingleConsent(UUID consentId, final String requestId) throws BlinkServiceException {
try {
revokeSingleConsentAsMono(consentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeSingleConsentAsMono(UUID consentId) throws BlinkServiceException {
return singleConsentsApiClient.revokeSingleConsent(consentId);
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeSingleConsentAsMono(UUID consentId, final String requestId)
throws BlinkServiceException {
return singleConsentsApiClient.revokeSingleConsent(consentId, requestId);
}
/**
* Creates an enduring consent.
*
* @param request the {@link EnduringConsentRequest}
* @return the {@link CreateConsentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateConsentResponse createEnduringConsent(EnduringConsentRequest request)
throws BlinkServiceException {
try {
return createEnduringConsentAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates an enduring consent.
*
* @param request the {@link EnduringConsentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateConsentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateConsentResponse createEnduringConsent(EnduringConsentRequest request, final String requestId)
throws BlinkServiceException {
try {
return createEnduringConsentAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates an enduring consent.
*
* @param request the {@link EnduringConsentRequest}
* @return the {@link CreateConsentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createEnduringConsentAsMono(EnduringConsentRequest request)
throws BlinkServiceException {
return enduringConsentsApiClient.createEnduringConsent(request);
}
/**
* Creates an enduring consent.
*
* @param request the {@link EnduringConsentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateConsentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createEnduringConsentAsMono(EnduringConsentRequest request,
final String requestId)
throws BlinkServiceException {
return enduringConsentsApiClient.createEnduringConsent(request, requestId);
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent getEnduringConsent(UUID consentId) throws BlinkServiceException {
try {
return getEnduringConsentAsMono(consentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent getEnduringConsent(UUID consentId, final String requestId) throws BlinkServiceException {
try {
return getEnduringConsentAsMono(consentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @return the {@link Consent} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getEnduringConsentAsMono(UUID consentId) throws BlinkServiceException {
return enduringConsentsApiClient.getEnduringConsent(consentId);
}
/**
* Retrieves an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @return the {@link Consent} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getEnduringConsentAsMono(UUID consentId, final String requestId)
throws BlinkServiceException {
return enduringConsentsApiClient.getEnduringConsent(consentId, requestId);
}
/**
* Retrieves an authorised enduring consent by ID within the specified time.
* Timeout and other exceptions are wrapped in a {@link RuntimeException}.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Consent awaitAuthorisedEnduringConsent(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono consentMono = getEnduringConsentAsMono(consentId);
try {
return consentMono
.flatMap(consent -> {
Consent.StatusEnum status = consent.getStatus();
log.debug("The last status polled was: {} \tfor Enduring Consent ID: {}", status,
consentId);
if (AUTHORISED == status) {
return consentMono;
}
return Mono.error(new BlinkRetryableException());
}).retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
try {
revokeEnduringConsentAsMono(consentId).then();
log.info("The max wait time was reached while waiting for the enduring consent to complete and the payment has been revoked with the server. Enduring consent ID: {}", consentId);
} catch (Throwable revokeException) {
log.error("Waiting for the enduring consent was not successful and it was also not able to be revoked with the server due to: {}. Enduring consent ID: {}", revokeException.getLocalizedMessage(), consentId);
awaitException.addSuppressed(revokeException);
}
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
})
).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an authorised enduring consent by ID within the specified time.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Consent}
* @throws BlinkServiceException thrown when a Blink Debit service exception occurs
*/
public Consent awaitAuthorisedEnduringConsentOrThrowException(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
try {
return awaitAuthorisedEnduringConsentAsMono(consentId, maxWaitSeconds).block();
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof BlinkServiceException) {
throw (BlinkServiceException) cause;
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an authorised enduring consent by ID within the specified time.
* The consent statuses are handled accordingly.
*
* @param consentId the consent ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Mono} containing the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono awaitAuthorisedEnduringConsentAsMono(UUID consentId, final int maxWaitSeconds)
throws BlinkServiceException {
try {
Mono consentMono = getEnduringConsentAsMono(consentId);
return consentMono
.flatMap(consent -> {
Consent.StatusEnum status = consent.getStatus();
log.debug("The last status polled was: {} \tfor Enduring Consent ID: {}", status,
consentId);
switch (status) {
case AUTHORISED:
case CONSUMED:
break;
case REJECTED:
case REVOKED:
BlinkConsentRejectedException exception1 =
new BlinkConsentRejectedException("Enduring consent [" + consentId
+ "] has been rejected or revoked");
return Mono.error(exception1);
case GATEWAYTIMEOUT:
BlinkConsentTimeoutException exception2 =
new BlinkConsentTimeoutException("Gateway timed out for enduring consent ["
+ consentId + "]");
return Mono.error(exception2);
case GATEWAYAWAITINGSUBMISSION:
case AWAITINGAUTHORISATION:
BlinkRetryableException exception3 = new BlinkRetryableException("Enduring consent ["
+ consentId + "] is waiting for authorisation");
return Mono.error(exception3);
}
log.debug("Enduring consent completed for ID: {}", consentId);
return consentMono;
})
.retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
try {
revokeEnduringConsentAsMono(consentId).then();
log.info("The max wait time was reached while waiting for the enduring consent to complete and the payment has been revoked with the server. Enduring consent ID: {}", consentId);
} catch (Throwable revokeException) {
log.error("Waiting for the enduring consent was not successful and it was also not able to be revoked with the server due to: {}. Enduring consent ID: {}", revokeException.getLocalizedMessage(), consentId);
awaitException.addSuppressed(revokeException);
}
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
}));
} catch (Throwable awaitException) {
try {
revokeEnduringConsent(consentId);
log.info("The max wait time was reached while waiting for the enduring consent to complete and the payment has been revoked with the server. Enduring consent ID: {}", consentId);
} catch (Throwable revokeException) {
log.error("Waiting for the enduring consent was not successful and it was also not able to be revoked with the server due to: {}. Enduring consent ID: {}", revokeException.getLocalizedMessage(), consentId);
awaitException.addSuppressed(revokeException);
}
throw awaitException;
}
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeEnduringConsent(UUID consentId) throws BlinkServiceException {
try {
revokeEnduringConsentAsMono(consentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeEnduringConsent(UUID consentId, final String requestId) throws BlinkServiceException {
try {
revokeEnduringConsentAsMono(consentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeEnduringConsentAsMono(UUID consentId) throws BlinkServiceException {
return enduringConsentsApiClient.revokeEnduringConsent(consentId);
}
/**
* Revokes an existing consent by ID.
*
* @param consentId the consent ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeEnduringConsentAsMono(UUID consentId, final String requestId)
throws BlinkServiceException {
return enduringConsentsApiClient.revokeEnduringConsent(consentId, requestId);
}
/**
* Creates a quick payment.
*
* @param request the {@link QuickPaymentRequest}
* @return the {@link CreateQuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateQuickPaymentResponse createQuickPayment(QuickPaymentRequest request)
throws BlinkServiceException {
try {
return createQuickPaymentAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a quick payment.
*
* @param request the {@link QuickPaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateQuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public CreateQuickPaymentResponse createQuickPayment(QuickPaymentRequest request, final String requestId)
throws BlinkServiceException {
try {
return createQuickPaymentAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a quick payment.
*
* @param request the {@link QuickPaymentRequest}
* @return the {@link CreateQuickPaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createQuickPaymentAsMono(QuickPaymentRequest request)
throws BlinkServiceException {
return quickPaymentsApiClient.createQuickPayment(request);
}
/**
* Creates a quick payment.
*
* @param request the {@link QuickPaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link CreateQuickPaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createQuickPaymentAsMono(QuickPaymentRequest request,
final String requestId)
throws BlinkServiceException {
return quickPaymentsApiClient.createQuickPayment(request, requestId);
}
/**
* Retrieves an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @return the {@link QuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public QuickPaymentResponse getQuickPayment(UUID quickPaymentId) throws BlinkServiceException {
try {
return getQuickPaymentAsMono(quickPaymentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @param requestId the optional correlation ID
* @return the {@link QuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public QuickPaymentResponse getQuickPayment(UUID quickPaymentId, final String requestId)
throws BlinkServiceException {
try {
return getQuickPaymentAsMono(quickPaymentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @return the {@link QuickPaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getQuickPaymentAsMono(UUID quickPaymentId) throws BlinkServiceException {
return quickPaymentsApiClient.getQuickPayment(quickPaymentId);
}
/**
* Retrieves an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @param requestId the optional correlation ID
* @return the {@link QuickPaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getQuickPaymentAsMono(UUID quickPaymentId, final String requestId)
throws BlinkServiceException {
return quickPaymentsApiClient.getQuickPayment(quickPaymentId, requestId);
}
/**
* Retrieves a successful quick payment by ID within the specified time.
* Timeout and other exceptions are wrapped in a {@link RuntimeException}.
*
* @param quickPaymentId the quick payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link QuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public QuickPaymentResponse awaitSuccessfulQuickPayment(UUID quickPaymentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono quickPaymentResponseMono = getQuickPaymentAsMono(quickPaymentId);
try {
return quickPaymentResponseMono
.flatMap(quickPaymentResponse -> {
Consent.StatusEnum status = quickPaymentResponse.getConsent().getStatus();
log.debug("The last status polled was: {} \tfor Quick Payment ID: {}", status, quickPaymentId);
if (AUTHORISED == status) {
return quickPaymentResponseMono;
}
return Mono.error(new BlinkRetryableException());
}).retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
// Gateway timed out. Revoke so it can't be used anymore
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
try {
revokeQuickPaymentAsMono(quickPaymentId).then();
log.info("The max wait time was reached while waiting for the quick payment to complete and the payment has been revoked with the server. Quick payment ID: {}", quickPaymentId);
} catch (Throwable revokeException) {
log.error("Waiting for the quick payment was not successful and it was also not able to be revoked with the server due to: {}. Quick payment ID: {}", revokeException.getLocalizedMessage(), quickPaymentId);
awaitException.addSuppressed(revokeException);
}
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
})
).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves a successful quick payment by ID within the specified time.
*
* @param quickPaymentId the quick payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link QuickPaymentResponse}
* @throws BlinkServiceException thrown when a Blink Debit service exception occurs
*/
public QuickPaymentResponse awaitSuccessfulQuickPaymentOrThrowException(UUID quickPaymentId, final int maxWaitSeconds)
throws BlinkServiceException {
try {
return awaitSuccessfulQuickPaymentAsMono(quickPaymentId, maxWaitSeconds).block();
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof BlinkServiceException) {
throw (BlinkServiceException) cause;
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves a successful quick payment by ID within the specified time.
* The consent statuses are handled accordingly.
*
* @param quickPaymentId the quick payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Mono} containing the {@link QuickPaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono awaitSuccessfulQuickPaymentAsMono(UUID quickPaymentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono quickPaymentResponseMono = getQuickPaymentAsMono(quickPaymentId);
return quickPaymentResponseMono
.flatMap(quickPaymentResponse -> {
Consent.StatusEnum status = quickPaymentResponse.getConsent().getStatus();
log.debug("The last status polled was: {} \tfor Quick Payment ID: {}", status, quickPaymentId);
switch (status) {
case AUTHORISED:
case CONSUMED:
break;
case REJECTED:
case REVOKED:
BlinkConsentRejectedException exception1 =
new BlinkConsentRejectedException("Quick payment [" + quickPaymentId
+ "] has been rejected or revoked");
return Mono.error(exception1);
case GATEWAYTIMEOUT:
BlinkConsentTimeoutException exception2 =
new BlinkConsentTimeoutException("Gateway timed out for quick payment ["
+ quickPaymentId + "]");
return Mono.error(exception2);
case GATEWAYAWAITINGSUBMISSION:
case AWAITINGAUTHORISATION:
BlinkRetryableException exception3 = new BlinkRetryableException("Quick payment ["
+ quickPaymentId + "] is waiting for authorisation");
return Mono.error(exception3);
}
// a successful quick payment will always have the consent status of consumed
log.debug("Quick Payment completed for ID: {}", quickPaymentId);
return quickPaymentResponseMono;
})
.retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
// Gateway timed out. Revoke so it can't be used anymore
BlinkConsentTimeoutException awaitException = new BlinkConsentTimeoutException();
try {
revokeQuickPaymentAsMono(quickPaymentId).then();
log.info("The max wait time was reached while waiting for the quick payment to complete and the payment has been revoked with the server. Quick payment ID: {}", quickPaymentId);
} catch (Throwable revokeException) {
log.error("Waiting for the quick payment was not successful and it was also not able to be revoked with the server due to: {}. Quick payment ID: {}", revokeException.getLocalizedMessage(), quickPaymentId);
awaitException.addSuppressed(revokeException);
}
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
}));
}
/**
* Revokes an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeQuickPayment(UUID quickPaymentId) throws BlinkServiceException {
try {
revokeQuickPaymentAsMono(quickPaymentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing quick payment by ID.
*
* @param consentId the quick payment ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public void revokeQuickPayment(UUID consentId, final String requestId) throws BlinkServiceException {
try {
revokeQuickPaymentAsMono(consentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Revokes an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeQuickPaymentAsMono(UUID quickPaymentId) throws BlinkServiceException {
return quickPaymentsApiClient.revokeQuickPayment(quickPaymentId);
}
/**
* Revokes an existing quick payment by ID.
*
* @param quickPaymentId the quick payment ID
* @param requestId the optional correlation ID
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono revokeQuickPaymentAsMono(UUID quickPaymentId, final String requestId)
throws BlinkServiceException {
return quickPaymentsApiClient.revokeQuickPayment(quickPaymentId, requestId);
}
/**
* Creates a payment.
*
* @param request the {@link PaymentRequest}
* @return the {@link PaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public PaymentResponse createPayment(PaymentRequest request) throws BlinkServiceException {
try {
return createPaymentAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a payment.
*
* @param request the {@link PaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link PaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public PaymentResponse createPayment(PaymentRequest request, final String requestId)
throws BlinkServiceException {
try {
return createPaymentAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a payment.
*
* @param request the {@link PaymentRequest}
* @return the {@link PaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createPaymentAsMono(PaymentRequest request) throws BlinkServiceException {
return paymentsApiClient.createPayment(request);
}
/**
* Creates a payment.
*
* @param request the {@link PaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link PaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createPaymentAsMono(PaymentRequest request, final String requestId)
throws BlinkServiceException {
return paymentsApiClient.createPayment(request, requestId);
}
/**
* Creates a Westpac payment. Once Westpac enables their Open Banking API, this can be replaced with
* {@link #createPayment(PaymentRequest)}.
*
* @param request the {@link PaymentRequest}
* @return the {@link PaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public PaymentResponse createWestpacPayment(PaymentRequest request) throws BlinkServiceException {
try {
return createWestpacPaymentAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a Westpac payment. Once Westpac enables their Open Banking API, this can be replaced with
* {@link #createPayment(PaymentRequest, String)}.
*
* @param request the {@link PaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link PaymentResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public PaymentResponse createWestpacPayment(PaymentRequest request, final String requestId)
throws BlinkServiceException {
try {
return createWestpacPaymentAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a Westpac payment. Once Westpac enables their Open Banking API, this can be replaced with
* {@link #createPayment(PaymentRequest)}.
*
* @param request the {@link PaymentRequest}
* @return the {@link PaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createWestpacPaymentAsMono(PaymentRequest request)
throws BlinkServiceException {
return paymentsApiClient.createWestpacPayment(request);
}
/**
* Creates a Westpac payment. Once Westpac enables their Open Banking API, this can be replaced with
* {@link #createPayment(PaymentRequest, String)}.
*
* @param request the {@link PaymentRequest}
* @param requestId the optional correlation ID
* @return the {@link PaymentResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createWestpacPaymentAsMono(PaymentRequest request, final String requestId)
throws BlinkServiceException {
return paymentsApiClient.createWestpacPayment(request, requestId);
}
/**
* Retrieves an existing payment by ID.
*
* @param paymentId the payment ID
* @return the {@link Payment}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Payment getPayment(UUID paymentId) throws BlinkServiceException {
try {
return getPaymentAsMono(paymentId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing payment by ID.
*
* @param paymentId the payment ID
* @param requestId the optional correlation ID
* @return the {@link Payment}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Payment getPayment(UUID paymentId, final String requestId) throws BlinkServiceException {
try {
return getPaymentAsMono(paymentId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing payment by ID.
*
* @param paymentId the payment ID
* @return the {@link Payment} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getPaymentAsMono(UUID paymentId) throws BlinkServiceException {
return paymentsApiClient.getPayment(paymentId);
}
/**
* Retrieves an existing payment by ID.
*
* @param paymentId the payment ID
* @param requestId the optional correlation ID
* @return the {@link Payment} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getPaymentAsMono(UUID paymentId, final String requestId) throws BlinkServiceException {
return paymentsApiClient.getPayment(paymentId, requestId);
}
/**
* Retrieves a successful payment by ID within the specified time.
* Timeout and other exceptions are wrapped in a {@link RuntimeException}.
*
* @param paymentId the payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Payment}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Payment awaitSuccessfulPayment(UUID paymentId, final int maxWaitSeconds) throws BlinkServiceException {
Mono paymentMono = getPaymentAsMono(paymentId);
try {
return paymentMono
.flatMap(payment -> {
Payment.StatusEnum status = payment.getStatus();
log.debug("The last status polled was: {} \tfor Payment ID: {}", status, paymentId);
if (ACCEPTEDSETTLEMENTCOMPLETED == status) {
return paymentMono;
}
return Mono.error(new BlinkRetryableException());
}).retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkPaymentTimeoutException awaitException = new BlinkPaymentTimeoutException();
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
})
).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves a successful payment by ID within the specified time.
*
* @param paymentId the payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Payment}
* @throws BlinkServiceException thrown when a Blink Debit service exception occurs
*/
public Payment awaitSuccessfulPaymentOrThrowException(UUID paymentId, final int maxWaitSeconds)
throws BlinkServiceException {
try {
return awaitSuccessfulPaymentAsMono(paymentId, maxWaitSeconds).block();
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof BlinkServiceException) {
throw (BlinkServiceException) cause;
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves a successful payment by ID within the specified time.
* The payment statuses are handled accordingly.
*
* @param paymentId the payment ID
* @param maxWaitSeconds the number of seconds to wait
* @return the {@link Mono} containing the {@link Consent}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono awaitSuccessfulPaymentAsMono(UUID paymentId, final int maxWaitSeconds)
throws BlinkServiceException {
Mono paymentMono = getPaymentAsMono(paymentId);
return paymentMono
.flatMap(payment -> {
Payment.StatusEnum status = payment.getStatus();
log.debug("The last status polled was: {} \tfor Payment ID: {}", status, paymentId);
switch (status) {
case ACCEPTEDSETTLEMENTCOMPLETED:
break;
case REJECTED:
BlinkPaymentRejectedException exception1 =
new BlinkPaymentRejectedException("Payment [" + paymentId
+ "] has been rejected");
return Mono.error(exception1);
case ACCEPTEDSETTLEMENTINPROCESS:
case PENDING:
BlinkRetryableException exception3 = new BlinkRetryableException("Payment [" + paymentId
+ "] is pending or being processed");
return Mono.error(exception3);
}
log.debug("Payment completed for ID: {}", paymentId);
return paymentMono;
})
.retryWhen(reactor.util.retry.Retry
.fixedDelay(maxWaitSeconds, Duration.ofSeconds(1))
.filter(BlinkRetryableException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
BlinkPaymentTimeoutException awaitException = new BlinkPaymentTimeoutException();
throw Exceptions.retryExhausted(awaitException.getMessage(), awaitException);
}));
}
/**
* Creates a refund.
*
* @param request the {@link RefundDetail}
* @return the {@link RefundResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public RefundResponse createRefund(RefundDetail request) throws BlinkServiceException {
try {
return createRefundAsMono(request).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a refund.
*
* @param request the {@link RefundDetail}
* @param requestId the optional correlation ID
* @return the {@link RefundResponse}
* @throws BlinkServiceException thrown when an exception occurs
*/
public RefundResponse createRefund(RefundDetail request, final String requestId)
throws BlinkServiceException {
try {
return createRefundAsMono(request, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Creates a refund.
*
* @param request the {@link RefundDetail}
* @return the {@link RefundResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createRefundAsMono(RefundDetail request) throws BlinkServiceException {
return refundsApiClient.createRefund(request);
}
/**
* Creates a refund.
*
* @param request the {@link RefundDetail}
* @param requestId the optional correlation ID
* @return the {@link RefundResponse} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono createRefundAsMono(RefundDetail request, final String requestId)
throws BlinkServiceException {
return refundsApiClient.createRefund(request, requestId);
}
/**
* Retrieves an existing refund by ID.
*
* @param refundId the refund ID
* @return the {@link Payment}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Refund getRefund(UUID refundId) throws BlinkServiceException {
try {
return getRefundAsMono(refundId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing refund by ID.
*
* @param refundId the refund ID
* @param requestId the optional correlation ID
* @return the {@link Payment}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Refund getRefund(UUID refundId, final String requestId) throws BlinkServiceException {
try {
return getRefundAsMono(refundId, requestId).block();
} catch (RuntimeException e) {
if (e.getCause() instanceof BlinkServiceException) {
throw (BlinkServiceException) e.getCause();
}
throw new BlinkServiceException(e.getMessage(), e);
}
}
/**
* Retrieves an existing refund by ID.
*
* @param refundId the refund ID
* @return the {@link Payment} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getRefundAsMono(UUID refundId) throws BlinkServiceException {
return refundsApiClient.getRefund(refundId);
}
/**
* Retrieves an existing refund by ID.
*
* @param refundId the refund ID
* @param requestId the optional correlation ID
* @return the {@link Payment} {@link Mono}
* @throws BlinkServiceException thrown when an exception occurs
*/
public Mono getRefundAsMono(UUID refundId, final String requestId) throws BlinkServiceException {
return refundsApiClient.getRefund(refundId, requestId);
}
}