Search in sources :

Example 11 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class PaymentOperation method convertToUnknownTransactionStatusAndErroredPaymentState.

//
// In case of exceptions, timeouts, we don't really know what happened:
// - Return an OperationResult.EXCEPTION to transition Payment State to Errored (see PaymentTransactionInfoPluginConverter#toOperationResult)
// - Construct a PaymentTransactionInfoPlugin whose PaymentPluginStatus = UNDEFINED to end up with a paymentTransactionStatus = UNKNOWN and have a chance to
//   be fixed by Janitor.
//
private OperationException convertToUnknownTransactionStatusAndErroredPaymentState(final Exception e) {
    final PaymentTransactionInfoPlugin paymentInfoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentStateContext.getPaymentId(), paymentStateContext.getTransactionId(), paymentStateContext.getTransactionType(), paymentStateContext.getAmount(), paymentStateContext.getCurrency(), paymentStateContext.getCallContext().getCreatedDate(), paymentStateContext.getCallContext().getCreatedDate(), PaymentPluginStatus.UNDEFINED, null, null);
    paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
    if (e.getCause() instanceof OperationException) {
        return (OperationException) e.getCause();
    }
    if (e instanceof OperationException) {
        return (OperationException) e;
    }
    return new OperationException(e, OperationResult.EXCEPTION);
}
Also used : DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) OperationException(org.killbill.automaton.OperationException)

Example 12 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class TestJanitor method testPendingEntriesThatDontMove.

// The test will check that when a PENDING entry stays PENDING, we go through all our retries and eventually give up (no infinite loop of retries)
@Test(groups = "slow")
public void testPendingEntriesThatDontMove() throws Exception {
    final BigDecimal requestedAmount = BigDecimal.TEN;
    final String paymentExternalKey = "haha";
    final String transactionExternalKey = "hoho!";
    testListener.pushExpectedEvent(NextEvent.PAYMENT);
    final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), paymentExternalKey, transactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
    testListener.assertListenerStatus();
    // Artificially move the transaction status to PENDING AND update state on the plugin as well
    final List<PaymentTransactionInfoPlugin> paymentTransactions = mockPaymentProviderPlugin.getPaymentInfo(account.getId(), payment.getId(), ImmutableList.<PluginProperty>of(), callContext);
    final PaymentTransactionInfoPlugin oTx = paymentTransactions.remove(0);
    final PaymentTransactionInfoPlugin updatePaymentTransaction = new DefaultNoOpPaymentInfoPlugin(oTx.getKbPaymentId(), oTx.getKbTransactionPaymentId(), oTx.getTransactionType(), oTx.getAmount(), oTx.getCurrency(), oTx.getCreatedDate(), oTx.getCreatedDate(), PaymentPluginStatus.PENDING, null, null);
    paymentTransactions.add(updatePaymentTransaction);
    mockPaymentProviderPlugin.updatePaymentTransactions(payment.getId(), paymentTransactions);
    final String paymentStateName = paymentSMHelper.getPendingStateForTransaction(TransactionType.AUTHORIZE).toString();
    testListener.pushExpectedEvent(NextEvent.PAYMENT);
    paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), null, payment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName, payment.getTransactions().get(0).getId(), TransactionStatus.PENDING, requestedAmount, account.getCurrency(), "loup", "chat", internalCallContext);
    testListener.assertListenerStatus();
    // 15s,1m,3m,1h,1d,1d,1d,1d,1d
    for (final TimeSpan cur : paymentConfig.getIncompleteTransactionsRetries(internalCallContext)) {
        // Verify there is a notification to retry updating the value
        assertEquals(getPendingNotificationCnt(internalCallContext), 1);
        clock.addDeltaFromReality(cur.getMillis() + 1);
        assertNotificationsCompleted(internalCallContext, 5);
        // We add a sleep here to make sure the notification gets processed. Note that calling assertNotificationsCompleted alone would not work
        // because there is a point in time where the notification queue is empty (showing notification was processed), but the processing of the notification
        // will itself enter a new notification, and so the synchronization is difficult without writing *too much code*.
        Thread.sleep(1000);
        assertNotificationsCompleted(internalCallContext, 5);
        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
        Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
    }
    await().atMost(TIMEOUT, TimeUnit.SECONDS).until(new Callable<Boolean>() {

        @Override
        public Boolean call() throws Exception {
            return getPendingNotificationCnt(internalCallContext) == 0;
        }
    });
}
Also used : TimeSpan(org.skife.config.TimeSpan) Payment(org.killbill.billing.payment.api.Payment) DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) 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) Test(org.testng.annotations.Test)

Example 13 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class TestDefaultAdminPaymentApi method testFixPaymentTransactionStateFromPaymentTransactionInfoPlugin.

@Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/551")
public void testFixPaymentTransactionStateFromPaymentTransactionInfoPlugin() throws PaymentApiException {
    final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, BigDecimal.TEN, Currency.EUR, UUID.randomUUID().toString(), UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
    final PaymentModelDao paymentModelDao = paymentDao.getPayment(payment.getId(), internalCallContext);
    final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(payment.getTransactions().get(0).getId(), internalCallContext);
    Assert.assertEquals(paymentModelDao.getStateName(), "AUTH_SUCCESS");
    Assert.assertEquals(paymentModelDao.getLastSuccessStateName(), "AUTH_SUCCESS");
    Assert.assertEquals(paymentTransactionModelDao.getTransactionStatus(), TransactionStatus.SUCCESS);
    Assert.assertEquals(paymentTransactionModelDao.getProcessedAmount().compareTo(BigDecimal.TEN), 0);
    Assert.assertEquals(paymentTransactionModelDao.getProcessedCurrency(), Currency.EUR);
    Assert.assertEquals(paymentTransactionModelDao.getGatewayErrorCode(), "");
    Assert.assertEquals(paymentTransactionModelDao.getGatewayErrorMsg(), "");
    final PaymentTransactionInfoPlugin infoPlugin = new DefaultNoOpPaymentInfoPlugin(paymentTransactionModelDao.getPaymentId(), paymentTransactionModelDao.getId(), paymentTransactionModelDao.getTransactionType(), paymentTransactionModelDao.getAmount(), paymentTransactionModelDao.getCurrency(), paymentTransactionModelDao.getEffectiveDate(), paymentTransactionModelDao.getCreatedDate(), PaymentPluginStatus.ERROR, "error-code", "error-msg");
    final PaymentTransaction newPaymentTransaction = new DefaultPaymentTransaction(paymentTransactionModelDao.getId(), paymentTransactionModelDao.getAttemptId(), paymentTransactionModelDao.getTransactionExternalKey(), paymentTransactionModelDao.getCreatedDate(), paymentTransactionModelDao.getUpdatedDate(), paymentTransactionModelDao.getPaymentId(), paymentTransactionModelDao.getTransactionType(), paymentTransactionModelDao.getEffectiveDate(), TransactionStatus.PAYMENT_FAILURE, paymentTransactionModelDao.getAmount(), paymentTransactionModelDao.getCurrency(), paymentTransactionModelDao.getProcessedAmount(), paymentTransactionModelDao.getProcessedCurrency(), infoPlugin.getGatewayErrorCode(), infoPlugin.getGatewayError(), infoPlugin);
    adminPaymentApi.fixPaymentTransactionState(payment, newPaymentTransaction, null, null, "AUTH_ERRORED", ImmutableList.<PluginProperty>of(), callContext);
    final PaymentModelDao refreshedPaymentModelDao = paymentDao.getPayment(payment.getId(), internalCallContext);
    final PaymentTransactionModelDao refreshedPaymentTransactionModelDao = paymentDao.getPaymentTransaction(payment.getTransactions().get(0).getId(), internalCallContext);
    Assert.assertEquals(refreshedPaymentModelDao.getStateName(), "AUTH_ERRORED");
    // TODO Shouldn't we allow the user to override this too?
    Assert.assertEquals(refreshedPaymentModelDao.getLastSuccessStateName(), "AUTH_SUCCESS");
    Assert.assertEquals(refreshedPaymentTransactionModelDao.getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
    Assert.assertEquals(refreshedPaymentTransactionModelDao.getProcessedAmount().compareTo(BigDecimal.TEN), 0);
    Assert.assertEquals(refreshedPaymentTransactionModelDao.getProcessedCurrency(), Currency.EUR);
    Assert.assertEquals(refreshedPaymentTransactionModelDao.getGatewayErrorCode(), "error-code");
    Assert.assertEquals(refreshedPaymentTransactionModelDao.getGatewayErrorMsg(), "error-msg");
}
Also used : DefaultNoOpPaymentInfoPlugin(org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin) PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentModelDao(org.killbill.billing.payment.dao.PaymentModelDao) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) Test(org.testng.annotations.Test)

Example 14 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class TestPaymentEnteringStateCallback method testEnterStateAndProcessPaymentTransactionInfoPlugin.

@Test(groups = "slow")
public void testEnterStateAndProcessPaymentTransactionInfoPlugin() throws Exception {
    // Create the payment and first transaction (would be done by PaymentLeavingStateCallback)
    daoHelper.createNewPaymentTransaction();
    Assert.assertEquals(paymentDao.getPaymentTransaction(paymentStateContext.getPaymentTransactionModelDao().getId(), internalCallContext).getTransactionStatus(), TransactionStatus.UNKNOWN);
    // Mock the plugin result
    final PaymentTransactionInfoPlugin paymentInfoPlugin = Mockito.mock(PaymentTransactionInfoPlugin.class);
    Mockito.when(paymentInfoPlugin.getAmount()).thenReturn(new BigDecimal("82010.222"));
    Mockito.when(paymentInfoPlugin.getCurrency()).thenReturn(Currency.CAD);
    Mockito.when(paymentInfoPlugin.getStatus()).thenReturn(PaymentPluginStatus.PENDING);
    Mockito.when(paymentInfoPlugin.getGatewayErrorCode()).thenReturn(UUID.randomUUID().toString().substring(0, 5));
    Mockito.when(paymentInfoPlugin.getGatewayError()).thenReturn(UUID.randomUUID().toString());
    paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
    // Process the plugin result
    callback.enteringState(state, operationCallback, operationResult, leavingStateCallback);
    // Verify the updated transaction
    final PaymentTransactionModelDao paymentTransaction = paymentDao.getPaymentTransaction(paymentStateContext.getPaymentTransactionModelDao().getId(), internalCallContext);
    Assert.assertEquals(paymentTransaction.getAmount().compareTo(paymentStateContext.getAmount()), 0);
    Assert.assertEquals(paymentTransaction.getCurrency(), paymentStateContext.getCurrency());
    Assert.assertEquals(paymentTransaction.getProcessedAmount().compareTo(paymentInfoPlugin.getAmount()), 0);
    Assert.assertEquals(paymentTransaction.getProcessedCurrency(), paymentInfoPlugin.getCurrency());
    Assert.assertEquals(paymentTransaction.getTransactionStatus(), TransactionStatus.PENDING);
    Assert.assertEquals(paymentTransaction.getGatewayErrorCode(), paymentInfoPlugin.getGatewayErrorCode());
    Assert.assertEquals(paymentTransaction.getGatewayErrorMsg(), paymentInfoPlugin.getGatewayError());
}
Also used : PaymentTransactionModelDao(org.killbill.billing.payment.dao.PaymentTransactionModelDao) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin) BigDecimal(java.math.BigDecimal) Test(org.testng.annotations.Test)

Example 15 with PaymentTransactionInfoPlugin

use of org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin in project killbill by killbill.

the class MockPaymentProviderPlugin method overridePaymentTransactionPluginResult.

public void overridePaymentTransactionPluginResult(final UUID kbPaymentId, final UUID kbTransactionId, final PaymentPluginStatus paymentPluginStatus) throws PaymentPluginApiException {
    final List<PaymentTransactionInfoPlugin> existingTransactions = paymentTransactions.get(kbPaymentId.toString());
    PaymentTransactionInfoPlugin paymentTransactionInfoPlugin = null;
    for (final PaymentTransactionInfoPlugin existingTransaction : existingTransactions) {
        if (existingTransaction.getKbTransactionPaymentId().equals(kbTransactionId)) {
            paymentTransactionInfoPlugin = existingTransaction;
            break;
        }
    }
    Preconditions.checkNotNull(paymentTransactionInfoPlugin);
    final Iterable<PluginProperty> pluginProperties = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, paymentPluginStatus.toString(), false));
    getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, paymentTransactionInfoPlugin.getAmount(), paymentTransactionInfoPlugin.getCurrency(), pluginProperties);
}
Also used : PluginProperty(org.killbill.billing.payment.api.PluginProperty) PaymentTransactionInfoPlugin(org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin)

Aggregations

PaymentTransactionInfoPlugin (org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin)16 PaymentTransactionModelDao (org.killbill.billing.payment.dao.PaymentTransactionModelDao)6 BigDecimal (java.math.BigDecimal)5 PaymentModelDao (org.killbill.billing.payment.dao.PaymentModelDao)5 DefaultNoOpPaymentInfoPlugin (org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin)5 Test (org.testng.annotations.Test)5 LinkedList (java.util.LinkedList)4 PluginProperty (org.killbill.billing.payment.api.PluginProperty)4 Predicate (com.google.common.base.Predicate)3 PaymentApiException (org.killbill.billing.payment.api.PaymentApiException)3 PaymentPluginApi (org.killbill.billing.payment.plugin.api.PaymentPluginApi)3 UUID (java.util.UUID)2 PaymentPluginApiException (org.killbill.billing.payment.plugin.api.PaymentPluginApiException)2 EventBusException (org.killbill.bus.api.PersistentBus.EventBusException)2 ImmutableList (com.google.common.collect.ImmutableList)1 IOException (java.io.IOException)1 List (java.util.List)1 OperationException (org.killbill.automaton.OperationException)1 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)1 Currency (org.killbill.billing.catalog.api.Currency)1