diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
index b15dcdb..dd4d222 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
@@ -66,6 +66,7 @@ import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
@@ -119,15 +120,14 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
state.runOperation(paymentControlStateMachineHelper.getOperation(), callback, enteringStateCallback, leavingStateCallback);
} catch (final MissingEntryException e) {
- throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
} catch (final OperationException e) {
- if (e.getCause() == null) {
- // Unclear if we should check whether there is a result that was set and return that result.
- throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
- } else if (e.getCause() instanceof PaymentApiException) {
+ if (e.getCause() instanceof PaymentApiException) {
throw (PaymentApiException) e.getCause();
- } else {
- throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
+ // If the result is set (and cause is null), that means we created a Payment but the associated transaction status is 'XXX_FAILURE',
+ // we don't throw, and return the failed Payment instead to be consistent with what happens when we don't go through control api.
+ } else if (e.getCause() != null || paymentStateContext.getResult() == null) {
+ throw new PaymentApiException(e.getCause(), ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
return paymentStateContext.getResult();
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index 0960bad..f843590 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -119,6 +119,42 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
}
@Test(groups = "slow")
+ public void testCreateFailedPurchase() throws PaymentApiException {
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+
+ final String paymentExternalKey = "ohhhh";
+ final String transactionExternalKey = "naaahhh";
+
+ mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+
+ final Payment payment = paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
+ ImmutableList.<PluginProperty>of(), callContext);
+
+ assertEquals(payment.getExternalKey(), paymentExternalKey);
+ assertEquals(payment.getPaymentMethodId(), account.getPaymentMethodId());
+ assertEquals(payment.getAccountId(), account.getId());
+ assertEquals(payment.getAuthAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCapturedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+ assertEquals(payment.getCurrency(), Currency.AED);
+
+ assertEquals(payment.getTransactions().size(), 1);
+ assertEquals(payment.getTransactions().get(0).getExternalKey(), transactionExternalKey);
+ assertEquals(payment.getTransactions().get(0).getPaymentId(), payment.getId());
+ assertEquals(payment.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getCurrency(), Currency.AED);
+ assertEquals(payment.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ assertEquals(payment.getTransactions().get(0).getProcessedCurrency(), Currency.AED);
+
+ assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
+ assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE);
+ assertNull(payment.getTransactions().get(0).getGatewayErrorMsg());
+ assertNull(payment.getTransactions().get(0).getGatewayErrorCode());
+ }
+
+ @Test(groups = "slow")
public void testCreateSuccessAuthCapture() throws PaymentApiException {
final BigDecimal authAmount = BigDecimal.TEN;
@@ -311,6 +347,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
} catch (final PaymentApiException expected) {
+ assertTrue(true);
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index 33b1484..cbc23e5 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -159,16 +159,10 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
Currency.USD));
setPaymentFailure(FailureType.PAYMENT_FAILURE);
- boolean failed = false;
final String paymentExternalKey = UUID.randomUUID().toString();
final String transactionExternalKey = UUID.randomUUID().toString();
- try {
- pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
- } catch (final PaymentApiException e) {
- failed = true;
- }
- assertTrue(failed);
+ pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
+ createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
Payment payment = getPaymentForExternalKey(paymentExternalKey);
List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -242,16 +236,10 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
Currency.USD));
setPaymentFailure(FailureType.PAYMENT_FAILURE);
- boolean failed = false;
final String paymentExternalKey = UUID.randomUUID().toString();
final String transactionExternalKey = UUID.randomUUID().toString();
- try {
- pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
- } catch (final PaymentApiException e) {
- failed = true;
- }
- assertTrue(failed);
+ pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
+ createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
Payment payment = getPaymentForExternalKey(paymentExternalKey);
List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);
@@ -335,16 +323,10 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
Currency.USD));
setPaymentFailure(FailureType.PAYMENT_FAILURE);
- boolean failed = false;
final String paymentExternalKey = UUID.randomUUID().toString();
final String transactionExternalKey = UUID.randomUUID().toString();
- try {
- pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
- createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
- } catch (final PaymentApiException e) {
- failed = true;
- }
- assertTrue(failed);
+ pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amount, Currency.USD, paymentExternalKey, transactionExternalKey,
+ createPropertiesForInvoice(invoice), ImmutableList.<String>of(InvoicePaymentControlPluginApi.PLUGIN_NAME), callContext, internalCallContext);
Payment payment = getPaymentForExternalKey(paymentExternalKey);
List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext);