Search in sources :

Example 6 with OperationException

use of org.killbill.automaton.OperationException in project killbill by killbill.

the class CompletionControlOperation method doOperationCallback.

@Override
public OperationResult doOperationCallback() throws OperationException {
    final List<String> controlPluginNameList = paymentStateControlContext.getPaymentControlPluginNames();
    final String controlPluginNames = JOINER.join(controlPluginNameList);
    return dispatchWithAccountLockAndTimeout(controlPluginNames, new DispatcherCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {

        @Override
        public PluginDispatcherReturnType<OperationResult> doOperation() throws OperationException {
            final PaymentTransactionModelDao transaction = paymentStateContext.getPaymentTransactionModelDao();
            final PaymentControlContext updatedPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(), paymentStateContext.getPaymentMethodId(), paymentStateControlContext.getAttemptId(), transaction.getPaymentId(), paymentStateContext.getPaymentExternalKey(), transaction.getId(), paymentStateContext.getPaymentTransactionExternalKey(), PaymentApiType.PAYMENT_TRANSACTION, paymentStateContext.getTransactionType(), null, transaction.getAmount(), transaction.getCurrency(), transaction.getProcessedAmount(), transaction.getProcessedCurrency(), paymentStateControlContext.isApiPayment(), paymentStateContext.getCallContext());
            try {
                final Payment result = doCallSpecificOperationCallback();
                ((PaymentStateControlContext) paymentStateContext).setResult(result);
                final boolean success = transaction.getTransactionStatus() == TransactionStatus.SUCCESS || transaction.getTransactionStatus() == TransactionStatus.PENDING;
                if (success) {
                    executePluginOnSuccessCalls(paymentStateControlContext.getPaymentControlPluginNames(), updatedPaymentControlContext);
                    // Remove scheduled retry, if any
                    paymentProcessor.cancelScheduledPaymentTransaction(paymentStateControlContext.getAttemptId(), paymentStateControlContext.getInternalCallContext());
                    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(updatedPaymentControlContext));
            } catch (final RuntimeException e) {
                // Attempts to set the retry date in context if needed.
                throw new OperationException(e, executePluginOnFailureCallsAndSetRetryDate(updatedPaymentControlContext));
            }
        }
    });
}
Also used : PluginDispatcherReturnType(org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType) Payment(org.killbill.billing.payment.api.Payment) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) DefaultPaymentControlContext(org.killbill.billing.payment.core.sm.control.ControlPluginRunner.DefaultPaymentControlContext) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) PaymentControlContext(org.killbill.billing.control.plugin.api.PaymentControlContext) DefaultPaymentControlContext(org.killbill.billing.payment.core.sm.control.ControlPluginRunner.DefaultPaymentControlContext) OperationException(org.killbill.automaton.OperationException)

Example 7 with OperationException

use of org.killbill.automaton.OperationException in project killbill by killbill.

the class DefaultControlInitiated method leavingState.

@Override
public void leavingState(final State state) throws OperationException {
    final DateTime utcNow = pluginControlPaymentAutomatonRunner.getClock().getUTCNow();
    // Retrieve the associated payment transaction, if any
    PaymentTransactionModelDao paymentTransactionModelDaoCandidate = null;
    if (stateContext.getTransactionId() != null) {
        paymentTransactionModelDaoCandidate = paymentDao.getPaymentTransaction(stateContext.getTransactionId(), stateContext.getInternalCallContext());
        Preconditions.checkNotNull(paymentTransactionModelDaoCandidate, "paymentTransaction cannot be null for id " + stateContext.getTransactionId());
    } else if (stateContext.getPaymentTransactionExternalKey() != null) {
        final List<PaymentTransactionModelDao> paymentTransactionModelDaos = paymentDao.getPaymentTransactionsByExternalKey(stateContext.getPaymentTransactionExternalKey(), stateContext.getInternalCallContext());
        if (!paymentTransactionModelDaos.isEmpty()) {
            paymentTransactionModelDaoCandidate = paymentTransactionModelDaos.get(paymentTransactionModelDaos.size() - 1);
        }
    }
    final PaymentTransactionModelDao paymentTransactionModelDao = paymentTransactionModelDaoCandidate != null && TRANSIENT_TRANSACTION_STATUSES.contains(paymentTransactionModelDaoCandidate.getTransactionStatus()) ? paymentTransactionModelDaoCandidate : null;
    if (stateContext.getPaymentId() != null && stateContext.getPaymentExternalKey() == null) {
        final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext());
        Preconditions.checkNotNull(payment, "payment cannot be null for id " + stateContext.getPaymentId());
        stateContext.setPaymentExternalKey(payment.getExternalKey());
        stateContext.setPaymentMethodId(payment.getPaymentMethodId());
    } else if (stateContext.getPaymentExternalKey() == null) {
        final UUID paymentIdForNewPayment = UUIDs.randomUUID();
        stateContext.setPaymentIdForNewPayment(paymentIdForNewPayment);
        stateContext.setPaymentExternalKey(paymentIdForNewPayment.toString());
    }
    if (paymentTransactionModelDao != null) {
        stateContext.setPaymentTransactionModelDao(paymentTransactionModelDao);
        stateContext.setProcessedAmount(paymentTransactionModelDao.getProcessedAmount());
        stateContext.setProcessedCurrency(paymentTransactionModelDao.getProcessedCurrency());
    } else if (stateContext.getPaymentTransactionExternalKey() == null) {
        final UUID paymentTransactionIdForNewPaymentTransaction = UUIDs.randomUUID();
        stateContext.setPaymentTransactionIdForNewPaymentTransaction(paymentTransactionIdForNewPaymentTransaction);
        stateContext.setPaymentTransactionExternalKey(paymentTransactionIdForNewPaymentTransaction.toString());
    }
    if (stateContext.getPaymentMethodId() == null) {
        // Similar logic in PaymentAutomatonRunner
        stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId());
    }
    if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) {
        try {
            final PaymentAttemptModelDao attempt;
            if (paymentTransactionModelDao != null && paymentTransactionModelDao.getAttemptId() != null) {
                attempt = pluginControlPaymentAutomatonRunner.getPaymentDao().getPaymentAttempt(paymentTransactionModelDao.getAttemptId(), stateContext.getInternalCallContext());
                Preconditions.checkNotNull(attempt, "attempt cannot be null for id " + paymentTransactionModelDao.getAttemptId());
            } else {
                //
                // We don't serialize any properties at this stage to avoid serializing sensitive information.
                // However, if after going through the control plugins, the attempt end up in RETRIED state,
                // the properties will be serialized in the enteringState callback (any plugin that sets a
                // retried date is responsible to correctly remove sensitive information such as CVV, ...)
                //
                final byte[] serializedProperties = PluginPropertySerializer.serialize(ImmutableList.<PluginProperty>of());
                attempt = new PaymentAttemptModelDao(stateContext.getAccount().getId(), stateContext.getPaymentMethodId(), utcNow, utcNow, stateContext.getPaymentExternalKey(), stateContext.getTransactionId(), stateContext.getPaymentTransactionExternalKey(), transactionType, initialState.getName(), stateContext.getAmount(), stateContext.getCurrency(), stateContext.getPaymentControlPluginNames(), serializedProperties);
                pluginControlPaymentAutomatonRunner.getPaymentDao().insertPaymentAttemptWithProperties(attempt, stateContext.getInternalCallContext());
            }
            stateContext.setAttemptId(attempt.getId());
        } catch (final PluginPropertySerializerException e) {
            throw new OperationException(e);
        }
    }
}
Also used : PaymentAttemptModelDao(org.killbill.billing.payment.dao.PaymentAttemptModelDao) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) PluginPropertySerializerException(org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) UUID(java.util.UUID) DateTime(org.joda.time.DateTime) OperationException(org.killbill.automaton.OperationException)

Example 8 with OperationException

use of org.killbill.automaton.OperationException in project killbill by killbill.

the class PaymentAutomatonRunner method runStateMachineOperation.

private void runStateMachineOperation(final String initialStateName, final TransactionType transactionType, final LeavingStateCallback leavingStateCallback, final OperationCallback operationCallback, final EnteringStateCallback enteringStateCallback, final Boolean includeDeletedPaymentMethod, final PaymentStateContext paymentStateContext, final PaymentAutomatonDAOHelper daoHelper) throws PaymentApiException {
    try {
        final StateMachineConfig stateMachineConfig = paymentSMHelper.getStateMachineConfig(daoHelper.getPaymentProviderPluginName(includeDeletedPaymentMethod), paymentStateContext.getInternalCallContext());
        final StateMachine initialStateMachine = stateMachineConfig.getStateMachineForState(initialStateName);
        final State initialState = initialStateMachine.getState(initialStateName);
        final Operation operation = paymentSMHelper.getOperationForTransaction(stateMachineConfig, transactionType);
        initialState.runOperation(operation, operationCallback, enteringStateCallback, leavingStateCallback);
    } catch (final MissingEntryException e) {
        throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INVALID_OPERATION, transactionType, initialStateName);
    } catch (final OperationException e) {
        if (e.getCause() == null) {
            throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
        } else if (e.getCause() instanceof PaymentApiException) {
            throw (PaymentApiException) e.getCause();
        } else {
            throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
        }
    }
}
Also used : StateMachine(org.killbill.automaton.StateMachine) State(org.killbill.automaton.State) StateMachineConfig(org.killbill.automaton.StateMachineConfig) MissingEntryException(org.killbill.automaton.MissingEntryException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) PurchaseOperation(org.killbill.billing.payment.core.sm.payments.PurchaseOperation) RefundOperation(org.killbill.billing.payment.core.sm.payments.RefundOperation) AuthorizeOperation(org.killbill.billing.payment.core.sm.payments.AuthorizeOperation) VoidOperation(org.killbill.billing.payment.core.sm.payments.VoidOperation) Operation(org.killbill.automaton.Operation) CreditOperation(org.killbill.billing.payment.core.sm.payments.CreditOperation) ChargebackOperation(org.killbill.billing.payment.core.sm.payments.ChargebackOperation) CaptureOperation(org.killbill.billing.payment.core.sm.payments.CaptureOperation) OperationException(org.killbill.automaton.OperationException)

Example 9 with OperationException

use of org.killbill.automaton.OperationException in project killbill by killbill.

the class ChargebackInitiated method leavingState.

@Override
public void leavingState(final State oldState) throws OperationException {
    // Sanity: chargeback reversals can only happen after a successful chargeback
    if (OperationResult.FAILURE.equals(paymentStateContext.getOverridePluginOperationResult())) {
        final List<PaymentTransactionModelDao> paymentTransactionsForCurrentPayment = paymentStateContext.getPaymentId() != null ? daoHelper.getPaymentDao().getTransactionsForPayment(paymentStateContext.getPaymentId(), paymentStateContext.getInternalCallContext()) : ImmutableList.<PaymentTransactionModelDao>of();
        final Iterable<PaymentTransactionModelDao> existingPaymentTransactionsForTransactionIdOrKey = filterExistingPaymentTransactionsForTransactionIdOrKey(paymentTransactionsForCurrentPayment, paymentStateContext.getTransactionId(), paymentStateContext.getPaymentTransactionExternalKey());
        if (Iterables.<PaymentTransactionModelDao>isEmpty(existingPaymentTransactionsForTransactionIdOrKey)) {
            // Chargeback reversals can only happen after a successful chargeback
            throw new OperationException(new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId()));
        }
    }
    super.leavingState(oldState);
}
Also used : PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) OperationException(org.killbill.automaton.OperationException)

Example 10 with OperationException

use of org.killbill.automaton.OperationException in project killbill by killbill.

the class PaymentOperation method convertToUnknownTransactionStatusAndErroredPaymentState.

//
// In case of exceptions, timeouts, we don't really know what happened:
// - Return an OperationResult.EXCEPTION to transition Payment State to Errored (see PaymentTransactionInfoPluginConverter#toOperationResult)
// - Construct a PaymentTransactionInfoPlugin whose PaymentPluginStatus = UNDEFINED to end up with a paymentTransactionStatus = UNKNOWN and have a chance to
//   be fixed by Janitor.
//
private OperationException convertToUnknownTransactionStatusAndErroredPaymentState(final Exception e) {
    final PaymentTransactionInfoPlugin paymentInfoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(), paymentStateContext.getTransactionId(), paymentStateContext.getTransactionType(), paymentStateContext.getAmount(), paymentStateContext.getCurrency(), paymentStateContext.getCallContext().getCreatedDate(), paymentStateContext.getCallContext().getCreatedDate(), PaymentPluginStatus.UNDEFINED, null, null);
    paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
    if (e.getCause() instanceof OperationException) {
        return (OperationException) e.getCause();
    }
    if (e instanceof OperationException) {
        return (OperationException) e;
    }
    return new OperationException(e, OperationResult.EXCEPTION);
}
Also used : DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) OperationException(org.killbill.automaton.OperationException)

Aggregations

OperationException (org.killbill.automaton.OperationException)11 PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)9 MissingEntryException (org.killbill.automaton.MissingEntryException)3 PaymentControlApiException (org.killbill.billing.control.plugin.api.PaymentControlApiException)3 PaymentTransactionModelDao (org.killbill.billing.payment.dao.PaymentTransactionModelDao)3 OperationCallback (org.killbill.automaton.Operation.OperationCallback)2 EnteringStateCallback (org.killbill.automaton.State.EnteringStateCallback)2 LeavingStateCallback (org.killbill.automaton.State.LeavingStateCallback)2 PaymentControlContext (org.killbill.billing.control.plugin.api.PaymentControlContext)2 Payment (org.killbill.billing.payment.api.Payment)2 DefaultPaymentControlContext (org.killbill.billing.payment.core.sm.control.ControlPluginRunner.DefaultPaymentControlContext)2 DefaultControlCompleted (org.killbill.billing.payment.core.sm.control.DefaultControlCompleted)2 PaymentOperation (org.killbill.billing.payment.core.sm.payments.PaymentOperation)2 PluginDispatcherReturnType (org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType)2 Test (org.testng.annotations.Test)2 ImmutableList (com.google.common.collect.ImmutableList)1 List (java.util.List)1 UUID (java.util.UUID)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1 DateTime (org.joda.time.DateTime)1