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