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);
}
}
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);
}
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);
}
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);
}
});
}
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);
}
}
Aggregations