Search in sources :

Example 41 with PaymentApiException

use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.

the class PluginControlPaymentAutomatonRunner method completeRun.

public Payment completeRun(final PaymentStateControlContext paymentStateContext) throws PaymentApiException {
    try {
        final OperationCallback callback = new CompletionControlOperation(locker, paymentPluginDispatcher, paymentConfig, paymentStateContext, paymentRefresher, paymentProcessor, controlPluginRunner);
        final LeavingStateCallback leavingStateCallback = new NoopControlInitiated();
        final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, paymentControlStateMachineHelper.getRetriedState(), retryServiceScheduler);
        paymentControlStateMachineHelper.getInitialState().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();
}
Also used : OperationCallback(org.killbill.automaton.Operation.OperationCallback) CompletionControlOperation(org.killbill.billing.payment.core.sm.control.CompletionControlOperation) NoopControlInitiated(org.killbill.billing.payment.core.sm.control.NoopControlInitiated) DefaultControlCompleted(org.killbill.billing.payment.core.sm.control.DefaultControlCompleted) MissingEntryException(org.killbill.automaton.MissingEntryException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) LeavingStateCallback(org.killbill.automaton.State.LeavingStateCallback) EnteringStateCallback(org.killbill.automaton.State.EnteringStateCallback) OperationException(org.killbill.automaton.OperationException) PaymentControlApiException(org.killbill.billing.control.plugin.api.PaymentControlApiException)

Example 42 with PaymentApiException

use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.

the class PaymentAutomatonDAOHelper method createNewPaymentTransaction.

public void createNewPaymentTransaction() throws PaymentApiException {
    final PaymentTransactionModelDao paymentTransactionModelDao;
    final List<PaymentTransactionModelDao> existingTransactions;
    if (paymentStateContext.getPaymentId() == null) {
        final PaymentModelDao newPaymentModelDao = buildNewPaymentModelDao();
        final PaymentTransactionModelDao newPaymentTransactionModelDao = buildNewPaymentTransactionModelDao(newPaymentModelDao.getId());
        existingTransactions = ImmutableList.of();
        final PaymentAndTransactionModelDao paymentAndTransactionModelDao = paymentDao.insertPaymentWithFirstTransaction(newPaymentModelDao, newPaymentTransactionModelDao, internalCallContext);
        paymentTransactionModelDao = paymentAndTransactionModelDao.getPaymentTransactionModelDao();
    } else {
        existingTransactions = paymentDao.getTransactionsForPayment(paymentStateContext.getPaymentId(), internalCallContext);
        if (existingTransactions.isEmpty()) {
            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentStateContext.getPaymentId());
        }
        if (paymentStateContext.getCurrency() != null && existingTransactions.get(0).getCurrency() != paymentStateContext.getCurrency() && !TransactionType.CHARGEBACK.equals(paymentStateContext.getTransactionType())) {
            // Note that we allow chargebacks in a different currency
            throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "currency", " should be " + existingTransactions.get(0).getCurrency() + " to match other existing transactions");
        }
        final PaymentTransactionModelDao newPaymentTransactionModelDao = buildNewPaymentTransactionModelDao(paymentStateContext.getPaymentId());
        paymentTransactionModelDao = paymentDao.updatePaymentWithNewTransaction(paymentStateContext.getPaymentId(), newPaymentTransactionModelDao, internalCallContext);
    }
    // Update the context
    paymentStateContext.setPaymentTransactionModelDao(paymentTransactionModelDao);
    paymentStateContext.setOnLeavingStateExistingTransactions(existingTransactions);
}
Also used : PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) PaymentAndTransactionModelDao(org.killbill.billing.payment.dao.PaymentAndTransactionModelDao) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException)

Example 43 with PaymentApiException

use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.

the class PaymentMethodProcessor method addPaymentMethod.

public UUID addPaymentMethod(final String paymentMethodExternalKey, final String paymentPluginServiceName, final Account account, final boolean setDefault, final PaymentMethodPlugin paymentMethodProps, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context) throws PaymentApiException {
    return dispatchWithExceptionHandling(account, paymentPluginServiceName, new CallableWithAccountLock<UUID, PaymentApiException>(locker, account.getId(), paymentConfig, new DispatcherCallback<PluginDispatcherReturnType<UUID>, PaymentApiException>() {

        @Override
        public PluginDispatcherReturnType<UUID> doOperation() throws PaymentApiException {
            PaymentMethod pm = null;
            try {
                validateUniqueExternalPaymentMethod(account.getId(), paymentPluginServiceName);
                pm = new DefaultPaymentMethod(paymentMethodExternalKey, account.getId(), paymentPluginServiceName, paymentMethodProps);
                final PaymentPluginApi pluginApi = getPaymentPluginApi(paymentPluginServiceName);
                pluginApi.addPaymentMethod(account.getId(), pm.getId(), paymentMethodProps, setDefault, properties, callContext);
                final String actualPaymentMethodExternalKey = retrieveActualPaymentMethodExternalKey(account, pm, pluginApi, properties, callContext, context);
                final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(pm.getId(), actualPaymentMethodExternalKey, pm.getCreatedDate(), pm.getUpdatedDate(), pm.getAccountId(), pm.getPluginName(), pm.isActive());
                paymentDao.insertPaymentMethod(pmModel, context);
                if (setDefault) {
                    accountInternalApi.updatePaymentMethod(account.getId(), pm.getId(), context);
                }
            } catch (final PaymentPluginApiException e) {
                throw new PaymentApiException(e, ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
            } catch (final AccountApiException e) {
                throw new PaymentApiException(e);
            }
            return PluginDispatcher.createPluginDispatcherReturnType(pm.getId());
        }

        private void validateUniqueExternalPaymentMethod(final UUID accountId, final String pluginName) throws PaymentApiException {
            if (ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(pluginName)) {
                final List<PaymentMethodModelDao> accountPaymentMethods = paymentDao.getPaymentMethods(context);
                if (Iterables.any(accountPaymentMethods, new Predicate<PaymentMethodModelDao>() {

                    @Override
                    public boolean apply(final PaymentMethodModelDao input) {
                        return ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(input.getPluginName());
                    }
                })) {
                    throw new PaymentApiException(ErrorCode.PAYMENT_EXTERNAL_PAYMENT_METHOD_ALREADY_EXISTS, accountId);
                }
            }
        }
    }), uuidPluginNotificationDispatcher);
}
Also used : PaymentPluginApiException(org.killbill.billing.payment.plugin.api.PaymentPluginApiException) PaymentMethodModelDao(org.killbill.billing.payment.dao.PaymentMethodModelDao) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) DefaultPaymentMethod(org.killbill.billing.payment.api.DefaultPaymentMethod) PaymentPluginApi(org.killbill.billing.payment.plugin.api.PaymentPluginApi) AccountApiException(org.killbill.billing.account.api.AccountApiException) PaymentMethod(org.killbill.billing.payment.api.PaymentMethod) DefaultPaymentMethod(org.killbill.billing.payment.api.DefaultPaymentMethod) UUID(java.util.UUID)

Example 44 with PaymentApiException

use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.

the class PaymentProcessor method performOperation.

private Payment performOperation(final boolean isApiPayment, final boolean runJanitor, @Nullable final UUID attemptId, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, @Nullable final UUID transactionId, @Nullable final BigDecimal amount, @Nullable final Currency currency, @Nullable final DateTime effectiveDate, @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey, @Nullable final UUID paymentIdForNewPayment, @Nullable final UUID paymentTransactionIdForNewPaymentTransaction, final boolean shouldLockAccountAndDispatch, @Nullable final OperationResult overridePluginOperationResult, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
    final PaymentStateContext paymentStateContext = paymentAutomatonRunner.buildPaymentStateContext(isApiPayment, transactionType, account, attemptId, paymentMethodId != null ? paymentMethodId : account.getPaymentMethodId(), paymentId, transactionId, paymentExternalKey, paymentTransactionExternalKey, amount, currency, effectiveDate, paymentIdForNewPayment, paymentTransactionIdForNewPaymentTransaction, shouldLockAccountAndDispatch, overridePluginOperationResult, properties, callContext, internalCallContext);
    final PaymentAutomatonDAOHelper daoHelper = paymentAutomatonRunner.buildDaoHelper(paymentStateContext, internalCallContext);
    String currentStateName = null;
    if (paymentStateContext.getPaymentId() != null) {
        PaymentModelDao paymentModelDao = daoHelper.getPayment();
        // Sanity: verify the payment belongs to the right account (in case it was looked-up by payment or transaction external key)
        if (!paymentModelDao.getAccountRecordId().equals(internalCallContext.getAccountRecordId())) {
            throw new PaymentApiException(ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID, paymentStateContext.getPaymentId());
        }
        // Note: the list needs to be modifiable for invokeJanitor
        final Collection<PaymentTransactionModelDao> paymentTransactionsForCurrentPayment = new LinkedList<PaymentTransactionModelDao>(daoHelper.getPaymentDao().getTransactionsForPayment(paymentStateContext.getPaymentId(), paymentStateContext.getInternalCallContext()));
        // prevent disallowed transitions in case the state couldn't be fixed (or if it's already in a final state).
        if (runJanitor) {
            final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), true, internalCallContext);
            final List<PaymentTransactionInfoPlugin> pluginTransactions = paymentRefresher.getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
            paymentModelDao = paymentRefresher.invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, isApiPayment, internalCallContext);
        }
        if (paymentStateContext.getPaymentTransactionExternalKey() != null) {
            final List<PaymentTransactionModelDao> allPaymentTransactionsForKey = daoHelper.getPaymentDao().getPaymentTransactionsByExternalKey(paymentStateContext.getPaymentTransactionExternalKey(), internalCallContext);
            runSanityOnTransactionExternalKey(allPaymentTransactionsForKey, paymentStateContext, internalCallContext);
        }
        if (paymentStateContext.getTransactionId() != null || paymentStateContext.getPaymentTransactionExternalKey() != null) {
            // If a transaction id or key is passed, we are maybe completing an existing transaction (unless a new key was provided)
            PaymentTransactionModelDao transactionToComplete = findTransactionToCompleteAndRunSanityChecks(paymentModelDao, paymentTransactionsForCurrentPayment, paymentStateContext, internalCallContext);
            if (transactionToComplete != null) {
                final UUID transactionToCompleteId = transactionToComplete.getId();
                transactionToComplete = Iterables.<PaymentTransactionModelDao>find(paymentTransactionsForCurrentPayment, new Predicate<PaymentTransactionModelDao>() {

                    @Override
                    public boolean apply(final PaymentTransactionModelDao input) {
                        return transactionToCompleteId.equals(input.getId());
                    }
                });
                // We can't tell where we should be in the state machine - bail (cannot be enforced by the state machine unfortunately because UNKNOWN and PLUGIN_FAILURE are both treated as EXCEPTION)
                if (transactionToComplete.getTransactionStatus() == TransactionStatus.UNKNOWN) {
                    throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_OPERATION, paymentStateContext.getTransactionType(), transactionToComplete.getTransactionStatus());
                }
                paymentStateContext.setPaymentTransactionModelDao(transactionToComplete);
            }
        }
        // Use the original payment method id of the payment being completed
        paymentStateContext.setPaymentMethodId(paymentModelDao.getPaymentMethodId());
        // We always take the last successful state name to permit retries on failures
        currentStateName = paymentModelDao.getLastSuccessStateName();
    }
    paymentAutomatonRunner.run(paymentStateContext, daoHelper, currentStateName, transactionType);
    return paymentRefresher.getPayment(paymentStateContext.getPaymentModelDao(), true, false, isApiPayment, properties, callContext, internalCallContext);
}
Also used : PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) LinkedList(java.util.LinkedList) Predicate(com.google.common.base.Predicate) PaymentPluginApi(org.killbill.billing.payment.plugin.api.PaymentPluginApi) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentAutomatonDAOHelper(org.killbill.billing.payment.core.sm.PaymentAutomatonDAOHelper) PaymentStateContext(org.killbill.billing.payment.core.sm.PaymentStateContext) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) UUID(java.util.UUID)

Example 45 with PaymentApiException

use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.

the class PaymentProcessor method notifyPendingPaymentOfStateChanged.

public Payment notifyPendingPaymentOfStateChanged(final Account account, final UUID transactionId, final boolean isSuccess, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
    final PaymentTransactionModelDao transactionModelDao = paymentDao.getPaymentTransaction(transactionId, internalCallContext);
    if (transactionModelDao.getTransactionStatus() != TransactionStatus.PENDING) {
        throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, transactionModelDao.getPaymentId());
    }
    final OperationResult overridePluginResult = isSuccess ? OperationResult.SUCCESS : OperationResult.FAILURE;
    final boolean runJanitor = false;
    return performOperation(true, runJanitor, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(), transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, null, transactionModelDao.getTransactionExternalKey(), null, null, true, overridePluginResult, PLUGIN_PROPERTIES, callContext, internalCallContext);
}
Also used : PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) OperationResult(org.killbill.automaton.OperationResult)

Aggregations

PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)57 UUID (java.util.UUID)20 Test (org.testng.annotations.Test)19 PaymentTransactionModelDao (org.killbill.billing.payment.dao.PaymentTransactionModelDao)14 Account (org.killbill.billing.account.api.Account)12 Payment (org.killbill.billing.payment.api.Payment)12 PaymentAttemptModelDao (org.killbill.billing.payment.dao.PaymentAttemptModelDao)11 BigDecimal (java.math.BigDecimal)10 OperationException (org.killbill.automaton.OperationException)10 PaymentModelDao (org.killbill.billing.payment.dao.PaymentModelDao)9 Predicate (com.google.common.base.Predicate)7 PaymentControlApiException (org.killbill.billing.control.plugin.api.PaymentControlApiException)7 PluginDispatcherReturnType (org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType)7 PaymentPluginApi (org.killbill.billing.payment.plugin.api.PaymentPluginApi)7 LinkedList (java.util.LinkedList)6 AccountApiException (org.killbill.billing.account.api.AccountApiException)6 Invoice (org.killbill.billing.invoice.api.Invoice)6 PluginProperty (org.killbill.billing.payment.api.PluginProperty)6 PaymentPluginApiException (org.killbill.billing.payment.plugin.api.PaymentPluginApiException)6 DateTime (org.joda.time.DateTime)5