use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.
the class TenantStateMachineConfigCacheLoader method compute.
@Override
public Object compute(final String key, final CacheLoaderArgument cacheLoaderArgument) {
final String[] parts = key.split(CacheControllerDispatcher.CACHE_KEY_SEPARATOR);
final String rawKey = parts[0];
final Matcher matcher = PATTERN.matcher(rawKey);
if (!matcher.matches()) {
throw new IllegalArgumentException("Unexpected key " + rawKey);
}
final String pluginName = matcher.group(1);
final String tenantRecordId = parts[1];
final LoaderCallback callback = (LoaderCallback) cacheLoaderArgument.getArgs()[0];
final InternalTenantContext internalTenantContext = new InternalTenantContext(Long.valueOf(tenantRecordId));
final String stateMachineConfigXML = tenantApi.getPluginPaymentStateMachineConfig(pluginName, internalTenantContext);
if (stateMachineConfigXML == null) {
return null;
}
try {
log.info("Loading config state machine cache for pluginName='{}', tenantRecordId='{}'", pluginName, internalTenantContext.getTenantRecordId());
return callback.loadStateMachineConfig(stateMachineConfigXML);
} catch (final PaymentApiException e) {
throw new IllegalStateException(String.format("Failed to de-serialize state machine config for tenantRecordId='%s'", internalTenantContext.getTenantRecordId()), e);
}
}
use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.
the class TestInvoicePayment method testMultiplePartialPaymentsWithSameExternalKeys.
@Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/1230")
public void testMultiplePartialPaymentsWithSameExternalKeys() throws Exception {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
final AccountData accountData = getAccountData(0);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
// Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay the initial invoice
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
// 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE);
Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
// Invoice is not paid
Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
// Trigger partial payment
final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), BigDecimal.TEN, TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
Assert.assertEquals(payment1.getTransactions().size(), 1);
Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
// Trigger another partial payment with same external keys
try {
createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), payment1.getExternalKey(), payment1.getTransactions().get(0).getExternalKey(), NextEvent.INVOICE_PAYMENT_ERROR);
Assert.fail("Shouldn't have been able to create a payment");
} catch (final PaymentApiException e) {
Assert.assertEquals(e.getCode(), (int) ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
}
}
use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.
the class TestInvoicePayment method testWithIncompletePaymentAttempt.
@Test(groups = "slow")
public void testWithIncompletePaymentAttempt() throws Exception {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
final AccountData accountData = getAccountData(0);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);
final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
// 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
// Invoice is fully paid
final Payment originalPayment = paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), new BigDecimal("249.95"), TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
Assert.assertEquals(originalPayment.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(originalPayment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(originalPayment.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
final PaymentTransaction originalTransaction = originalPayment.getTransactions().get(0);
// Let 's hack invoice_payment table by hand to simulate a non completion of the payment (onSuccessCall was never called)
dbi.withHandle(new HandleCallback<Void>() {
@Override
public Void withHandle(final Handle handle) throws Exception {
handle.execute("update invoice_payments set success = false where payment_cookie_id = ?", originalTransaction.getExternalKey());
return null;
}
});
final Invoice updateInvoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
// Invoice now shows as unpaid
Assert.assertEquals(updateInvoice2.getBalance().compareTo(originalPayment.getPurchasedAmount()), 0);
Assert.assertEquals(updateInvoice2.getPayments().size(), 1);
Assert.assertEquals(updateInvoice2.getPayments().get(0).getPaymentCookieId(), originalTransaction.getExternalKey());
//
// Now trigger invoice payment again (no new payment should be made as code should detect broken state and fix it by itself)
// We expect an INVOICE_PAYMENT that indicates the invoice was repaired, and also an exception because plugin aborts payment call since there is nothing to do.
//
busHandler.pushExpectedEvents(NextEvent.INVOICE_PAYMENT);
try {
invoicePaymentApi.createPurchaseForInvoicePayment(account, updateInvoice2.getId(), account.getPaymentMethodId(), null, updateInvoice2.getBalance(), updateInvoice2.getCurrency(), null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), PAYMENT_OPTIONS, callContext);
Assert.fail("The payment should not succeed (and yet it will repair the broken state....)");
} catch (final PaymentApiException expected) {
Assert.assertEquals(expected.getCode(), ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode());
}
assertListenerStatus();
final Invoice updateInvoice3 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
Assert.assertEquals(updateInvoice3.getBalance().compareTo(BigDecimal.ZERO), 0);
Assert.assertEquals(updateInvoice3.getPayments().size(), 1);
Assert.assertEquals(updateInvoice3.getPayments().get(0).getPaymentCookieId(), originalTransaction.getExternalKey());
Assert.assertTrue(updateInvoice3.getPayments().get(0).isSuccess());
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
Assert.assertEquals(payments.size(), 1);
Assert.assertEquals(payments.get(0).getTransactions().size(), 1);
}
use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.
the class ComboPaymentResource method getOrCreatePaymentMethod.
protected UUID getOrCreatePaymentMethod(final Account account, @Nullable final PaymentMethodJson paymentMethodJson, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
// No info about payment method was passed, we default to null payment Method ID (which is allowed to be overridden in payment control plugins)
if (paymentMethodJson == null || paymentMethodJson.getPluginName() == null) {
return null;
}
// Get all payment methods for account
final List<PaymentMethod> accountPaymentMethods = paymentApi.getAccountPaymentMethods(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
// If we were specified a paymentMethod id and we find it, we return it
if (paymentMethodJson.getPaymentMethodId() != null) {
final UUID match = paymentMethodJson.getPaymentMethodId();
if (Iterables.any(accountPaymentMethods, new Predicate<PaymentMethod>() {
@Override
public boolean apply(final PaymentMethod input) {
return input.getId().equals(match);
}
})) {
return match;
}
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, match);
}
// If we were specified a paymentMethod externalKey and we find it, we return it
if (paymentMethodJson.getExternalKey() != null) {
final PaymentMethod match = Iterables.tryFind(accountPaymentMethods, new Predicate<PaymentMethod>() {
@Override
public boolean apply(final PaymentMethod input) {
return input.getExternalKey().equals(paymentMethodJson.getExternalKey());
}
}).orNull();
if (match != null) {
return match.getId();
}
}
// Only set as default if this is the first paymentMethod on the account
final boolean isDefault = accountPaymentMethods.isEmpty();
final PaymentMethod paymentData = paymentMethodJson.toPaymentMethod(account.getId());
return paymentApi.addPaymentMethod(account, paymentMethodJson.getExternalKey(), paymentMethodJson.getPluginName(), isDefault, paymentData.getPluginDetail(), pluginProperties, callContext);
}
use of org.killbill.billing.payment.api.PaymentApiException in project killbill by killbill.
the class PluginControlPaymentProcessor method notifyPendingPaymentOfStateChanged.
public Payment notifyPendingPaymentOfStateChanged(final boolean isApiPayment, final Account account, final UUID paymentTransactionId, final boolean isSuccess, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(paymentTransactionId, internalCallContext);
final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttemptByTransactionExternalKey(paymentTransactionModelDao.getTransactionExternalKey(), internalCallContext);
final PaymentAttemptModelDao attempt = Iterables.find(attempts, new Predicate<PaymentAttemptModelDao>() {
@Override
public boolean apply(final PaymentAttemptModelDao input) {
return input.getTransactionId().equals(paymentTransactionId);
}
});
final Iterable<PluginProperty> pluginProperties;
try {
pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
} catch (final PluginPropertySerializerException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, String.format("Unable to deserialize payment attemptId='%s' properties", attempt.getId()));
}
return pluginControlledPaymentAutomatonRunner.run(isApiPayment, isSuccess, paymentTransactionModelDao.getTransactionType(), ControlOperation.NOTIFICATION_OF_STATE_CHANGE, account, attempt.getPaymentMethodId(), paymentTransactionModelDao.getPaymentId(), attempt.getPaymentExternalKey(), paymentTransactionId, paymentTransactionModelDao.getTransactionExternalKey(), paymentTransactionModelDao.getAmount(), paymentTransactionModelDao.getCurrency(), null, pluginProperties, paymentControlPluginNames, callContext, internalCallContext);
}
Aggregations