use of org.killbill.billing.control.plugin.api.PaymentControlApiException in project killbill by killbill.
the class InvoicePaymentControlPluginApi method getPluginRefundResult.
private PriorPaymentControlResult getPluginRefundResult(final PaymentControlContext paymentControlPluginContext, final Iterable<PluginProperty> pluginProperties, final InternalCallContext internalContext) throws PaymentControlApiException {
final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
if ((paymentControlPluginContext.getAmount() == null || paymentControlPluginContext.getAmount().compareTo(BigDecimal.ZERO) == 0) && idWithAmount.size() == 0) {
throw new PaymentControlApiException("Abort refund call: ", new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, String.format("Refund for payment, key = %s, aborted: requested refund amount is = %s", paymentControlPluginContext.getPaymentExternalKey(), paymentControlPluginContext.getAmount())));
}
final PaymentModelDao payment = paymentDao.getPayment(paymentControlPluginContext.getPaymentId(), internalContext);
if (payment == null) {
throw new PaymentControlApiException("Unexpected null payment");
}
// This will calculate the upper bound on the refund amount based on the invoice items associated with that payment.
// Note that we are not checking that other (partial) refund occurred, but if the refund ends up being greater than what is allowed
// the call to the gateway would fail; it would need noce to validate on our side though...
final BigDecimal amountToBeRefunded = computeRefundAmount(payment.getId(), paymentControlPluginContext.getAmount(), idWithAmount, internalContext);
final boolean isAborted = amountToBeRefunded.compareTo(BigDecimal.ZERO) == 0;
if (paymentControlPluginContext.isApiPayment() && isAborted) {
throw new PaymentControlApiException("Abort refund call: ", new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, String.format("Refund for payment %s aborted : invoice item sum amount is %s, requested refund amount is = %s", payment.getId(), amountToBeRefunded, paymentControlPluginContext.getAmount())));
}
final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
if (isAdjusted) {
try {
invoiceApi.validateInvoiceItemAdjustments(paymentControlPluginContext.getPaymentId(), idWithAmount, internalContext);
} catch (InvoiceApiException e) {
throw new PaymentControlApiException(String.format("Refund for payment %s aborted", payment.getId()), new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getMessage()));
}
}
return new DefaultPriorPaymentControlResult(isAborted, amountToBeRefunded);
}
use of org.killbill.billing.control.plugin.api.PaymentControlApiException in project killbill by killbill.
the class TestPaymentApi method testCreatePurchaseWithControlPluginException.
@Test(groups = "slow")
public void testCreatePurchaseWithControlPluginException() throws Exception {
mockPaymentControlProviderPlugin.throwsException(new PaymentControlApiException());
final BigDecimal requestedAmount = BigDecimal.TEN;
final String paymentExternalKey = "pay controle external key";
;
final String transactionExternalKey = "txn control external key";
try {
paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey, ImmutableList.<PluginProperty>of(), CONTROL_PLUGIN_OPTIONS, callContext);
fail();
} catch (PaymentApiException e) {
assertTrue(e.getCause() instanceof PaymentControlApiException);
}
}
use of org.killbill.billing.control.plugin.api.PaymentControlApiException in project killbill by killbill.
the class PluginControlPaymentAutomatonRunner method run.
public Payment run(final State state, final boolean isApiPayment, final Boolean isSuccess, final TransactionType transactionType, final ControlOperation controlOperation, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, @Nullable final UUID transactionId, final String paymentTransactionExternalKey, @Nullable final BigDecimal amount, @Nullable final Currency currency, final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final PaymentStateControlContext paymentStateContext = createContext(isApiPayment, isSuccess, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, transactionId, paymentTransactionExternalKey, amount, currency, properties, paymentControlPluginNames, callContext, internalCallContext);
try {
final OperationCallback callback = createOperationCallback(controlOperation, paymentStateContext);
final LeavingStateCallback leavingStateCallback = new DefaultControlInitiated(this, paymentStateContext, paymentDao, paymentControlStateMachineHelper.getInitialState(), paymentControlStateMachineHelper.getRetriedState(), transactionType);
final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, paymentControlStateMachineHelper.getRetriedState(), retryServiceScheduler);
state.runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
} catch (final MissingEntryException e) {
throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
} catch (final OperationException e) {
if (e.getCause() instanceof PaymentApiException) {
throw (PaymentApiException) e.getCause();
// If the control plugin tries to pass us back a PaymentApiException we throw it
} else if (e.getCause() instanceof PaymentControlApiException && e.getCause().getCause() instanceof PaymentApiException) {
throw (PaymentApiException) e.getCause().getCause();
} else if (e.getCause() != null || paymentStateContext.getResult() == null) {
throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
// we don't throw, and return the failed Payment instead to be consistent with what happens when we don't go through control api.
return paymentStateContext.getResult();
}
use of org.killbill.billing.control.plugin.api.PaymentControlApiException in project killbill by killbill.
the class ControlPluginRunner method executePluginOnFailureCalls.
public OnFailurePaymentControlResult executePluginOnFailureCalls(final Account account, final UUID paymentMethodId, final UUID paymentAttemptId, final UUID paymentId, final String paymentExternalKey, final UUID transactionId, final String paymentTransactionExternalKey, final PaymentApiType paymentApiType, final TransactionType transactionType, final HPPType hppType, final BigDecimal amount, final Currency currency, final BigDecimal processedAmount, final Currency processedCurrency, final boolean isApiPayment, final List<String> paymentControlPluginNames, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) {
final PaymentControlContext inputPaymentControlContext = new DefaultPaymentControlContext(account, paymentMethodId, paymentAttemptId, paymentId, paymentExternalKey, transactionId, paymentTransactionExternalKey, paymentApiType, transactionType, hppType, amount, currency, processedAmount, processedCurrency, isApiPayment, callContext);
DateTime candidate = null;
Iterable<PluginProperty> inputPluginProperties = pluginProperties;
for (final String pluginName : paymentControlPluginNames) {
final PaymentControlPluginApi plugin = paymentControlPluginRegistry.getServiceForName(pluginName);
if (plugin != null) {
try {
log.debug("Calling onSuccessCall of plugin {}", pluginName);
final OnFailurePaymentControlResult result = plugin.onFailureCall(inputPaymentControlContext, inputPluginProperties);
log.debug("Successful executed onSuccessCall of plugin {}", pluginName);
if (result == null) {
// Nothing returned by the plugin
continue;
}
if (candidate == null) {
candidate = result.getNextRetryDate();
} else if (result.getNextRetryDate() != null) {
candidate = candidate.compareTo(result.getNextRetryDate()) > 0 ? result.getNextRetryDate() : candidate;
}
if (result.getAdjustedPluginProperties() != null) {
inputPluginProperties = result.getAdjustedPluginProperties();
}
} catch (final PaymentControlApiException e) {
log.warn("Error during onFailureCall for plugin='{}', paymentExternalKey='{}'", pluginName, inputPaymentControlContext.getPaymentExternalKey(), e);
return new DefaultFailureCallResult(candidate, inputPluginProperties);
}
}
}
return new DefaultFailureCallResult(candidate, inputPluginProperties);
}
use of org.killbill.billing.control.plugin.api.PaymentControlApiException in project killbill by killbill.
the class OperationControlCallback method doOperationCallback.
@Override
public OperationResult doOperationCallback() throws OperationException {
final List<String> pluginNameList = paymentStateControlContext.getPaymentControlPluginNames();
final String pluginNames = JOINER.join(pluginNameList);
return dispatchWithAccountLockAndTimeout(pluginNames, new DispatcherCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {
@Override
public PluginDispatcherReturnType<OperationResult> doOperation() throws OperationException {
final PaymentControlContext paymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(), paymentStateContext.getPaymentMethodId(), paymentStateControlContext.getAttemptId(), paymentStateContext.getPaymentId(), paymentStateContext.getPaymentExternalKey(), paymentStateContext.getTransactionId(), paymentStateContext.getPaymentTransactionExternalKey(), PaymentApiType.PAYMENT_TRANSACTION, paymentStateContext.getTransactionType(), null, paymentStateContext.getAmount(), paymentStateContext.getCurrency(), paymentStateControlContext.getProcessedAmount(), paymentStateControlContext.getProcessedCurrency(), paymentStateControlContext.isApiPayment(), paymentStateContext.getCallContext());
try {
executePluginPriorCalls(paymentStateControlContext.getPaymentControlPluginNames(), paymentControlContext);
} catch (final PaymentControlApiAbortException e) {
// Transition to ABORTED
final PaymentApiException paymentAbortedException = new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_API_ABORTED, e.getPluginName());
throw new OperationException(paymentAbortedException, OperationResult.EXCEPTION);
} catch (final PaymentControlApiException e) {
// Transition to ABORTED and throw PaymentControlApiException to caller.
throw new OperationException(e, OperationResult.EXCEPTION);
}
final boolean success;
try {
final Payment result = doCallSpecificOperationCallback();
((PaymentStateControlContext) paymentStateContext).setResult(result);
final PaymentTransaction transaction = ((PaymentStateControlContext) paymentStateContext).getCurrentTransaction();
success = transaction.getTransactionStatus() == TransactionStatus.SUCCESS || transaction.getTransactionStatus() == TransactionStatus.PENDING;
final PaymentControlContext updatedPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(), paymentStateContext.getPaymentMethodId(), paymentStateControlContext.getAttemptId(), result.getId(), result.getExternalKey(), transaction.getId(), paymentStateContext.getPaymentTransactionExternalKey(), PaymentApiType.PAYMENT_TRANSACTION, paymentStateContext.getTransactionType(), null, transaction.getAmount(), transaction.getCurrency(), transaction.getProcessedAmount(), transaction.getProcessedCurrency(), paymentStateControlContext.isApiPayment(), paymentStateContext.getCallContext());
if (success) {
executePluginOnSuccessCalls(paymentStateControlContext.getPaymentControlPluginNames(), updatedPaymentControlContext);
return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
} else {
throw new OperationException(null, executePluginOnFailureCallsAndSetRetryDate(updatedPaymentControlContext));
}
} catch (final PaymentApiException e) {
// Wrap PaymentApiException, and throw a new OperationException with an ABORTED/FAILURE state based on the retry result.
throw new OperationException(e, executePluginOnFailureCallsAndSetRetryDate(paymentControlContext));
} catch (final RuntimeException e) {
// Attempts to set the retry date in context if needed.
throw new OperationException(e, executePluginOnFailureCallsAndSetRetryDate(paymentControlContext));
}
}
});
}
Aggregations