Search in sources :

Example 11 with InternalCallContext

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

the class TestStateMachineConfigCache method testExistingTenantStateMachineConfig.

@Test(groups = "fast")
public void testExistingTenantStateMachineConfig() throws PaymentApiException, URISyntaxException, IOException {
    final String pluginName = UUID.randomUUID().toString();
    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();
    Mockito.when(tenantInternalApi.getPluginPaymentStateMachineConfig(Mockito.eq(pluginName), Mockito.any(InternalTenantContext.class))).thenAnswer(new Answer<String>() {

        @Override
        public String answer(final InvocationOnMock invocation) throws Throwable {
            if (shouldThrow.get()) {
                throw new RuntimeException();
            }
            final InternalTenantContext internalContext = (InternalTenantContext) invocation.getArguments()[1];
            if (multiTenantRecordId.equals(internalContext.getTenantRecordId())) {
                return new String(ByteStreams.toByteArray(UriAccessor.accessUri(Resources.getResource(PaymentModule.DEFAULT_STATE_MACHINE_PAYMENT_XML).toExternalForm())));
            } else if (otherMultiTenantRecordId.equals(internalContext.getTenantRecordId())) {
                return new String(ByteStreams.toByteArray(UriAccessor.accessUri(Resources.getResource(PaymentModule.DEFAULT_STATE_MACHINE_RETRY_XML).toExternalForm())));
            } else {
                return null;
            }
        }
    });
    // Verify the lookup for a non-cached tenant. No system config is set yet but EhCacheStateMachineConfigCache returns a default empty one
    final StateMachineConfig defaultStateMachineConfig = stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, differentMultiTenantContext);
    Assert.assertNotNull(defaultStateMachineConfig);
    // Make sure the cache loader isn't invoked, see https://github.com/killbill/killbill/issues/300
    shouldThrow.set(true);
    final StateMachineConfig defaultStateMachineConfig2 = stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, differentMultiTenantContext);
    Assert.assertNotNull(defaultStateMachineConfig2);
    Assert.assertEquals(defaultStateMachineConfig2, defaultStateMachineConfig);
    shouldThrow.set(false);
    // Verify the lookup for this tenant
    Assert.assertEquals(stateMachineConfigCache.getPaymentStateMachineConfig(UUID.randomUUID().toString(), multiTenantContext), defaultStateMachineConfig);
    final StateMachineConfig result = stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, multiTenantContext);
    Assert.assertNotNull(result);
    Assert.assertNotEquals(result, defaultStateMachineConfig);
    Assert.assertEquals(result.getStateMachines().length, 8);
    // Verify the lookup for another tenant
    Assert.assertEquals(stateMachineConfigCache.getPaymentStateMachineConfig(UUID.randomUUID().toString(), otherMultiTenantContext), defaultStateMachineConfig);
    final StateMachineConfig otherResult = stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, otherMultiTenantContext);
    Assert.assertNotNull(otherResult);
    Assert.assertEquals(otherResult.getStateMachines().length, 1);
    shouldThrow.set(true);
    // Verify the lookup for this tenant
    Assert.assertEquals(stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, multiTenantContext), 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(stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, sameMultiTenantContext), result);
    // Verify the lookup with the other tenant
    Assert.assertEquals(stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, otherMultiTenantContext), otherResult);
    // Verify clearing the cache works
    stateMachineConfigCache.clearPaymentStateMachineConfig(pluginName, multiTenantContext);
    Assert.assertEquals(stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, otherMultiTenantContext), otherResult);
    try {
        stateMachineConfigCache.getPaymentStateMachineConfig(pluginName, multiTenantContext);
        Assert.fail();
    } catch (final CacheException exception) {
        Assert.assertTrue(exception.getCause() instanceof RuntimeException);
    }
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CacheException(net.sf.ehcache.CacheException) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) InvocationOnMock(org.mockito.invocation.InvocationOnMock) StateMachineConfig(org.killbill.automaton.StateMachineConfig) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) Test(org.testng.annotations.Test)

Example 12 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 13 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 14 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 15 with InternalCallContext

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

the class DefaultSubscriptionBaseService method processEventReady.

@Override
public void processEventReady(final SubscriptionBaseEvent event, final int seqId, final InternalCallContext context) {
    if (!event.isActive()) {
        return;
    }
    try {
        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) dao.getSubscriptionFromId(event.getSubscriptionId(), context);
        if (subscription == null) {
            log.warn("Error retrieving subscriptionId='{}'", event.getSubscriptionId());
            return;
        }
        final SubscriptionBaseTransitionData transition = subscription.getTransitionFromEvent(event, seqId);
        if (transition == null) {
            log.warn("Skipping event ='{}', no matching transition was built", event.getType());
            return;
        }
        boolean eventSent = false;
        if (event.getType() == EventType.PHASE) {
            eventSent = onPhaseEvent(subscription, event, context);
        } else if (event.getType() == EventType.API_USER && subscription.getCategory() == ProductCategory.BASE) {
            final CallContext callContext = internalCallContextFactory.createCallContext(context);
            eventSent = onBasePlanEvent(subscription, event, callContext);
        } else if (event.getType() == EventType.BCD_UPDATE) {
            eventSent = false;
        }
        if (!eventSent) {
            // Methods above invoking the DAO will send this event directly from the transaction
            final BusEvent busEvent = new DefaultEffectiveSubscriptionEvent(transition, subscription.getAlignStartDate(), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
            eventBus.post(busEvent);
        }
    } catch (final EventBusException e) {
        log.warn("Failed to post event {}", event, e);
    } catch (final CatalogApiException e) {
        log.warn("Failed to post event {}", event, e);
    }
}
Also used : SubscriptionBaseTransitionData(org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionData) DefaultEffectiveSubscriptionEvent(org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) EventBusException(org.killbill.bus.api.PersistentBus.EventBusException) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) CallContext(org.killbill.billing.util.callcontext.CallContext) BusEvent(org.killbill.bus.api.BusEvent)

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