Search in sources :

Example 6 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class PaymentEnteringStateCallback method enteringState.

@Override
public void enteringState(final State newState, final Operation.OperationCallback operationCallback, final OperationResult operationResult, final LeavingStateCallback leavingStateCallback) {
    logger.debug("Entering state {} with result {}", newState.getName(), operationResult);
    // If the transaction was not created -- for instance we had an exception in leavingState callback then we bail; if not, then update state:
    if (paymentStateContext.getPaymentTransactionModelDao() != null && paymentStateContext.getPaymentTransactionModelDao().getId() != null) {
        final PaymentTransactionInfoPlugin paymentInfoPlugin = paymentStateContext.getPaymentTransactionInfoPlugin();
        final TransactionStatus transactionStatus = PaymentTransactionInfoPluginConverter.toTransactionStatus(paymentInfoPlugin);
        // The bus event will be posted from the transaction
        daoHelper.processPaymentInfoPlugin(transactionStatus, paymentInfoPlugin, newState.getName());
    } else if (!paymentStateContext.isApiPayment()) {
        //
        // If there is NO transaction to update (because payment transaction did not occur), then there is something wrong happening (maybe a missing defaultPaymentMethodId, ...)
        // so, if the call does NOT originates from api then we still want to send a bus event so the system can react to it if needed.
        //
        final BusInternalEvent event = new DefaultPaymentErrorEvent(paymentStateContext.getAccount().getId(), null, null, paymentStateContext.getAmount(), paymentStateContext.getCurrency(), null, paymentStateContext.getTransactionType(), null, "Early abortion of payment transaction", paymentStateContext.getInternalCallContext().getAccountRecordId(), paymentStateContext.getInternalCallContext().getTenantRecordId(), paymentStateContext.getInternalCallContext().getUserToken());
        try {
            daoHelper.getEventBus().post(event);
        } catch (EventBusException e) {
            logger.warn("Failed to post event {}", event, e);
        }
    }
}
Also used : PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) TransactionStatus(org.killbill.billing.payment.api.TransactionStatus) EventBusException(org.killbill.bus.api.PersistentBus.EventBusException) DefaultPaymentErrorEvent(org.killbill.billing.payment.api.DefaultPaymentErrorEvent) BusInternalEvent(org.killbill.billing.events.BusInternalEvent)

Example 7 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class IncompletePaymentTransactionTask method tryToProcessNotification.

public void tryToProcessNotification(final JanitorNotificationKey notificationKey, final UUID userToken, final Long accountRecordId, final long tenantRecordId) throws LockFailedException {
    final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, accountRecordId);
    tryToDoJanitorOperationWithAccountLock(new JanitorIterationCallback() {

        @Override
        public Void doIteration() {
            // State may have changed since we originally retrieved with no lock
            final PaymentTransactionModelDao rehydratedPaymentTransaction = paymentDao.getPaymentTransaction(notificationKey.getUuidKey(), internalTenantContext);
            final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
            final PaymentModelDao payment = paymentDao.getPayment(rehydratedPaymentTransaction.getPaymentId(), internalTenantContext);
            final PaymentTransactionInfoPlugin undefinedPaymentTransaction = new DefaultNoOpPaymentInfoPlugin(payment.getId(), rehydratedPaymentTransaction.getId(), rehydratedPaymentTransaction.getTransactionType(), rehydratedPaymentTransaction.getAmount(), rehydratedPaymentTransaction.getCurrency(), rehydratedPaymentTransaction.getCreatedDate(), rehydratedPaymentTransaction.getCreatedDate(), PaymentPluginStatus.UNDEFINED, null, null);
            PaymentTransactionInfoPlugin paymentTransactionInfoPlugin;
            try {
                final PaymentPluginApi paymentPluginApi = paymentPluginServiceRegistration.getPaymentPluginApi(payment.getPaymentMethodId(), false, internalTenantContext);
                final List<PaymentTransactionInfoPlugin> result = paymentPluginApi.getPaymentInfo(payment.getAccountId(), payment.getId(), ImmutableList.<PluginProperty>of(), tenantContext);
                paymentTransactionInfoPlugin = Iterables.tryFind(result, new Predicate<PaymentTransactionInfoPlugin>() {

                    @Override
                    public boolean apply(final PaymentTransactionInfoPlugin input) {
                        return input.getKbTransactionPaymentId().equals(rehydratedPaymentTransaction.getId());
                    }
                }).or(new Supplier<PaymentTransactionInfoPlugin>() {

                    @Override
                    public PaymentTransactionInfoPlugin get() {
                        return undefinedPaymentTransaction;
                    }
                });
            } catch (final Exception e) {
                paymentTransactionInfoPlugin = undefinedPaymentTransaction;
            }
            updatePaymentAndTransactionIfNeeded(payment, notificationKey.getAttemptNumber(), userToken, rehydratedPaymentTransaction, paymentTransactionInfoPlugin, internalTenantContext);
            return null;
        }
    }, internalTenantContext);
}
Also used : DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) TenantContext(org.killbill.billing.util.callcontext.TenantContext) LockFailedException(org.killbill.commons.locker.LockFailedException) IOException(java.io.IOException) Predicate(com.google.common.base.Predicate) PaymentPluginApi(org.killbill.billing.payment.plugin.api.PaymentPluginApi) PluginProperty(org.killbill.billing.payment.api.PluginProperty) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List)

Example 8 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin 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 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, 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 = getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
            paymentModelDao = invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, 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();
    }
    final UUID nonNullPaymentId = paymentAutomatonRunner.run(paymentStateContext, daoHelper, currentStateName, transactionType);
    return getPayment(nonNullPaymentId, true, false, 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 9 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class PaymentProcessor method toPayment.

// Used in bulk get API (getAccountPayments)
private Payment toPayment(final PaymentModelDao curPaymentModelDao, final Collection<PaymentTransactionModelDao> curTransactionsModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final boolean withAttempts, final InternalTenantContext internalTenantContext) {
    final Collection<PaymentTransactionModelDao> transactionsModelDao = new LinkedList<PaymentTransactionModelDao>(curTransactionsModelDao);
    invokeJanitor(curPaymentModelDao, transactionsModelDao, pluginTransactions, internalTenantContext);
    final Collection<PaymentTransaction> transactions = new LinkedList<PaymentTransaction>();
    for (final PaymentTransactionModelDao newPaymentTransactionModelDao : transactionsModelDao) {
        final PaymentTransactionInfoPlugin paymentTransactionInfoPlugin = findPaymentTransactionInfoPlugin(newPaymentTransactionModelDao, pluginTransactions);
        final PaymentTransaction transaction = new DefaultPaymentTransaction(newPaymentTransactionModelDao.getId(), newPaymentTransactionModelDao.getAttemptId(), newPaymentTransactionModelDao.getTransactionExternalKey(), newPaymentTransactionModelDao.getCreatedDate(), newPaymentTransactionModelDao.getUpdatedDate(), newPaymentTransactionModelDao.getPaymentId(), newPaymentTransactionModelDao.getTransactionType(), newPaymentTransactionModelDao.getEffectiveDate(), newPaymentTransactionModelDao.getTransactionStatus(), newPaymentTransactionModelDao.getAmount(), newPaymentTransactionModelDao.getCurrency(), newPaymentTransactionModelDao.getProcessedAmount(), newPaymentTransactionModelDao.getProcessedCurrency(), newPaymentTransactionModelDao.getGatewayErrorCode(), newPaymentTransactionModelDao.getGatewayErrorMsg(), paymentTransactionInfoPlugin);
        transactions.add(transaction);
    }
    final Ordering<PaymentTransaction> perPaymentTransactionOrdering = Ordering.<PaymentTransaction>from(new Comparator<PaymentTransaction>() {

        @Override
        public int compare(final PaymentTransaction o1, final PaymentTransaction o2) {
            return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
        }
    });
    final List<PaymentTransaction> sortedTransactions = perPaymentTransactionOrdering.immutableSortedCopy(transactions);
    return new DefaultPayment(curPaymentModelDao.getId(), curPaymentModelDao.getCreatedDate(), curPaymentModelDao.getUpdatedDate(), curPaymentModelDao.getAccountId(), curPaymentModelDao.getPaymentMethodId(), curPaymentModelDao.getPaymentNumber(), curPaymentModelDao.getExternalKey(), sortedTransactions, (withAttempts && !sortedTransactions.isEmpty()) ? getPaymentAttempts(paymentDao.getPaymentAttempts(curPaymentModelDao.getExternalKey(), internalTenantContext), internalTenantContext) : null);
}
Also used : DefaultPaymentTransaction(org.killbill.billing.payment.api.DefaultPaymentTransaction) PaymentTransaction(org.killbill.billing.payment.api.PaymentTransaction) DefaultPayment(org.killbill.billing.payment.api.DefaultPayment) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) DefaultPaymentTransaction(org.killbill.billing.payment.api.DefaultPaymentTransaction) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) LinkedList(java.util.LinkedList)

Example 10 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class PaymentOperation method doOperation.

private OperationResult doOperation() throws PaymentApiException {
    try {
        //
        if (paymentStateContext.getOverridePluginOperationResult() == null) {
            final PaymentTransactionInfoPlugin paymentInfoPlugin = doCallSpecificOperationCallback();
            //
            if (paymentInfoPlugin == null) {
                throw new IllegalStateException("Payment plugin returned a null result");
            }
            paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
            return PaymentTransactionInfoPluginConverter.toOperationResult(paymentStateContext.getPaymentTransactionInfoPlugin());
        } else {
            final PaymentTransactionInfoPlugin paymentInfoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(), paymentStateContext.getTransactionId(), paymentStateContext.getTransactionType(), paymentStateContext.getPaymentTransactionModelDao().getProcessedAmount(), paymentStateContext.getPaymentTransactionModelDao().getProcessedCurrency(), paymentStateContext.getPaymentTransactionModelDao().getEffectiveDate(), paymentStateContext.getPaymentTransactionModelDao().getCreatedDate(), buildPaymentPluginStatusFromOperationResult(paymentStateContext.getOverridePluginOperationResult()), null, null);
            paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
            return paymentStateContext.getOverridePluginOperationResult();
        }
    } catch (final PaymentPluginApiException e) {
        throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
    }
}
Also used : DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentPluginApiException(org.killbill.billing.payment.plugin.api.PaymentPluginApiException) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException)

Aggregations

PaymentTransactionInfoPlugin (org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin)16 PaymentTransactionModelDao (org.killbill.billing.payment.dao.PaymentTransactionModelDao)6 BigDecimal (java.math.BigDecimal)5 PaymentModelDao (org.killbill.billing.payment.dao.PaymentModelDao)5 DefaultNoOpPaymentInfoPlugin (org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin)5 Test (org.testng.annotations.Test)5 LinkedList (java.util.LinkedList)4 PluginProperty (org.killbill.billing.payment.api.PluginProperty)4 Predicate (com.google.common.base.Predicate)3 PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)3 PaymentPluginApi (org.killbill.billing.payment.plugin.api.PaymentPluginApi)3 UUID (java.util.UUID)2 PaymentPluginApiException (org.killbill.billing.payment.plugin.api.PaymentPluginApiException)2 EventBusException (org.killbill.bus.api.PersistentBus.EventBusException)2 ImmutableList (com.google.common.collect.ImmutableList)1 IOException (java.io.IOException)1 List (java.util.List)1 OperationException (org.killbill.automaton.OperationException)1 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)1 Currency (org.killbill.billing.catalog.api.Currency)1