Search in sources :

Example 11 with AccountApiException

use of org.killbill.billing.account.api.AccountApiException in project killbill by killbill.

the class PaymentMethodResource method searchPaymentMethods.

@TimedResource
@GET
@Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Search payment methods", response = PaymentMethodJson.class, responseContainer = "List")
@ApiResponses(value = {})
public Response searchPaymentMethods(@PathParam("searchKey") final String searchKey, @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_NAME) final String pluginName, @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo, @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
    final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
    final TenantContext tenantContext = context.createContext(request);
    // Search the plugin(s)
    final Pagination<PaymentMethod> paymentMethods;
    if (Strings.isNullOrEmpty(pluginName)) {
        paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, pluginProperties, tenantContext);
    } else {
        paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, pluginProperties, tenantContext);
    }
    final URI nextPageUri = uriBuilder.nextPage(PaymentMethodResource.class, "searchPaymentMethods", paymentMethods.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey, QUERY_PAYMENT_METHOD_PLUGIN_NAME, Strings.nullToEmpty(pluginName), QUERY_AUDIT, auditMode.getLevel().toString()));
    final AtomicReference<Map<UUID, AccountAuditLogs>> accountsAuditLogs = new AtomicReference<Map<UUID, AccountAuditLogs>>(new HashMap<UUID, AccountAuditLogs>());
    final Map<UUID, Account> accounts = new HashMap<UUID, Account>();
    return buildStreamingPaginationResponse(paymentMethods, new Function<PaymentMethod, PaymentMethodJson>() {

        @Override
        public PaymentMethodJson apply(final PaymentMethod paymentMethod) {
            // Cache audit logs per account
            if (accountsAuditLogs.get().get(paymentMethod.getAccountId()) == null) {
                accountsAuditLogs.get().put(paymentMethod.getAccountId(), auditUserApi.getAccountAuditLogs(paymentMethod.getAccountId(), auditMode.getLevel(), tenantContext));
            }
            // Lookup the associated account(s)
            if (accounts.get(paymentMethod.getAccountId()) == null) {
                final Account account;
                try {
                    account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
                    accounts.put(paymentMethod.getAccountId(), account);
                } catch (final AccountApiException e) {
                    log.warn("Error retrieving accountId='{}'", paymentMethod.getAccountId(), e);
                    return null;
                }
            }
            return PaymentMethodJson.toPaymentMethodJson(accounts.get(paymentMethod.getAccountId()), paymentMethod, accountsAuditLogs.get().get(paymentMethod.getAccountId()));
        }
    }, nextPageUri);
}
Also used : Account(org.killbill.billing.account.api.Account) HashMap(java.util.HashMap) TenantContext(org.killbill.billing.util.callcontext.TenantContext) AtomicReference(java.util.concurrent.atomic.AtomicReference) URI(java.net.URI) PluginProperty(org.killbill.billing.payment.api.PluginProperty) PaymentMethodJson(org.killbill.billing.jaxrs.json.PaymentMethodJson) AccountApiException(org.killbill.billing.account.api.AccountApiException) PaymentMethod(org.killbill.billing.payment.api.PaymentMethod) UUID(java.util.UUID) AccountAuditLogs(org.killbill.billing.util.audit.AccountAuditLogs) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) HashMap(java.util.HashMap) Path(javax.ws.rs.Path) TimedResource(org.killbill.commons.metrics.TimedResource) Produces(javax.ws.rs.Produces) GET(javax.ws.rs.GET) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 12 with AccountApiException

use of org.killbill.billing.account.api.AccountApiException in project killbill by killbill.

the class InvoiceDispatcher method processAccountWithLockAndInputTargetDate.

private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final LocalDate targetDate, final BillingEventSet billingEvents, final boolean isDryRun, final InternalCallContext context) throws InvoiceApiException {
    try {
        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
        final List<Invoice> invoices = billingEvents.isAccountAutoInvoiceOff() ? ImmutableList.<Invoice>of() : ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(context), new Function<InvoiceModelDao, Invoice>() {

            @Override
            public Invoice apply(final InvoiceModelDao input) {
                return new DefaultInvoice(input);
            }
        }));
        final Currency targetCurrency = account.getCurrency();
        final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
        final DefaultInvoice invoice = invoiceWithMetadata.getInvoice();
        // Compute future notifications
        final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, context);
        //
        if (invoice == null) {
            if (isDryRun) {
                log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
            } else {
                log.info("Generated null invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
                final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
                commitInvoiceAndSetFutureNotifications(account, null, futureAccountNotifications, context);
                postEvent(event);
            }
            return null;
        }
        // Generate missing credit (> 0 for generation and < 0 for use) prior we call the plugin
        final InvoiceItem cbaItemPreInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
        DefaultInvoice tmpInvoiceForInvoicePlugins = invoice;
        if (cbaItemPreInvoicePlugins != null) {
            tmpInvoiceForInvoicePlugins = (DefaultInvoice) tmpInvoiceForInvoicePlugins.clone();
            tmpInvoiceForInvoicePlugins.addInvoiceItem(cbaItemPreInvoicePlugins);
        }
        //
        // Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
        //
        final CallContext callContext = buildCallContext(context);
        final List<InvoiceItem> additionalInvoiceItemsFromPlugins = invoicePluginDispatcher.getAdditionalInvoiceItems(tmpInvoiceForInvoicePlugins, isDryRun, callContext);
        if (additionalInvoiceItemsFromPlugins.isEmpty()) {
            // PERF: avoid re-computing the CBA if no change was made
            if (cbaItemPreInvoicePlugins != null) {
                invoice.addInvoiceItem(cbaItemPreInvoicePlugins);
            }
        } else {
            invoice.addInvoiceItems(additionalInvoiceItemsFromPlugins);
            // Use credit after we call the plugin (https://github.com/killbill/killbill/issues/637)
            final InvoiceItem cbaItemPostInvoicePlugins = computeCBAOnExistingInvoice(invoice, context);
            if (cbaItemPostInvoicePlugins != null) {
                invoice.addInvoiceItem(cbaItemPostInvoicePlugins);
            }
        }
        if (!isDryRun) {
            // Compute whether this is a new invoice object (or just some adjustments on an existing invoice), and extract invoiceIds for later use
            final Set<UUID> uniqueInvoiceIds = getUniqueInvoiceIds(invoice);
            final boolean isRealInvoiceWithItems = uniqueInvoiceIds.remove(invoice.getId());
            final Set<UUID> adjustedUniqueOtherInvoiceId = uniqueInvoiceIds;
            logInvoiceWithItems(account, invoice, targetDate, adjustedUniqueOtherInvoiceId, isRealInvoiceWithItems);
            // Transformation to Invoice -> InvoiceModelDao
            final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoice);
            final List<InvoiceItemModelDao> invoiceItemModelDaos = transformToInvoiceModelDao(invoice.getInvoiceItems());
            invoiceModelDao.addInvoiceItems(invoiceItemModelDaos);
            // Commit invoice on disk
            final boolean isThereAnyItemsLeft = commitInvoiceAndSetFutureNotifications(account, invoiceModelDao, futureAccountNotifications, context);
            final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
            setChargedThroughDates(invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
            if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
                notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
            }
        }
        return invoice;
    } catch (final AccountApiException e) {
        log.error("Failed handling SubscriptionBase change.", e);
        return null;
    } catch (final SubscriptionBaseApiException e) {
        log.error("Failed handling SubscriptionBase change.", e);
        return null;
    }
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) Invoice(org.killbill.billing.invoice.api.Invoice) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) ItemAdjInvoiceItem(org.killbill.billing.invoice.model.ItemAdjInvoiceItem) FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) ParentInvoiceItem(org.killbill.billing.invoice.model.ParentInvoiceItem) RecurringInvoiceItem(org.killbill.billing.invoice.model.RecurringInvoiceItem) InvoiceModelDao(org.killbill.billing.invoice.dao.InvoiceModelDao) FixedPriceInvoiceItem(org.killbill.billing.invoice.model.FixedPriceInvoiceItem) BusInternalEvent(org.killbill.billing.events.BusInternalEvent) InvoiceWithMetadata(org.killbill.billing.invoice.generator.InvoiceWithMetadata) CallContext(org.killbill.billing.util.callcontext.CallContext) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) DefaultNullInvoiceEvent(org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent) InvoiceItemModelDao(org.killbill.billing.invoice.dao.InvoiceItemModelDao) Currency(org.killbill.billing.catalog.api.Currency) AccountApiException(org.killbill.billing.account.api.AccountApiException) UUID(java.util.UUID) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 13 with AccountApiException

use of org.killbill.billing.account.api.AccountApiException in project killbill by killbill.

the class InvoiceDispatcher method processAccountWithLock.

private Invoice processAccountWithLock(final boolean parkedAccount, final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull, @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
    final boolean isDryRun = dryRunArguments != null;
    final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
    LocalDate inputTargetDate = inputTargetDateMaybeNull;
    // A null inputTargetDate is only allowed in dryRun mode to have the system compute it
    if (inputTargetDate == null && !upcomingInvoiceDryRun) {
        inputTargetDate = clock.getUTCToday();
    }
    Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
    try {
        // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
        final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
        if (billingEvents.isEmpty()) {
            return null;
        }
        final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsForDryRun(dryRunArguments, billingEvents);
        final List<LocalDate> candidateTargetDates = (inputTargetDate != null) ? ImmutableList.<LocalDate>of(inputTargetDate) : getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
        for (final LocalDate curTargetDate : candidateTargetDates) {
            final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDate, billingEvents, isDryRun, context);
            if (invoice != null) {
                filterInvoiceItemsForDryRun(filteredSubscriptionIdsForDryRun, invoice);
                if (!isDryRun && parkedAccount) {
                    try {
                        log.info("Illegal invoicing state fixed for accountId='{}', unparking account", accountId);
                        parkedAccountsManager.unparkAccount(accountId, context);
                    } catch (final TagApiException ignored) {
                        log.warn("Unable to unpark account", ignored);
                    }
                }
                return invoice;
            }
        }
        return null;
    } catch (final CatalogApiException e) {
        log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
        return null;
    } catch (final AccountApiException e) {
        log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
        return null;
    } catch (final SubscriptionBaseApiException e) {
        log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
        return null;
    } catch (final InvoiceApiException e) {
        if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) {
            log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e);
            parkAccount(accountId, context);
        }
        throw e;
    }
}
Also used : InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) DefaultInvoice(org.killbill.billing.invoice.model.DefaultInvoice) Invoice(org.killbill.billing.invoice.api.Invoice) TagApiException(org.killbill.billing.util.api.TagApiException) BillingEventSet(org.killbill.billing.junction.BillingEventSet) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) AccountApiException(org.killbill.billing.account.api.AccountApiException) UUID(java.util.UUID) LocalDate(org.joda.time.LocalDate) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 14 with AccountApiException

use of org.killbill.billing.account.api.AccountApiException in project killbill by killbill.

the class CompletionTaskBase method tryToDoJanitorOperationWithAccountLock.

protected <T> T tryToDoJanitorOperationWithAccountLock(final JanitorIterationCallback callback, final InternalTenantContext internalTenantContext) throws LockFailedException {
    GlobalLock lock = null;
    try {
        final ImmutableAccountData account = accountInternalApi.getImmutableAccountDataByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
        lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), account.getId().toString(), paymentConfig.getMaxGlobalLockRetries());
        return callback.doIteration();
    } catch (final AccountApiException e) {
        log.warn("Error retrieving accountRecordId='{}'", internalTenantContext.getAccountRecordId(), e);
    } finally {
        if (lock != null) {
            lock.release();
        }
    }
    return null;
}
Also used : GlobalLock(org.killbill.commons.locker.GlobalLock) ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) AccountApiException(org.killbill.billing.account.api.AccountApiException)

Example 15 with AccountApiException

use of org.killbill.billing.account.api.AccountApiException in project killbill by killbill.

the class IncompletePaymentAttemptTask method doIteration.

@Override
public void doIteration(final PaymentAttemptModelDao attempt) {
    // We don't grab account lock here as the lock will be taken when calling the completeRun API.
    final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(attempt.getTenantRecordId(), attempt.getAccountRecordId());
    final CallContext callContext = createCallContext("AttemptCompletionJanitorTask", tenantContext);
    final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(attempt.getAccountId(), callContext);
    final List<PaymentTransactionModelDao> transactions = paymentDao.getPaymentTransactionsByExternalKey(attempt.getTransactionExternalKey(), tenantContext);
    final List<PaymentTransactionModelDao> filteredTransactions = ImmutableList.copyOf(Iterables.filter(transactions, new Predicate<PaymentTransactionModelDao>() {

        @Override
        public boolean apply(final PaymentTransactionModelDao input) {
            return input.getAttemptId().equals(attempt.getId());
        }
    }));
    // We only expect at most one transaction for a given attempt, but as a precaution we check for more; if this is the case we log a warn and continue processing the first one.
    if (filteredTransactions.size() > 1) {
        log.warn("Found {} transactions for paymentAttempt {}", filteredTransactions.size(), attempt.getId());
    }
    final PaymentTransactionModelDao transaction = filteredTransactions.isEmpty() ? null : filteredTransactions.get(0);
    if (transaction == null) {
        log.info("Moving attemptId='{}' to ABORTED", attempt.getId());
        paymentDao.updatePaymentAttempt(attempt.getId(), attempt.getTransactionId(), "ABORTED", internalCallContext);
        return;
    }
    // at which point the attempt can also be transition to a different state.
    if (transaction.getTransactionStatus() == TransactionStatus.UNKNOWN) {
        return;
    }
    try {
        log.info("Completing attemptId='{}'", attempt.getId());
        final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), tenantContext);
        // unclear
        final boolean isApiPayment = true;
        final PaymentStateControlContext paymentStateContext = new PaymentStateControlContext(attempt.toPaymentControlPluginNames(), isApiPayment, null, transaction.getPaymentId(), attempt.getPaymentExternalKey(), transaction.getId(), transaction.getTransactionExternalKey(), transaction.getTransactionType(), account, attempt.getPaymentMethodId(), transaction.getAmount(), transaction.getCurrency(), PluginPropertySerializer.deserialize(attempt.getPluginProperties()), internalCallContext, callContext);
        // Normally set by leavingState Callback
        paymentStateContext.setAttemptId(attempt.getId());
        // Normally set by raw state machine
        paymentStateContext.setPaymentTransactionModelDao(transaction);
        //
        // Will rerun the state machine with special callbacks to only make the executePluginOnSuccessCalls / executePluginOnFailureCalls calls
        // to the PaymentControlPluginApi plugin and transition the state.
        //
        pluginControlledPaymentAutomatonRunner.completeRun(paymentStateContext);
    } catch (final AccountApiException e) {
        log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
    } catch (final PluginPropertySerializerException e) {
        log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
    } catch (final PaymentApiException e) {
        log.warn("Error completing paymentAttemptId='{}'", attempt.getId(), e);
    }
}
Also used : Account(org.killbill.billing.account.api.Account) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PluginPropertySerializerException(org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) AccountApiException(org.killbill.billing.account.api.AccountApiException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) CallContext(org.killbill.billing.util.callcontext.CallContext) Predicate(com.google.common.base.Predicate) PaymentStateControlContext(org.killbill.billing.payment.core.sm.control.PaymentStateControlContext)

Aggregations

AccountApiException (org.killbill.billing.account.api.AccountApiException)36 UUID (java.util.UUID)17 Account (org.killbill.billing.account.api.Account)16 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)12 DefaultAccount (org.killbill.billing.account.api.DefaultAccount)10 ArrayList (java.util.ArrayList)7 ImmutableAccountData (org.killbill.billing.account.api.ImmutableAccountData)7 InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)7 PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)6 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)6 CallContext (org.killbill.billing.util.callcontext.CallContext)6 BigDecimal (java.math.BigDecimal)5 HashMap (java.util.HashMap)5 AccountModelDao (org.killbill.billing.account.dao.AccountModelDao)5 Invoice (org.killbill.billing.invoice.api.Invoice)5 AllowConcurrentEvents (com.google.common.eventbus.AllowConcurrentEvents)4 Subscribe (com.google.common.eventbus.Subscribe)4 LocalDate (org.joda.time.LocalDate)4 DefaultMutableAccountData (org.killbill.billing.account.api.DefaultMutableAccountData)4 PaymentMethod (org.killbill.billing.payment.api.PaymentMethod)4