Search in sources :

Example 6 with InternalCallContext

use of org.killbill.billing.callcontext.InternalCallContext in project killbill by killbill.

the class TestEhCacheCatalogCache method testExistingTenantCatalog.

//
// Verify CatalogCache returns per tenant catalog:
// 1. We first mock TenantInternalApi to return a different catalog than the default one
// 2. We then mock TenantInternalApi to throw RuntimeException which means catalog was cached and there was no additional call
//    to the TenantInternalApi api (otherwise test would fail with RuntimeException)
//
@Test(groups = "fast")
public void testExistingTenantCatalog() throws CatalogApiException, URISyntaxException, IOException {
    final InternalCallContext differentMultiTenantContext = Mockito.mock(InternalCallContext.class);
    Mockito.when(differentMultiTenantContext.getTenantRecordId()).thenReturn(55667788L);
    final AtomicBoolean shouldThrow = new AtomicBoolean(false);
    final Long multiTenantRecordId = multiTenantContext.getTenantRecordId();
    final Long otherMultiTenantRecordId = otherMultiTenantContext.getTenantRecordId();
    final InputStream tenantInputCatalog = UriAccessor.accessUri(new URI(Resources.getResource("SpyCarAdvanced.xml").toExternalForm()));
    final String tenantCatalogXML = CharStreams.toString(new InputStreamReader(tenantInputCatalog, "UTF-8"));
    final InputStream otherTenantInputCatalog = UriAccessor.accessUri(new URI(Resources.getResource("SpyCarBasic.xml").toExternalForm()));
    final String otherTenantCatalogXML = CharStreams.toString(new InputStreamReader(otherTenantInputCatalog, "UTF-8"));
    Mockito.when(tenantInternalApi.getTenantCatalogs(Mockito.any(InternalTenantContext.class))).thenAnswer(new Answer<List<String>>() {

        @Override
        public List<String> answer(final InvocationOnMock invocation) throws Throwable {
            if (shouldThrow.get()) {
                throw new RuntimeException();
            }
            final InternalTenantContext internalContext = (InternalTenantContext) invocation.getArguments()[0];
            if (multiTenantRecordId.equals(internalContext.getTenantRecordId())) {
                return ImmutableList.<String>of(tenantCatalogXML);
            } else if (otherMultiTenantRecordId.equals(internalContext.getTenantRecordId())) {
                return ImmutableList.<String>of(otherTenantCatalogXML);
            } else {
                return ImmutableList.<String>of();
            }
        }
    });
    // Verify the lookup for a non-cached tenant. No system config is set yet but EhCacheCatalogCache returns a default empty one
    VersionedCatalog differentResult = catalogCache.getCatalog(true, true, differentMultiTenantContext);
    Assert.assertNotNull(differentResult);
    Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
    // Make sure the cache loader isn't invoked, see https://github.com/killbill/killbill/issues/300
    shouldThrow.set(true);
    differentResult = catalogCache.getCatalog(true, true, differentMultiTenantContext);
    Assert.assertNotNull(differentResult);
    Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
    shouldThrow.set(false);
    // Set a default config
    catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
    // Verify the lookup for this tenant
    final VersionedCatalog result = catalogCache.getCatalog(true, true, multiTenantContext);
    Assert.assertNotNull(result);
    final Collection<Product> products = result.getProducts(clock.getUTCNow());
    Assert.assertEquals(products.size(), 6);
    // Verify the lookup for another tenant
    final VersionedCatalog otherResult = catalogCache.getCatalog(true, true, otherMultiTenantContext);
    Assert.assertNotNull(otherResult);
    final Collection<Product> otherProducts = otherResult.getProducts(clock.getUTCNow());
    Assert.assertEquals(otherProducts.size(), 3);
    shouldThrow.set(true);
    // Verify the lookup for this tenant
    final VersionedCatalog result2 = catalogCache.getCatalog(true, true, multiTenantContext);
    Assert.assertEquals(result2, result);
    // Verify the lookup with another context for the same tenant
    final InternalCallContext sameMultiTenantContext = Mockito.mock(InternalCallContext.class);
    Mockito.when(sameMultiTenantContext.getAccountRecordId()).thenReturn(9102L);
    Mockito.when(sameMultiTenantContext.getTenantRecordId()).thenReturn(multiTenantRecordId);
    Assert.assertEquals(catalogCache.getCatalog(true, true, sameMultiTenantContext), result);
    // Verify the lookup with the other tenant
    Assert.assertEquals(catalogCache.getCatalog(true, true, otherMultiTenantContext), otherResult);
}
Also used : InputStreamReader(java.io.InputStreamReader) InputStream(java.io.InputStream) DefaultProduct(org.killbill.billing.catalog.DefaultProduct) Product(org.killbill.billing.catalog.api.Product) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) URI(java.net.URI) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) VersionedCatalog(org.killbill.billing.catalog.VersionedCatalog) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) InvocationOnMock(org.mockito.invocation.InvocationOnMock) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) StandaloneCatalogWithPriceOverride(org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride) Test(org.testng.annotations.Test)

Example 7 with InternalCallContext

use of org.killbill.billing.callcontext.InternalCallContext in project killbill by killbill.

the class InvoicePaymentControlPluginApi method onFailureCall.

@Override
public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
    final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
    final TransactionType transactionType = paymentControlContext.getTransactionType();
    DateTime nextRetryDate = null;
    switch(transactionType) {
        case PURCHASE:
            final UUID invoiceId = getInvoiceId(pluginProperties);
            try {
                log.debug("Notifying invoice of failed payment: id={}, amount={}, currency={}, invoiceId={}", paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), paymentControlContext.getCurrency(), invoiceId);
                invoiceApi.recordPaymentAttemptCompletion(invoiceId, BigDecimal.ZERO, paymentControlContext.getCurrency(), // processed currency may be null so we use currency; processed currency will be updated if/when payment succeeds
                paymentControlContext.getCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), paymentControlContext.getCreatedDate(), false, internalContext);
            } catch (final InvoiceApiException e) {
                log.error("InvoicePaymentControlPluginApi onFailureCall failed ton update invoice for attemptId = " + paymentControlContext.getAttemptPaymentId() + ", transactionType  = " + transactionType, e);
            }
            nextRetryDate = computeNextRetryDate(paymentControlContext.getPaymentExternalKey(), paymentControlContext.isApiPayment(), internalContext);
            break;
        case CREDIT:
        case REFUND:
            // We don't retry REFUND
            break;
        case CHARGEBACK:
            try {
                invoiceApi.recordChargebackReversal(paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), internalContext);
            } catch (final InvoiceApiException e) {
                log.warn("onFailureCall failed for attemptId='{}', transactionType='{}'", paymentControlContext.getAttemptPaymentId(), transactionType, e);
            }
            break;
        default:
            throw new IllegalStateException("Unexpected transactionType " + transactionType);
    }
    return new DefaultFailureCallResult(nextRetryDate);
}
Also used : DefaultFailureCallResult(org.killbill.billing.payment.retry.DefaultFailureCallResult) TransactionType(org.killbill.billing.payment.api.TransactionType) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) UUID(java.util.UUID) DateTime(org.joda.time.DateTime)

Example 8 with InternalCallContext

use of org.killbill.billing.callcontext.InternalCallContext in project killbill by killbill.

the class BaseRetryService method initialize.

@Override
public void initialize() throws NotificationQueueAlreadyExists {
    retryQueue = notificationQueueService.createNotificationQueue(DefaultPaymentService.SERVICE_NAME, getQueueName(), new NotificationQueueHandler() {

        @Override
        public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
            if (!(notificationKey instanceof PaymentRetryNotificationKey)) {
                log.error("Payment service got an unexpected notification type {}", notificationKey.getClass().getName());
                return;
            }
            final PaymentRetryNotificationKey key = (PaymentRetryNotificationKey) notificationKey;
            final InternalCallContext callContext = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, paymentRetryService, CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
            retryPaymentTransaction(key.getAttemptId(), key.getPaymentControlPluginNames(), callContext);
        }
    });
}
Also used : NotificationEvent(org.killbill.notificationq.api.NotificationEvent) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) UUID(java.util.UUID) NotificationQueueHandler(org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler) DateTime(org.joda.time.DateTime)

Example 9 with InternalCallContext

use of org.killbill.billing.callcontext.InternalCallContext in project killbill by killbill.

the class PaymentGatewayProcessor method processNotification.

public GatewayNotification processNotification(final boolean shouldDispatch, final String notification, final UUID paymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
    final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(paymentMethodId, ObjectType.PAYMENT_METHOD, callContext);
    final String pluginName = getPaymentMethodById(paymentMethodId, true, internalCallContext).getPluginName();
    return processNotification(shouldDispatch, notification, pluginName, properties, callContext);
}
Also used : InternalCallContext(org.killbill.billing.callcontext.InternalCallContext)

Example 10 with InternalCallContext

use of org.killbill.billing.callcontext.InternalCallContext in project killbill by killbill.

the class PluginControlPaymentProcessor method retryPaymentTransaction.

public void retryPaymentTransaction(final UUID attemptId, final List<String> paymentControlPluginNames, final InternalCallContext internalCallContext) {
    final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
    log.info("Retrying attemptId='{}', paymentExternalKey='{}', transactionExternalKey='{}'. paymentControlPluginNames='{}'", attemptId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), paymentControlPluginNames);
    final PaymentModelDao paymentModelDao = paymentDao.getPaymentByExternalKey(attempt.getPaymentExternalKey(), internalCallContext);
    final UUID paymentId = paymentModelDao != null ? paymentModelDao.getId() : null;
    final CallContext callContext = buildCallContext(internalCallContext);
    final String transactionType = TransactionType.PURCHASE.name();
    Account account = null;
    Payment payment = null;
    PaymentTransaction paymentTransaction = null;
    try {
        account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
        final State state = paymentControlStateMachineHelper.getState(attempt.getStateName());
        final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
        logEnterAPICall(log, transactionType, account, attempt.getPaymentMethodId(), paymentId, null, attempt.getAmount(), attempt.getCurrency(), attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), null, paymentControlPluginNames);
        payment = pluginControlledPaymentAutomatonRunner.run(state, false, attempt.getTransactionType(), ControlOperation.valueOf(attempt.getTransactionType().toString()), account, attempt.getPaymentMethodId(), paymentId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), attempt.getAmount(), attempt.getCurrency(), pluginProperties, paymentControlPluginNames, callContext, internalCallContext);
        paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()), new Predicate<PaymentTransaction>() {

            @Override
            public boolean apply(final PaymentTransaction input) {
                return attempt.getTransactionExternalKey().equals(input.getExternalKey());
            }
        });
    } catch (final AccountApiException e) {
        log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
    } catch (final PaymentApiException e) {
        // Log exception unless nothing left to be paid
        if (e.getCode() == ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode() && paymentControlPluginNames != null && paymentControlPluginNames.size() == 1 && InvoicePaymentControlPluginApi.PLUGIN_NAME.equals(paymentControlPluginNames.get(0))) {
            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'. Invoice has already been paid", attemptId, toPluginNamesOnError(paymentControlPluginNames));
        } else {
            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
        }
    } catch (final PluginPropertySerializerException e) {
        log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
    } catch (final MissingEntryException e) {
        log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
    } finally {
        logExitAPICall(log, transactionType, account, payment != null ? payment.getPaymentMethodId() : null, payment != null ? payment.getId() : null, paymentTransaction != null ? paymentTransaction.getId() : null, paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null, paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null, payment != null ? payment.getExternalKey() : null, paymentTransaction != null ? paymentTransaction.getExternalKey() : null, paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null, paymentControlPluginNames, null);
    }
}
Also used : PaymentAttemptModelDao(org.killbill.billing.payment.dao.PaymentAttemptModelDao) Account(org.killbill.billing.account.api.Account) PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) PluginPropertySerializerException(org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) CallContext(org.killbill.billing.util.callcontext.CallContext) Predicate(com.google.common.base.Predicate) PaymentTransaction(org.killbill.billing.payment.api.PaymentTransaction) PluginProperty(org.killbill.billing.payment.api.PluginProperty) Payment(org.killbill.billing.payment.api.Payment) State(org.killbill.automaton.State) AccountApiException(org.killbill.billing.account.api.AccountApiException) MissingEntryException(org.killbill.automaton.MissingEntryException) UUID(java.util.UUID)

Aggregations

InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)110 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)26 DateTime (org.joda.time.DateTime)25 UUID (java.util.UUID)24 ArrayList (java.util.ArrayList)21 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)17 InvoiceApiException (org.killbill.billing.invoice.api.InvoiceApiException)14 CallContext (org.killbill.billing.util.callcontext.CallContext)14 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)13 WithEntitlementPlugin (org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin)13 EntitlementContext (org.killbill.billing.entitlement.plugin.api.EntitlementContext)13 AccountApiException (org.killbill.billing.account.api.AccountApiException)12 DefaultBlockingState (org.killbill.billing.junction.DefaultBlockingState)12 NotificationEvent (org.killbill.notificationq.api.NotificationEvent)9 BigDecimal (java.math.BigDecimal)8 LinkedList (java.util.LinkedList)8 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)8 Invoice (org.killbill.billing.invoice.api.Invoice)8 AllowConcurrentEvents (com.google.common.eventbus.AllowConcurrentEvents)7 Subscribe (com.google.common.eventbus.Subscribe)7