Search in sources :

Example 16 with PaymentApiException

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);
    }
}
Also used : Matcher(java.util.regex.Matcher) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException)

Example 17 with PaymentApiException

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());
    }
}
Also used : Account(org.killbill.billing.account.api.Account) Payment(org.killbill.billing.payment.api.Payment) Invoice(org.killbill.billing.invoice.api.Invoice) AccountData(org.killbill.billing.account.api.AccountData) DefaultEntitlement(org.killbill.billing.entitlement.api.DefaultEntitlement) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) ExpectedInvoiceItemCheck(org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime) BigDecimal(java.math.BigDecimal) ExpectedPaymentCheck(org.killbill.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck) Test(org.testng.annotations.Test)

Example 18 with PaymentApiException

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);
}
Also used : Account(org.killbill.billing.account.api.Account) Invoice(org.killbill.billing.invoice.api.Invoice) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) ExpectedInvoiceItemCheck(org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime) BigDecimal(java.math.BigDecimal) ExpectedPaymentCheck(org.killbill.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) Handle(org.skife.jdbi.v2.Handle) PaymentTransaction(org.killbill.billing.payment.api.PaymentTransaction) Payment(org.killbill.billing.payment.api.Payment) AccountData(org.killbill.billing.account.api.AccountData) DefaultEntitlement(org.killbill.billing.entitlement.api.DefaultEntitlement) Test(org.testng.annotations.Test)

Example 19 with PaymentApiException

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);
}
Also used : PaymentMethod(org.killbill.billing.payment.api.PaymentMethod) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) UUID(java.util.UUID) Predicate(com.google.common.base.Predicate)

Example 20 with PaymentApiException

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);
}
Also used : PaymentAttemptModelDao(org.killbill.billing.payment.dao.PaymentAttemptModelDao) PluginProperty(org.killbill.billing.payment.api.PluginProperty) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PluginPropertySerializerException(org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException)

Aggregations

PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)57 UUID (java.util.UUID)20 Test (org.testng.annotations.Test)19 PaymentTransactionModelDao (org.killbill.billing.payment.dao.PaymentTransactionModelDao)14 Account (org.killbill.billing.account.api.Account)12 Payment (org.killbill.billing.payment.api.Payment)12 PaymentAttemptModelDao (org.killbill.billing.payment.dao.PaymentAttemptModelDao)11 BigDecimal (java.math.BigDecimal)10 OperationException (org.killbill.automaton.OperationException)10 PaymentModelDao (org.killbill.billing.payment.dao.PaymentModelDao)9 Predicate (com.google.common.base.Predicate)7 PaymentControlApiException (org.killbill.billing.control.plugin.api.PaymentControlApiException)7 PluginDispatcherReturnType (org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType)7 PaymentPluginApi (org.killbill.billing.payment.plugin.api.PaymentPluginApi)7 LinkedList (java.util.LinkedList)6 AccountApiException (org.killbill.billing.account.api.AccountApiException)6 Invoice (org.killbill.billing.invoice.api.Invoice)6 PluginProperty (org.killbill.billing.payment.api.PluginProperty)6 PaymentPluginApiException (org.killbill.billing.payment.plugin.api.PaymentPluginApiException)6 DateTime (org.joda.time.DateTime)5