Search in sources :

Example 86 with PluginProperty

use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.

the class TestJanitor method testCreateSuccessRefundPaymentControlWithItemAdjustments.

@Test(groups = "slow")
public void testCreateSuccessRefundPaymentControlWithItemAdjustments() throws Exception {
    final BigDecimal requestedAmount = BigDecimal.TEN;
    final UUID subscriptionId = UUID.randomUUID();
    final UUID bundleId = UUID.randomUUID();
    final LocalDate now = clock.getUTCToday();
    testListener.pushExpectedEvent(NextEvent.INVOICE);
    final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
    testListener.assertListenerStatus();
    final String paymentExternalKey = invoice.getId().toString();
    final String transactionExternalKey = "craboom";
    final String transactionExternalKey2 = "qwerty";
    final InvoiceItem invoiceItem = new MockRecurringInvoiceItem(invoice.getId(), account.getId(), subscriptionId, bundleId, "test plan", "test phase", null, now, now.plusMonths(1), requestedAmount, new BigDecimal("1.0"), Currency.USD);
    invoice.addInvoiceItem(invoiceItem);
    testListener.pushExpectedEvent(NextEvent.PAYMENT);
    final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey, createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
    testListener.assertListenerStatus();
    final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>();
    final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>();
    uuidBigDecimalHashMap.put(invoiceItem.getId(), new BigDecimal("1.0"));
    final PluginProperty refundIdsProp = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
    refundProperties.add(refundIdsProp);
    testListener.pushExpectedEvent(NextEvent.PAYMENT);
    final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, transactionExternalKey2, refundProperties, INVOICE_PAYMENT, callContext);
    testListener.assertListenerStatus();
    assertEquals(payment2.getTransactions().size(), 2);
    PaymentTransaction refundTransaction = payment2.getTransactions().get(1);
    assertEquals(refundTransaction.getTransactionType(), TransactionType.REFUND);
    final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
    assertEquals(attempts.size(), 2);
    final PaymentAttemptModelDao refundAttempt = attempts.get(1);
    assertEquals(refundAttempt.getTransactionType(), TransactionType.REFUND);
    // Ok now the fun part starts... we modify the attempt state to be 'INIT' and wait the the Janitor to do its job.
    paymentDao.updatePaymentAttempt(refundAttempt.getId(), refundAttempt.getTransactionId(), "INIT", internalCallContext);
    final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
    assertEquals(attempt2.getStateName(), "INIT");
    clock.addDays(1);
    await().atMost(TIMEOUT, TimeUnit.SECONDS).until(new Callable<Boolean>() {

        @Override
        public Boolean call() throws Exception {
            final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
            return "SUCCESS".equals(attempt3.getStateName());
        }
    });
}
Also used : PaymentAttemptModelDao(org.killbill.billing.payment.dao.PaymentAttemptModelDao) Invoice(org.killbill.billing.invoice.api.Invoice) InvoiceItem(org.killbill.billing.invoice.api.InvoiceItem) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) LocalDate(org.joda.time.LocalDate) BigDecimal(java.math.BigDecimal) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) PaymentApiException(org.killbill.billing.payment.api.PaymentApiException) EventBusException(org.killbill.bus.api.PersistentBus.EventBusException) PaymentTransaction(org.killbill.billing.payment.api.PaymentTransaction) PluginProperty(org.killbill.billing.payment.api.PluginProperty) Payment(org.killbill.billing.payment.api.Payment) UUID(java.util.UUID) Test(org.testng.annotations.Test)

Example 87 with PluginProperty

use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.

the class TestJanitor method createPropertiesForInvoice.

private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
    final List<PluginProperty> result = new ArrayList<PluginProperty>();
    result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
    return result;
}
Also used : PluginProperty(org.killbill.billing.payment.api.PluginProperty) ArrayList(java.util.ArrayList)

Example 88 with PluginProperty

use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.

the class TestRetryService method createPropertiesForInvoice.

private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
    final List<PluginProperty> result = new ArrayList<PluginProperty>();
    result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
    return result;
}
Also used : PluginProperty(org.killbill.billing.payment.api.PluginProperty) ArrayList(java.util.ArrayList)

Example 89 with PluginProperty

use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.

the class InvoicePaymentControlPluginApi method onSuccessCall.

@Override
public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
    final TransactionType transactionType = paymentControlContext.getTransactionType();
    Preconditions.checkArgument(transactionType == TransactionType.PURCHASE || transactionType == TransactionType.REFUND || transactionType == TransactionType.CHARGEBACK || transactionType == TransactionType.CREDIT);
    final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
    try {
        final InvoicePayment existingInvoicePayment;
        switch(transactionType) {
            case PURCHASE:
                final UUID invoiceId = getInvoiceId(pluginProperties);
                existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
                if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
                    // Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
                    log.info("onSuccessCall was already completed for purchase paymentId='{}'", paymentControlContext.getPaymentId());
                } else {
                    final BigDecimal invoicePaymentAmount;
                    if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
                        invoicePaymentAmount = paymentControlContext.getProcessedAmount();
                    } else {
                        log.warn("processedCurrency='{}' of invoice paymentId='{}' doesn't match invoice currency='{}', assuming it is a full payment", paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
                        invoicePaymentAmount = paymentControlContext.getAmount();
                    }
                    final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(paymentControlContext.getTransactionId(), internalContext);
                    // If it's not SUCCESS, it is PENDING
                    final boolean success = paymentTransactionModelDao.getTransactionStatus() == TransactionStatus.SUCCESS;
                    log.debug("Notifying invoice of {} paymentId='{}', amount='{}', currency='{}', invoiceId='{}'", success ? "successful" : "pending", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
                    // For PENDING payments, the attempt will be kept as unsuccessful and an InvoicePaymentErrorInternalEvent sent on the bus (e.g. for Overdue)
                    invoiceApi.recordPaymentAttemptCompletion(invoiceId, invoicePaymentAmount, paymentControlContext.getCurrency(), paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), paymentControlContext.getCreatedDate(), success, internalContext);
                }
                break;
            case REFUND:
                final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
                final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
                final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
                invoiceApi.recordRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
                break;
            case CHARGEBACK:
                existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
                if (existingInvoicePayment != null) {
                    // We don't support partial chargebacks (yet?)
                    log.info("onSuccessCall was already completed for chargeback paymentId='{}'", paymentControlContext.getPaymentId());
                } else {
                    final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
                    final BigDecimal amount;
                    final Currency currency;
                    if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getProcessedCurrency()) && paymentControlContext.getProcessedAmount() != null) {
                        amount = paymentControlContext.getProcessedAmount();
                        currency = paymentControlContext.getProcessedCurrency();
                    } else if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getCurrency()) && paymentControlContext.getAmount() != null) {
                        amount = paymentControlContext.getAmount();
                        currency = paymentControlContext.getCurrency();
                    } else {
                        amount = linkedInvoicePayment.getAmount();
                        currency = linkedInvoicePayment.getCurrency();
                    }
                    invoiceApi.recordChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), amount, currency, internalContext);
                }
                break;
            case CREDIT:
                final Map<UUID, BigDecimal> idWithAmountMap = extractIdsWithAmountFromProperties(pluginProperties);
                final PluginProperty properties = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
                final boolean isInvoiceAdjusted = properties != null ? Boolean.valueOf((String) properties.getValue()) : false;
                final PluginProperty legacyPayment = getPluginProperty(pluginProperties, PROP_IPCD_PAYMENT_ID);
                final UUID paymentId = legacyPayment != null ? (UUID) legacyPayment.getValue() : paymentControlContext.getPaymentId();
                invoiceApi.recordRefund(paymentId, paymentControlContext.getAmount(), isInvoiceAdjusted, idWithAmountMap, paymentControlContext.getTransactionExternalKey(), internalContext);
                break;
            default:
                throw new IllegalStateException("Unexpected transactionType " + transactionType);
        }
    } catch (final InvoiceApiException e) {
        log.warn("onSuccessCall failed for attemptId='{}', transactionType='{}'", paymentControlContext.getAttemptPaymentId(), transactionType, e);
    }
    return new DefaultOnSuccessPaymentControlResult();
}
Also used : InvoicePayment(org.killbill.billing.invoice.api.InvoicePayment) TransactionType(org.killbill.billing.payment.api.TransactionType) DefaultOnSuccessPaymentControlResult(org.killbill.billing.payment.retry.DefaultOnSuccessPaymentControlResult) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) BigDecimal(java.math.BigDecimal) PluginProperty(org.killbill.billing.payment.api.PluginProperty) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) Currency(org.killbill.billing.catalog.api.Currency) UUID(java.util.UUID)

Example 90 with PluginProperty

use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.

the class TestPaymentOperation method setUp.

private void setUp(final PaymentPluginStatus paymentPluginStatus) throws Exception {
    final GlobalLocker locker = new MemoryGlobalLocker();
    final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(1, paymentExecutors);
    paymentStateContext = new PaymentStateContext(true, UUID.randomUUID(), null, null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), TransactionType.CAPTURE, Mockito.mock(Account.class), UUID.randomUUID(), new BigDecimal("192.3920111"), Currency.BRL, null, null, false, null, ImmutableList.<PluginProperty>of(), internalCallContext, callContext);
    final PaymentMethodModelDao paymentMethodModelDao = new PaymentMethodModelDao(paymentStateContext.getPaymentMethodId(), UUID.randomUUID().toString(), clock.getUTCNow(), clock.getUTCNow(), paymentStateContext.getAccount().getId(), MockPaymentProviderPlugin.PLUGIN_NAME, true);
    final PaymentDao paymentDao = Mockito.mock(PaymentDao.class);
    Mockito.when(paymentDao.getPaymentMethod(paymentStateContext.getPaymentMethodId(), internalCallContext)).thenReturn(paymentMethodModelDao);
    final PaymentPluginServiceRegistration paymentPluginServiceRegistration = new PaymentPluginServiceRegistration(paymentDao, registry);
    final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
    paymentOperation = new PaymentOperationTest(paymentPluginStatus, daoHelper, locker, paymentPluginDispatcher, paymentConfig, paymentStateContext);
}
Also used : GlobalLocker(org.killbill.commons.locker.GlobalLocker) MemoryGlobalLocker(org.killbill.commons.locker.memory.MemoryGlobalLocker) PaymentMethodModelDao(org.killbill.billing.payment.dao.PaymentMethodModelDao) OperationResult(org.killbill.automaton.OperationResult) BigDecimal(java.math.BigDecimal) PluginProperty(org.killbill.billing.payment.api.PluginProperty) MemoryGlobalLocker(org.killbill.commons.locker.memory.MemoryGlobalLocker) PluginDispatcher(org.killbill.billing.payment.dispatcher.PluginDispatcher) PaymentPluginServiceRegistration(org.killbill.billing.payment.core.PaymentPluginServiceRegistration) PaymentDao(org.killbill.billing.payment.dao.PaymentDao)

Aggregations

PluginProperty (org.killbill.billing.payment.api.PluginProperty)105 UUID (java.util.UUID)45 Account (org.killbill.billing.account.api.Account)42 ApiOperation (io.swagger.annotations.ApiOperation)35 ApiResponses (io.swagger.annotations.ApiResponses)35 Produces (javax.ws.rs.Produces)35 TimedResource (org.killbill.commons.metrics.TimedResource)35 Test (org.testng.annotations.Test)33 Path (javax.ws.rs.Path)32 Payment (org.killbill.billing.payment.api.Payment)30 CallContext (org.killbill.billing.util.callcontext.CallContext)29 ArrayList (java.util.ArrayList)19 LocalDate (org.joda.time.LocalDate)19 Consumes (javax.ws.rs.Consumes)17 BigDecimal (java.math.BigDecimal)16 TenantContext (org.killbill.billing.util.callcontext.TenantContext)15 GET (javax.ws.rs.GET)14 POST (javax.ws.rs.POST)13 DefaultEntitlement (org.killbill.billing.entitlement.api.DefaultEntitlement)13 AccountAuditLogs (org.killbill.billing.util.audit.AccountAuditLogs)13