killbill-memoizeit
Changes
account/pom.xml 2(+1 -1)
analytics/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
invoice/pom.xml 2(+1 -1)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
overdue/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java 46(+46 -0)
payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java 59(+54 -5)
payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java 59(+48 -11)
payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java 80(+51 -29)
payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java 5(+3 -2)
payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java 43(+43 -0)
payment/src/test/java/com/ning/billing/payment/glue/TestDefaultPaymentProviderPluginRegistryProvider.java 52(+52 -0)
payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java 55(+55 -0)
payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentMethodPlugin.java 63(+63 -0)
payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java 136(+136 -0)
pom.xml 2(+1 -1)
server/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index 03aadad..2b14a3f 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
analytics/pom.xml 2(+1 -1)
diff --git a/analytics/pom.xml b/analytics/pom.xml
index a221105..cc45ee2 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-analytics</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index 146503d..e8ed611 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index 479966a..dc7488c 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.payment.api;
import java.math.BigDecimal;
@@ -25,23 +26,23 @@ import com.ning.billing.util.callcontext.CallContext;
public interface PaymentApi {
- public Payment createPayment(final String accountKey, final UUID invoiceId, final BigDecimal amount, final CallContext context)
+ public Payment createPayment(final Account account, final UUID invoiceId, final BigDecimal amount, final CallContext context)
throws PaymentApiException;
- public Payment createPayment(final Account account, final UUID invoiceId, final BigDecimal amount, final CallContext context)
+ public Payment createExternalPayment(final Account account, final UUID invoiceId, final BigDecimal amount, final CallContext context)
throws PaymentApiException;
public Refund getRefund(final UUID refundId)
- throws PaymentApiException;
+ throws PaymentApiException;
public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
- throws PaymentApiException;
+ throws PaymentApiException;
public List<Refund> getAccountRefunds(final Account account)
- throws PaymentApiException;
+ throws PaymentApiException;
public List<Refund> getPaymentRefunds(final UUID paymentId)
- throws PaymentApiException;
+ throws PaymentApiException;
public List<Payment> getInvoicePayments(final UUID invoiceId)
throws PaymentApiException;
@@ -53,8 +54,8 @@ public interface PaymentApi {
throws PaymentApiException;
/*
- * Payment method Apis
- */
+ * Payment method Apis
+ */
public Set<String> getAvailablePlugins();
public String initializeAccountPlugin(final String pluginName, final Account account)
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
index cacdf3a..b0151ae 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.payment.api;
import java.util.List;
@@ -28,12 +29,12 @@ public interface PaymentMethodPlugin {
public String getValueString(String key);
public class PaymentMethodKVInfo {
+
private final String key;
private final Object value;
private final Boolean isUpdatable;
public PaymentMethodKVInfo(final String key, final Object value, final Boolean isUpdatable) {
- super();
this.key = key;
this.value = value;
this.isUpdatable = isUpdatable;
@@ -50,5 +51,48 @@ public interface PaymentMethodPlugin {
public Boolean getIsUpdatable() {
return isUpdatable;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("PaymentMethodKVInfo");
+ sb.append("{key='").append(key).append('\'');
+ sb.append(", value=").append(value);
+ sb.append(", isUpdatable=").append(isUpdatable);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final PaymentMethodKVInfo that = (PaymentMethodKVInfo) o;
+
+ if (isUpdatable != null ? !isUpdatable.equals(that.isUpdatable) : that.isUpdatable != null) {
+ return false;
+ }
+ if (key != null ? !key.equals(that.key) : that.key != null) {
+ return false;
+ }
+ if (value != null ? !value.equals(that.value) : that.value != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key != null ? key.hashCode() : 0;
+ result = 31 * result + (value != null ? value.hashCode() : 0);
+ result = 31 * result + (isUpdatable != null ? isUpdatable.hashCode() : 0);
+ return result;
+ }
}
}
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
index 5f8fe2d..dd54118 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
@@ -33,11 +33,11 @@ public interface PaymentPluginApi {
public PaymentInfoPlugin getPaymentInfo(UUID paymentId)
throws PaymentPluginApiException;
- public void processRefund(Account account, UUID paymentId, BigDecimal refundAmout)
- throws PaymentPluginApiException;
+ public void processRefund(final Account account, final UUID paymentId, BigDecimal refundAmount)
+ throws PaymentPluginApiException;
public int getNbRefundForPaymentAmount(final Account account, final UUID paymentId, final BigDecimal refundAmount)
- throws PaymentPluginApiException;
+ throws PaymentPluginApiException;
public String createPaymentProviderAccount(Account account)
throws PaymentPluginApiException;
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 100c119..351e867 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index 4d891e3..c222110 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index eb87818..935e9a0 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 5a05993..1f4329a 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index be19405..cf3b2bd 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 7a9c201..20560e6 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -64,6 +64,7 @@ import com.ning.billing.util.api.CustomFieldUserApi;
import com.ning.billing.util.api.TagApiException;
import com.ning.billing.util.api.TagDefinitionApiException;
import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.dao.ObjectType;
import com.google.inject.Inject;
@@ -188,12 +189,16 @@ public class InvoiceResource extends JaxRsResourceBase {
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment) throws AccountApiException, PaymentApiException {
+ final Account account = accountApi.getAccountById(UUID.fromString(payment.getAccountId()));
+
+ final UUID invoiceId = UUID.fromString(payment.getInvoiceId());
+ final CallContext callContext = context.createContext(createdBy, reason, comment);
if (externalPayment) {
- return Response.status(Status.BAD_REQUEST).entity("External payments have not been implemented yet").build();
+ paymentApi.createExternalPayment(account, invoiceId, payment.getAmount(), callContext);
+ } else {
+ paymentApi.createPayment(account, invoiceId, payment.getAmount(), callContext);
}
- final Account account = accountApi.getAccountById(UUID.fromString(payment.getAccountId()));
- paymentApi.createPayment(account, UUID.fromString(payment.getInvoiceId()), null, context.createContext(createdBy, reason, comment));
return uriBuilder.buildResponse(InvoiceResource.class, "getPayments", payment.getInvoiceId());
}
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index 4e0975a..e18c456 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index 2884d12..45d6b25 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 3288ae8..7d7f623 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 9e2f0af..b4631a2 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -17,14 +17,10 @@
package com.ning.billing.payment.api;
import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
-import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.payment.core.PaymentMethodProcessor;
@@ -32,8 +28,9 @@ import com.ning.billing.payment.core.PaymentProcessor;
import com.ning.billing.payment.core.RefundProcessor;
import com.ning.billing.util.callcontext.CallContext;
-public class DefaultPaymentApi implements PaymentApi {
+import com.google.inject.Inject;
+public class DefaultPaymentApi implements PaymentApi {
private final PaymentMethodProcessor methodProcessor;
private final PaymentProcessor paymentProcessor;
@@ -49,15 +46,14 @@ public class DefaultPaymentApi implements PaymentApi {
}
@Override
- public Payment createPayment(final String accountKey, final UUID invoiceId, final BigDecimal amount, final CallContext context)
- throws PaymentApiException {
- return paymentProcessor.createPayment(accountKey, invoiceId, amount, context, true);
+ public Payment createPayment(final Account account, final UUID invoiceId,
+ final BigDecimal amount, final CallContext context) throws PaymentApiException {
+ return paymentProcessor.createPayment(account, invoiceId, amount, context, true, false);
}
@Override
- public Payment createPayment(final Account account, final UUID invoiceId,
- final BigDecimal amount, final CallContext context) throws PaymentApiException {
- return paymentProcessor.createPayment(account, invoiceId, amount, context, true);
+ public Payment createExternalPayment(final Account account, final UUID invoiceId, final BigDecimal amount, final CallContext context) throws PaymentApiException {
+ return paymentProcessor.createPayment(account, invoiceId, amount, context, true, true);
}
@Override
@@ -80,16 +76,14 @@ public class DefaultPaymentApi implements PaymentApi {
return paymentProcessor.getAccountPayments(accountId);
}
-
-
@Override
- public Refund getRefund(UUID refundId) throws PaymentApiException {
+ public Refund getRefund(final UUID refundId) throws PaymentApiException {
return refundProcessor.getRefund(refundId);
}
@Override
- public Refund createRefund(Account account, UUID paymentId,
- BigDecimal refundAmount, boolean isAdjusted, CallContext context)
+ public Refund createRefund(final Account account, final UUID paymentId,
+ final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
throws PaymentApiException {
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL);
@@ -99,31 +93,28 @@ public class DefaultPaymentApi implements PaymentApi {
}
@Override
- public List<Refund> getAccountRefunds(Account account)
+ public List<Refund> getAccountRefunds(final Account account)
throws PaymentApiException {
return refundProcessor.getAccountRefunds(account);
}
@Override
- public List<Refund> getPaymentRefunds(UUID paymentId)
+ public List<Refund> getPaymentRefunds(final UUID paymentId)
throws PaymentApiException {
return refundProcessor.getPaymentRefunds(paymentId);
}
-
@Override
public Set<String> getAvailablePlugins() {
return methodProcessor.getAvailablePlugins();
}
-
@Override
public String initializeAccountPlugin(final String pluginName, final Account account)
throws PaymentApiException {
return methodProcessor.initializeAccountPlugin(pluginName, account);
}
-
@Override
public UUID addPaymentMethod(final String pluginName, final Account account,
final boolean setDefault, final PaymentMethodPlugin paymentMethodInfo, final CallContext context)
@@ -131,7 +122,6 @@ public class DefaultPaymentApi implements PaymentApi {
return methodProcessor.addPaymentMethod(pluginName, account, setDefault, paymentMethodInfo, context);
}
-
@Override
public List<PaymentMethod> refreshPaymentMethods(final String pluginName,
final Account account, final CallContext context)
diff --git a/payment/src/main/java/com/ning/billing/payment/bus/InvoiceHandler.java b/payment/src/main/java/com/ning/billing/payment/bus/InvoiceHandler.java
index 527a776..a690ea7 100644
--- a/payment/src/main/java/com/ning/billing/payment/bus/InvoiceHandler.java
+++ b/payment/src/main/java/com/ning/billing/payment/bus/InvoiceHandler.java
@@ -70,7 +70,7 @@ public class InvoiceHandler {
final CallContext context = new DefaultCallContext("PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken(), clock);
account = accountUserApi.getAccountById(event.getAccountId());
- paymentProcessor.createPayment(account, event.getInvoiceId(), null, context, false);
+ paymentProcessor.createPayment(account, event.getInvoiceId(), null, context, false, false);
} catch (AccountApiException e) {
log.error("Failed to process invoice payment", e);
} catch (PaymentApiException e) {
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index d0f9612..83e4146 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -42,10 +42,13 @@ import com.ning.billing.payment.api.DefaultPaymentMethodPlugin;
import com.ning.billing.payment.api.PaymentApiException;
import com.ning.billing.payment.api.PaymentMethod;
import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.api.PaymentMethodPlugin.PaymentMethodKVInfo;
import com.ning.billing.payment.dao.PaymentDao;
import com.ning.billing.payment.dao.PaymentMethodModelDao;
import com.ning.billing.payment.plugin.api.PaymentPluginApi;
import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
import com.ning.billing.util.bus.Bus;
import com.ning.billing.util.callcontext.CallContext;
@@ -189,6 +192,27 @@ public class PaymentMethodProcessor extends ProcessorBase {
return (result.size() == 0) ? null : result.get(0);
}
+ public PaymentMethod getExternalPaymentMethod(final Account account) throws PaymentApiException {
+ final List<PaymentMethod> paymentMethods = getPaymentMethods(account, false);
+ for (final PaymentMethod paymentMethod : paymentMethods) {
+ if (ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(paymentMethod.getPluginName())) {
+ return paymentMethod;
+ }
+ }
+
+ return null;
+ }
+
+ public ExternalPaymentProviderPlugin getExternalPaymentProviderPlugin(final Account account, final CallContext context) throws PaymentApiException {
+ // Check if this account has already used the external payment plugin
+ // If not, it's the first time - add a payment method for it
+ if (getExternalPaymentMethod(account) == null) {
+ final DefaultNoOpPaymentMethodPlugin props = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, ImmutableList.<PaymentMethodKVInfo>of());
+ addPaymentMethod(ExternalPaymentProviderPlugin.PLUGIN_NAME, account, false, props, context);
+ }
+
+ return (ExternalPaymentProviderPlugin) pluginRegistry.getPlugin(ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ }
private List<PaymentMethod> getPaymentMethodInternal(final List<PaymentMethodModelDao> paymentMethodModels, final UUID accountId, final String accountKey, final boolean withPluginDetail)
throws PaymentApiException {
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index f820705..e0e64f4 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -15,6 +15,7 @@
*/
package com.ning.billing.payment.core;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import java.math.BigDecimal;
import java.math.RoundingMode;
@@ -78,6 +79,7 @@ import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
public class PaymentProcessor extends ProcessorBase {
+ private final PaymentMethodProcessor paymentMethodProcessor;
private final InvoicePaymentApi invoicePaymentApi;
private final TagUserApi tagUserApi;
private final FailedPaymentRetryServiceScheduler failedPaymentRetryService;
@@ -95,19 +97,21 @@ public class PaymentProcessor extends ProcessorBase {
@Inject
public PaymentProcessor(final PaymentProviderPluginRegistry pluginRegistry,
- final AccountUserApi accountUserApi,
- final InvoicePaymentApi invoicePaymentApi,
- final TagUserApi tagUserApi,
- final FailedPaymentRetryServiceScheduler failedPaymentRetryService,
- final PluginFailureRetryServiceScheduler pluginFailureRetryService,
- final AutoPayRetryServiceScheduler autoPayoffRetryService,
- final PaymentDao paymentDao,
- final Bus eventBus,
- final Clock clock,
- final GlobalLocker locker,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
- final CallContextFactory factory) {
+ final PaymentMethodProcessor paymentMethodProcessor,
+ final AccountUserApi accountUserApi,
+ final InvoicePaymentApi invoicePaymentApi,
+ final TagUserApi tagUserApi,
+ final FailedPaymentRetryServiceScheduler failedPaymentRetryService,
+ final PluginFailureRetryServiceScheduler pluginFailureRetryService,
+ final AutoPayRetryServiceScheduler autoPayoffRetryService,
+ final PaymentDao paymentDao,
+ final Bus eventBus,
+ final Clock clock,
+ final GlobalLocker locker,
+ @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
+ final CallContextFactory factory) {
super(pluginRegistry, accountUserApi, eventBus, paymentDao, locker, executor);
+ this.paymentMethodProcessor = paymentMethodProcessor;
this.invoicePaymentApi = invoicePaymentApi;
this.tagUserApi = tagUserApi;
this.failedPaymentRetryService = failedPaymentRetryService;
@@ -197,22 +201,19 @@ public class PaymentProcessor extends ProcessorBase {
}
}
-
- public Payment createPayment(final String accountKey, final UUID invoiceId, final BigDecimal inputAmount, final CallContext context, final boolean isInstantPayment)
- throws PaymentApiException {
- try {
- final Account account = accountUserApi.getAccountByKey(accountKey);
- return createPayment(account, invoiceId, inputAmount, context, isInstantPayment);
- } catch (AccountApiException e) {
- throw new PaymentApiException(e);
+ public Payment createPayment(final Account account, final UUID invoiceId, @Nullable final BigDecimal inputAmount,
+ final CallContext context, final boolean isInstantPayment, final boolean isExternalPayment)
+ throws PaymentApiException {
+ // Use the special external payment plugin to handle external payments
+ final PaymentPluginApi plugin;
+ final UUID paymentMethodId;
+ if (isExternalPayment) {
+ plugin = paymentMethodProcessor.getExternalPaymentProviderPlugin(account, context);
+ paymentMethodId = paymentMethodProcessor.getExternalPaymentMethod(account).getId();
+ } else {
+ plugin = getPaymentProviderPlugin(account);
+ paymentMethodId = account.getPaymentMethodId();
}
- }
-
-
-
- public Payment createPayment(final Account account, final UUID invoiceId, final BigDecimal inputAmount, final CallContext context, final boolean isInstantPayment)
- throws PaymentApiException {
- final PaymentPluginApi plugin = getPaymentProviderPlugin(account);
try {
return paymentPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<Payment>(locker,
@@ -238,9 +239,9 @@ public class PaymentProcessor extends ProcessorBase {
final BigDecimal requestedAmount = getAndValidatePaymentAmount(invoice, inputAmount, isInstantPayment);
if (isAccountAutoPayOff) {
- return processNewPaymentForAutoPayOffWithAccountLocked(account, invoice, requestedAmount, context);
+ return processNewPaymentForAutoPayOffWithAccountLocked(paymentMethodId, account, invoice, requestedAmount, context);
} else {
- return processNewPaymentWithAccountLocked(plugin, account, invoice, requestedAmount, isInstantPayment, context);
+ return processNewPaymentWithAccountLocked(paymentMethodId, plugin, account, invoice, requestedAmount, isInstantPayment, context);
}
}
}));
@@ -298,7 +299,7 @@ public class PaymentProcessor extends ProcessorBase {
- private BigDecimal getAndValidatePaymentAmount(final Invoice invoice, final BigDecimal inputAmount, final boolean isInstantPayment)
+ private BigDecimal getAndValidatePaymentAmount(final Invoice invoice, @Nullable final BigDecimal inputAmount, final boolean isInstantPayment)
throws PaymentApiException {
if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
@@ -310,7 +311,7 @@ public class PaymentProcessor extends ProcessorBase {
throw new PaymentApiException(ErrorCode.PAYMENT_AMOUNT_DENIED,
invoice.getId(), inputAmount.floatValue(), invoice.getBalance().floatValue());
}
- BigDecimal result = inputAmount != null ? inputAmount : invoice.getBalance();
+ final BigDecimal result = inputAmount != null ? inputAmount : invoice.getBalance();
return result.setScale(2, RoundingMode.HALF_EVEN);
}
@@ -401,24 +402,21 @@ public class PaymentProcessor extends ProcessorBase {
}
}
- private Payment processNewPaymentForAutoPayOffWithAccountLocked(final Account account, final Invoice invoice, final BigDecimal requestedAmount, final CallContext context)
- throws PaymentApiException {
-
- final PaymentStatus paymentStatus = PaymentStatus.AUTO_PAY_OFF;
+ private Payment processNewPaymentForAutoPayOffWithAccountLocked(final UUID paymentMethodId, final Account account, final Invoice invoice,
+ final BigDecimal requestedAmount, final CallContext context)
+ throws PaymentApiException {
+ final PaymentStatus paymentStatus = PaymentStatus.AUTO_PAY_OFF;
- final PaymentModelDao paymentInfo = new PaymentModelDao(account.getId(), invoice.getId(), account.getPaymentMethodId(), requestedAmount, invoice.getCurrency(), clock.getUTCNow(), paymentStatus);
+ final PaymentModelDao paymentInfo = new PaymentModelDao(account.getId(), invoice.getId(), paymentMethodId, requestedAmount, invoice.getCurrency(), clock.getUTCNow(), paymentStatus);
final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), paymentInfo.getId(), paymentStatus, clock.getUTCNow(), requestedAmount);
paymentDao.insertPaymentWithAttempt(paymentInfo, attempt, context);
return new DefaultPayment(paymentInfo, Collections.singletonList(attempt), Collections.<RefundModelDao>emptyList());
}
-
- private Payment processNewPaymentWithAccountLocked(final PaymentPluginApi plugin, final Account account, final Invoice invoice,
- final BigDecimal requestedAmount, final boolean isInstantPayment, final CallContext context) throws PaymentApiException {
-
-
- final PaymentModelDao payment = new PaymentModelDao(account.getId(), invoice.getId(), account.getPaymentMethodId(), requestedAmount.setScale(2, RoundingMode.HALF_EVEN), invoice.getCurrency(), clock.getUTCNow());
+ private Payment processNewPaymentWithAccountLocked(final UUID paymentMethodId, final PaymentPluginApi plugin, final Account account, final Invoice invoice,
+ final BigDecimal requestedAmount, final boolean isInstantPayment, final CallContext context) throws PaymentApiException {
+ final PaymentModelDao payment = new PaymentModelDao(account.getId(), invoice.getId(), paymentMethodId, requestedAmount.setScale(2, RoundingMode.HALF_EVEN), invoice.getCurrency(), clock.getUTCNow());
final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), clock.getUTCNow(), requestedAmount);
final PaymentModelDao savedPayment = paymentDao.insertPaymentWithAttempt(payment, attempt, context);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index b7ed65f..b75f21c 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -17,7 +17,9 @@ package com.ning.billing.payment.dao;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import org.skife.jdbi.v2.IDBI;
@@ -208,6 +210,7 @@ public class AuditedPaymentDao implements PaymentDao {
public List<PaymentMethodModelDao> inTransaction(final PaymentMethodSqlDao transactional, final TransactionStatus status) throws Exception {
final List<PaymentMethodModelDao> existingPaymentMethods = getPaymentMethodsInTransaction(transactional, accountId);
+ final Set<String> externalPaymentIdProcessed = new HashSet<String>();
for (final PaymentMethodModelDao finalPaymentMethod : paymentMethods) {
boolean isExistingPaymentMethod = false;
@@ -218,11 +221,8 @@ public class AuditedPaymentDao implements PaymentDao {
break;
} else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
// We already have it but its status has changed - update it accordingly
- if (finalPaymentMethod.isActive()) {
- undeletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId());
- } else {
- deletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId());
- }
+ // Note - in the remote system, the payment method will always be active
+ undeletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId());
isExistingPaymentMethod = true;
break;
}
@@ -232,6 +232,15 @@ public class AuditedPaymentDao implements PaymentDao {
if (!isExistingPaymentMethod) {
insertPaymentMethodInTransaction(transactional, finalPaymentMethod, context);
}
+
+ externalPaymentIdProcessed.add(finalPaymentMethod.getExternalId());
+ }
+
+ // Finally, mark as deleted the ones that don't exist in the specified list (remote system)
+ for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+ if (!externalPaymentIdProcessed.contains(existingPaymentMethod.getExternalId())) {
+ deletedPaymentMethodInTransaction(transactional, existingPaymentMethod.getId());
+ }
}
return getPaymentMethodsInTransaction(transactional, accountId);
diff --git a/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java b/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
new file mode 100644
index 0000000..5a92d7f
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.glue;
+
+import com.ning.billing.config.PaymentConfig;
+import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
+import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class DefaultPaymentProviderPluginRegistryProvider implements Provider<DefaultPaymentProviderPluginRegistry> {
+
+ private final PaymentConfig paymentConfig;
+ private final ExternalPaymentProviderPlugin externalPaymentProviderPlugin;
+
+ @Inject
+ public DefaultPaymentProviderPluginRegistryProvider(final PaymentConfig paymentConfig, final ExternalPaymentProviderPlugin externalPaymentProviderPlugin) {
+ this.paymentConfig = paymentConfig;
+ this.externalPaymentProviderPlugin = externalPaymentProviderPlugin;
+ }
+
+ @Override
+ public DefaultPaymentProviderPluginRegistry get() {
+ final DefaultPaymentProviderPluginRegistry pluginRegistry = new DefaultPaymentProviderPluginRegistry(paymentConfig);
+
+ // Make the external payment provider plugin available by default
+ pluginRegistry.register(externalPaymentProviderPlugin, ExternalPaymentProviderPlugin.PLUGIN_NAME);
+
+ return pluginRegistry;
+ }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
index 348a0ed..9e601aa 100644
--- a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
@@ -25,9 +25,6 @@ import org.skife.config.ConfigSource;
import org.skife.config.ConfigurationObjectFactory;
import org.skife.config.SimplePropertyConfigSource;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.inject.AbstractModule;
-import com.google.inject.name.Names;
import com.ning.billing.config.PaymentConfig;
import com.ning.billing.payment.api.DefaultPaymentApi;
import com.ning.billing.payment.api.PaymentApi;
@@ -39,7 +36,6 @@ import com.ning.billing.payment.core.PaymentProcessor;
import com.ning.billing.payment.core.RefundProcessor;
import com.ning.billing.payment.dao.AuditedPaymentDao;
import com.ning.billing.payment.dao.PaymentDao;
-import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
import com.ning.billing.payment.retry.AutoPayRetryService;
import com.ning.billing.payment.retry.AutoPayRetryService.AutoPayRetryServiceScheduler;
@@ -48,6 +44,10 @@ import com.ning.billing.payment.retry.FailedPaymentRetryService.FailedPaymentRet
import com.ning.billing.payment.retry.PluginFailureRetryService;
import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
+
public class PaymentModule extends AbstractModule {
private static final int PLUGIN_NB_THREADS = 3;
private static final String PLUGIN_THREAD_PREFIX = "Plugin-th-";
@@ -107,7 +107,8 @@ public class PaymentModule extends AbstractModule {
final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
bind(PaymentConfig.class).toInstance(paymentConfig);
- bind(PaymentProviderPluginRegistry.class).to(DefaultPaymentProviderPluginRegistry.class).asEagerSingleton();
+ bind(PaymentProviderPluginRegistry.class).toProvider(DefaultPaymentProviderPluginRegistryProvider.class).asEagerSingleton();
+
bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
bind(InvoiceHandler.class).asEagerSingleton();
bind(TagHandler.class).asEagerSingleton();
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
index 3dd0a01..135ba1b 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.payment.provider;
import java.math.BigDecimal;
@@ -31,7 +32,6 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
public DefaultNoOpPaymentInfoPlugin(final BigDecimal amount, final DateTime effectiveDate,
final DateTime createdDate, final PaymentPluginStatus status, final String error) {
- super();
this.amount = amount;
this.effectiveDate = effectiveDate;
this.createdDate = createdDate;
@@ -39,13 +39,11 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
this.error = error;
}
-
@Override
public BigDecimal getAmount() {
return amount;
}
-
@Override
public DateTime getEffectiveDate() {
return effectiveDate;
@@ -71,15 +69,66 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
return null;
}
-
@Override
public String getExtFirstReferenceId() {
return null;
}
-
@Override
public String getExtSecondReferenceId() {
return null;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DefaultNoOpPaymentInfoPlugin");
+ sb.append("{amount=").append(amount);
+ sb.append(", effectiveDate=").append(effectiveDate);
+ sb.append(", createdDate=").append(createdDate);
+ sb.append(", status=").append(status);
+ sb.append(", error='").append(error).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultNoOpPaymentInfoPlugin that = (DefaultNoOpPaymentInfoPlugin) o;
+
+ if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+ return false;
+ }
+ if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+ return false;
+ }
+ if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+ return false;
+ }
+ if (error != null ? !error.equals(that.error) : that.error != null) {
+ return false;
+ }
+ if (status != that.status) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = amount != null ? amount.hashCode() : 0;
+ result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+ result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+ result = 31 * result + (status != null ? status.hashCode() : 0);
+ result = 31 * result + (error != null ? error.hashCode() : 0);
+ return result;
+ }
}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
index af88c29..f84b111 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentMethodPlugin.java
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.payment.provider;
import java.util.List;
@@ -22,8 +23,8 @@ import com.ning.billing.payment.api.PaymentMethodPlugin;
public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
- private String externalId;
- private boolean isDefault;
+ private final String externalId;
+ private final boolean isDefault;
private List<PaymentMethodKVInfo> props;
public DefaultNoOpPaymentMethodPlugin(final PaymentMethodPlugin src) {
@@ -34,7 +35,6 @@ public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
public DefaultNoOpPaymentMethodPlugin(final String externalId, final boolean isDefault,
final List<PaymentMethodKVInfo> props) {
- super();
this.externalId = externalId;
this.isDefault = isDefault;
this.props = props;
@@ -55,14 +55,6 @@ public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
return props;
}
- public void setExternalId(final String externalId) {
- this.externalId = externalId;
- }
-
- public void setDefault(final boolean isDefault) {
- this.isDefault = isDefault;
- }
-
public void setProps(final List<PaymentMethodKVInfo> props) {
this.props = props;
}
@@ -72,11 +64,56 @@ public class DefaultNoOpPaymentMethodPlugin implements PaymentMethodPlugin {
if (props == null) {
return null;
}
+
for (final PaymentMethodKVInfo cur : props) {
if (cur.getKey().equals(key)) {
return cur.getValue().toString();
}
}
+
return null;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DefaultNoOpPaymentMethodPlugin");
+ sb.append("{externalId='").append(externalId).append('\'');
+ sb.append(", isDefault=").append(isDefault);
+ sb.append(", props=").append(props);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultNoOpPaymentMethodPlugin that = (DefaultNoOpPaymentMethodPlugin) o;
+
+ if (isDefault != that.isDefault) {
+ return false;
+ }
+ if (externalId != null ? !externalId.equals(that.externalId) : that.externalId != null) {
+ return false;
+ }
+ if (props != null ? !props.equals(that.props) : that.props != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = externalId != null ? externalId.hashCode() : 0;
+ result = 31 * result + (isDefault ? 1 : 0);
+ result = 31 * result + (props != null ? props.hashCode() : 0);
+ return result;
+ }
}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index c43b64b..2db4092 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -24,7 +24,6 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
-import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
import com.ning.billing.payment.api.PaymentMethodPlugin;
import com.ning.billing.payment.plugin.api.NoOpPaymentPluginApi;
@@ -34,15 +33,24 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
import com.ning.billing.payment.plugin.api.PaymentProviderAccount;
import com.ning.billing.util.clock.Clock;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.inject.Inject;
+
public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
+
+ private static final String PLUGIN_NAME = "__NO_OP__";
+
private final AtomicBoolean makeNextInvoiceFailWithError = new AtomicBoolean(false);
private final AtomicBoolean makeNextInvoiceFailWithException = new AtomicBoolean(false);
private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
- private final Map<UUID, PaymentInfoPlugin> payments = new ConcurrentHashMap<UUID, PaymentInfoPlugin>();
+ private final Map<UUID, PaymentInfoPlugin> payments = new ConcurrentHashMap<UUID, PaymentInfoPlugin>();
+ // Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
+ private final Multimap<UUID, BigDecimal> refunds = LinkedListMultimap.<UUID, BigDecimal>create();
private final Map<String, List<PaymentMethodPlugin>> paymentMethods = new ConcurrentHashMap<String, List<PaymentMethodPlugin>>();
-
private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
+
private final Clock clock;
@Inject
@@ -73,13 +81,11 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
makeAllInvoicesFailWithError.set(failure);
}
-
@Override
public String getName() {
- return null;
+ return PLUGIN_NAME;
}
-
@Override
public PaymentInfoPlugin processPayment(final String externalKey, final UUID paymentId, final BigDecimal amount) throws PaymentPluginApiException {
if (makeNextInvoiceFailWithException.getAndSet(false)) {
@@ -92,7 +98,6 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
return result;
}
-
@Override
public PaymentInfoPlugin getPaymentInfo(final UUID paymentId) throws PaymentPluginApiException {
final PaymentInfoPlugin payment = payments.get(paymentId);
@@ -128,11 +133,9 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
}
pms.add(realWithID);
-
return realWithID.getExternalPaymentMethodId();
}
-
@Override
public void updatePaymentMethod(final String accountKey, final PaymentMethodPlugin paymentMethodProps)
throws PaymentPluginApiException {
@@ -144,11 +147,9 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
@Override
public void deletePaymentMethod(final String accountKey, final String paymentMethodId) throws PaymentPluginApiException {
-
PaymentMethodPlugin toBeDeleted = null;
final List<PaymentMethodPlugin> pms = paymentMethods.get(accountKey);
if (pms != null) {
-
for (final PaymentMethodPlugin cur : pms) {
if (cur.getExternalPaymentMethodId().equals(paymentMethodId)) {
toBeDeleted = cur;
@@ -156,6 +157,7 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
}
}
}
+
if (toBeDeleted != null) {
pms.remove(toBeDeleted);
}
@@ -170,7 +172,42 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
@Override
public PaymentMethodPlugin getPaymentMethodDetail(final String accountKey, final String externalPaymentId)
throws PaymentPluginApiException {
- return getPaymentMethodDetail(accountKey, externalPaymentId);
+ return getPaymentMethod(accountKey, externalPaymentId);
+ }
+
+ @Override
+ public void setDefaultPaymentMethod(final String accountKey, final String externalPaymentId) throws PaymentPluginApiException {
+ }
+
+ @Override
+ public void processRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount) throws PaymentPluginApiException {
+ final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(paymentId);
+ if (paymentInfoPlugin == null) {
+ throw new PaymentPluginApiException("", String.format("No payment found for paymentId %s (plugin %s)", paymentId, getName()));
+ }
+
+ BigDecimal maxAmountRefundable = paymentInfoPlugin.getAmount();
+ for (final BigDecimal refund : refunds.get(paymentId)) {
+ maxAmountRefundable = maxAmountRefundable.add(refund.negate());
+ }
+ if (maxAmountRefundable.compareTo(refundAmount) < 0) {
+ throw new PaymentPluginApiException("", String.format("Refund amount of %s for paymentId %s is bigger than the payment amount %s (plugin %s)",
+ refundAmount, paymentId, paymentInfoPlugin.getAmount(), getName()));
+ }
+
+ refunds.put(paymentId, refundAmount);
+ }
+
+ @Override
+ public int getNbRefundForPaymentAmount(final Account account, final UUID paymentId, final BigDecimal refundAmount) throws PaymentPluginApiException {
+ int nbRefunds = 0;
+ for (final BigDecimal amount : refunds.get(paymentId)) {
+ if (amount.compareTo(refundAmount) == 0) {
+ nbRefunds++;
+ }
+ }
+
+ return nbRefunds;
}
private DefaultNoOpPaymentMethodPlugin getPaymentMethod(final String accountKey, final String externalPaymentId) {
@@ -178,28 +215,13 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
if (pms == null) {
return null;
}
+
for (final PaymentMethodPlugin cur : pms) {
if (cur.getExternalPaymentMethodId().equals(externalPaymentId)) {
return (DefaultNoOpPaymentMethodPlugin) cur;
}
}
- return null;
- }
-
- @Override
- public void setDefaultPaymentMethod(final String accountKey,
- final String externalPaymentId) throws PaymentPluginApiException {
- }
- @Override
- public void processRefund(Account account, UUID paymentId,
- BigDecimal refundAmout) throws PaymentPluginApiException {
- }
-
- @Override
- public int getNbRefundForPaymentAmount(Account account, UUID paymentId,
- BigDecimal refundAmount) throws PaymentPluginApiException {
- return 0;
+ return null;
}
-
}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
index 200eaca..2ffa03c 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
@@ -20,11 +20,12 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import com.google.common.base.Strings;
-import com.google.inject.Inject;
import com.ning.billing.config.PaymentConfig;
import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+
public class DefaultPaymentProviderPluginRegistry implements PaymentProviderPluginRegistry {
private final String defaultPlugin;
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
new file mode 100644
index 0000000..52fcb0b
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.provider;
+
+import com.ning.billing.util.clock.Clock;
+
+import com.google.inject.Inject;
+
+/**
+ * Special plugin used to record external payments (i.e. payments not issued by Killbill), such as checks.
+ * <p/>
+ * The implementation is very similar to the no-op plugin, which it extends. This can potentially be an issue
+ * if Killbill is processing a lot of external payments as they are all kept in memory.
+ * TODO: do something about it
+ */
+public class ExternalPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlugin {
+
+ public static final String PLUGIN_NAME = "__EXTERNAL_PAYMENT__";
+
+ @Inject
+ public ExternalPaymentProviderPlugin(final Clock clock) {
+ super(clock);
+ }
+
+ @Override
+ public String getName() {
+ return PLUGIN_NAME;
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index a1a649e..88369bf 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -157,7 +157,7 @@ public class TestPaymentApi extends PaymentTestSuite {
Currency.USD));
try {
- final Payment paymentInfo = paymentApi.createPayment(account.getExternalKey(), invoice.getId(), requestedAmount, context);
+ final Payment paymentInfo = paymentApi.createPayment(account, invoice.getId(), requestedAmount, context);
if (expectedAmount == null) {
fail("Expected to fail because requested amount > invoice amount");
}
diff --git a/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java
new file mode 100644
index 0000000..a349629
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.core;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.config.PaymentConfig;
+import com.ning.billing.payment.PaymentTestSuite;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.dao.MockPaymentDao;
+import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
+import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.globallocker.GlobalLocker;
+
+public class TestPaymentMethodProcessor extends PaymentTestSuite {
+
+ private PaymentMethodProcessor processor;
+
+ @BeforeMethod(groups = "fast")
+ public void setUp() throws Exception {
+ final DefaultPaymentProviderPluginRegistry pluginRegistry = new DefaultPaymentProviderPluginRegistry(Mockito.mock(PaymentConfig.class));
+ pluginRegistry.register(new ExternalPaymentProviderPlugin(new ClockMock()), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+
+ final AccountUserApi accountUserApi = Mockito.mock(AccountUserApi.class);
+ final Bus bus = Mockito.mock(Bus.class);
+ final MockPaymentDao paymentDao = new MockPaymentDao();
+ final GlobalLocker globalLocker = Mockito.mock(GlobalLocker.class);
+ final ExecutorService executorService = Mockito.mock(ExecutorService.class);
+ processor = new PaymentMethodProcessor(pluginRegistry, accountUserApi, bus, paymentDao, globalLocker, executorService);
+ }
+
+ @Test(groups = "fast")
+ public void testGetExternalPaymentProviderPlugin() throws Exception {
+ final UUID accountId = UUID.randomUUID();
+ final Account account = Mockito.mock(Account.class);
+ Mockito.when(account.getId()).thenReturn(accountId);
+ Mockito.when(account.getExternalKey()).thenReturn(accountId.toString());
+ final CallContext context = Mockito.mock(CallContext.class);
+
+ Assert.assertEquals(processor.getPaymentMethods(account, false).size(), 0);
+
+ // The first call should create the payment method
+ final ExternalPaymentProviderPlugin providerPlugin = processor.getExternalPaymentProviderPlugin(account, context);
+ Assert.assertEquals(providerPlugin.getName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ final List<PaymentMethod> paymentMethods = processor.getPaymentMethods(account, false);
+ Assert.assertEquals(paymentMethods.size(), 1);
+ Assert.assertEquals(paymentMethods.get(0).getPluginName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ Assert.assertEquals(paymentMethods.get(0).getAccountId(), account.getId());
+
+ // The succeeding calls should not create any other payment method
+ final UUID externalPaymentMethodId = paymentMethods.get(0).getId();
+ for (int i = 0; i < 50; i++) {
+ final ExternalPaymentProviderPlugin foundProviderPlugin = processor.getExternalPaymentProviderPlugin(account, context);
+ Assert.assertEquals(foundProviderPlugin.getName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+
+ final List<PaymentMethod> foundPaymentMethods = processor.getPaymentMethods(account, false);
+ Assert.assertEquals(foundPaymentMethods.size(), 1);
+ Assert.assertEquals(foundPaymentMethods.get(0).getPluginName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ Assert.assertEquals(foundPaymentMethods.get(0).getAccountId(), account.getId());
+ Assert.assertEquals(foundPaymentMethods.get(0).getId(), externalPaymentMethodId);
+ }
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/TestDefaultPaymentProviderPluginRegistryProvider.java b/payment/src/test/java/com/ning/billing/payment/glue/TestDefaultPaymentProviderPluginRegistryProvider.java
new file mode 100644
index 0000000..ef6ffde
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/glue/TestDefaultPaymentProviderPluginRegistryProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.glue;
+
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.config.PaymentConfig;
+import com.ning.billing.payment.PaymentTestSuite;
+import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
+import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.clock.Clock;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+public class TestDefaultPaymentProviderPluginRegistryProvider extends PaymentTestSuite {
+
+ @Test(groups = "fast")
+ public void testInjection() throws Exception {
+ final Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(PaymentConfig.class).toInstance(Mockito.mock(PaymentConfig.class));
+ bind(Clock.class).toInstance(Mockito.mock(Clock.class));
+
+ bind(PaymentProviderPluginRegistry.class)
+ .toProvider(DefaultPaymentProviderPluginRegistryProvider.class)
+ .asEagerSingleton();
+ }
+ });
+
+ final PaymentProviderPluginRegistry registry = injector.getInstance(PaymentProviderPluginRegistry.class);
+ Assert.assertNotNull(registry.getPlugin(ExternalPaymentProviderPlugin.PLUGIN_NAME));
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
new file mode 100644
index 0000000..158f30e
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.provider;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.payment.PaymentTestSuite;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin.PaymentPluginStatus;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestDefaultNoOpPaymentInfoPlugin extends PaymentTestSuite {
+
+ private final Clock clock = new ClockMock();
+
+ @Test(groups = "fast")
+ public void testEquals() throws Exception {
+ final BigDecimal amount = new BigDecimal("1.394810E-3");
+ final DateTime effectiveDate = clock.getUTCNow().plusDays(1);
+ final DateTime createdDate = clock.getUTCNow();
+ final PaymentPluginStatus status = PaymentPluginStatus.UNDEFINED;
+ final String error = UUID.randomUUID().toString();
+
+ final DefaultNoOpPaymentInfoPlugin info = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+ status, error);
+ Assert.assertEquals(info, info);
+
+ final DefaultNoOpPaymentInfoPlugin sameInfo = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+ status, error);
+ Assert.assertEquals(sameInfo, info);
+
+ final DefaultNoOpPaymentInfoPlugin otherInfo = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+ status, UUID.randomUUID().toString());
+ Assert.assertNotEquals(otherInfo, info);
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentMethodPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentMethodPlugin.java
new file mode 100644
index 0000000..d093077
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentMethodPlugin.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.provider;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.payment.PaymentTestSuite;
+import com.ning.billing.payment.api.PaymentMethodPlugin.PaymentMethodKVInfo;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultNoOpPaymentMethodPlugin extends PaymentTestSuite {
+
+ @Test(groups = "fast")
+ public void testEquals() throws Exception {
+ final String externalId = UUID.randomUUID().toString();
+ final boolean isDefault = false;
+ final List<PaymentMethodKVInfo> props = ImmutableList.<PaymentMethodKVInfo>of(new PaymentMethodKVInfo(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false));
+
+ final DefaultNoOpPaymentMethodPlugin paymentMethod = new DefaultNoOpPaymentMethodPlugin(externalId, isDefault, props);
+ Assert.assertEquals(paymentMethod, paymentMethod);
+
+ final DefaultNoOpPaymentMethodPlugin samePaymentMethod = new DefaultNoOpPaymentMethodPlugin(externalId, isDefault, props);
+ Assert.assertEquals(samePaymentMethod, paymentMethod);
+
+ final DefaultNoOpPaymentMethodPlugin otherPaymentMethod = new DefaultNoOpPaymentMethodPlugin(externalId, isDefault, ImmutableList.<PaymentMethodKVInfo>of());
+ Assert.assertNotEquals(otherPaymentMethod, paymentMethod);
+ }
+
+ @Test(groups = "fast")
+ public void testEqualsForPaymentMethodKVInfo() throws Exception {
+ final String key = UUID.randomUUID().toString();
+ final Object value = UUID.randomUUID();
+ final boolean updatable = false;
+
+ final PaymentMethodKVInfo kvInfo = new PaymentMethodKVInfo(key, value, updatable);
+ Assert.assertEquals(kvInfo, kvInfo);
+
+ final PaymentMethodKVInfo sameKvInfo = new PaymentMethodKVInfo(key, value, updatable);
+ Assert.assertEquals(sameKvInfo, kvInfo);
+
+ final PaymentMethodKVInfo otherKvInfo = new PaymentMethodKVInfo(key, value, !updatable);
+ Assert.assertNotEquals(otherKvInfo, kvInfo);
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
new file mode 100644
index 0000000..ff07534
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.provider;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.Seconds;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.payment.PaymentTestSuite;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin.PaymentPluginStatus;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestExternalPaymentProviderPlugin extends PaymentTestSuite {
+
+ private final Clock clock = new ClockMock();
+ private ExternalPaymentProviderPlugin plugin;
+
+ @BeforeMethod(groups = "fast")
+ public void setUp() throws Exception {
+ plugin = new ExternalPaymentProviderPlugin(clock);
+ }
+
+ @Test(groups = "fast")
+ public void testGetName() throws Exception {
+ Assert.assertEquals(plugin.getName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ }
+
+ @Test(groups = "fast")
+ public void testProcessPayment() throws Exception {
+ final String externalKey = UUID.randomUUID().toString();
+ final UUID paymentId = UUID.randomUUID();
+ final BigDecimal amount = BigDecimal.TEN;
+ final PaymentInfoPlugin paymentInfoPlugin = plugin.processPayment(externalKey, paymentId, amount);
+
+ Assert.assertEquals(paymentInfoPlugin.getAmount(), amount);
+ Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getCreatedDate(), clock.getUTCNow()).getSeconds(), 0);
+ Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getEffectiveDate(), clock.getUTCNow()).getSeconds(), 0);
+ Assert.assertNull(paymentInfoPlugin.getExtFirstReferenceId());
+ Assert.assertNull(paymentInfoPlugin.getExtSecondReferenceId());
+ Assert.assertNull(paymentInfoPlugin.getGatewayError());
+ Assert.assertNull(paymentInfoPlugin.getGatewayErrorCode());
+ Assert.assertEquals(paymentInfoPlugin.getStatus(), PaymentPluginStatus.PROCESSED);
+
+ final PaymentInfoPlugin retrievedPaymentInfoPlugin = plugin.getPaymentInfo(paymentId);
+ Assert.assertEquals(retrievedPaymentInfoPlugin, paymentInfoPlugin);
+ }
+
+ @Test(groups = "fast", expectedExceptions = PaymentPluginApiException.class)
+ public void testRefundForNonExistingPayment() throws Exception {
+ plugin.processRefund(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE);
+ }
+
+ @Test(groups = "fast", expectedExceptions = PaymentPluginApiException.class)
+ public void testRefundTooLarge() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ plugin.processPayment(UUID.randomUUID().toString(), paymentId, BigDecimal.ZERO);
+
+ plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE);
+ }
+
+ @Test(groups = "fast")
+ public void testRefundTooLargeMultipleTimes() throws Exception {
+ final UUID paymentId = UUID.randomUUID();
+ plugin.processPayment(UUID.randomUUID().toString(), paymentId, BigDecimal.TEN);
+
+ final Account account = Mockito.mock(Account.class);
+ for (int i = 0; i < 10; i++) {
+ plugin.processRefund(account, paymentId, BigDecimal.ONE);
+ }
+
+ try {
+ plugin.processRefund(account, paymentId, BigDecimal.ONE);
+ Assert.fail("Shouldn't have been able to refund");
+ } catch (PaymentPluginApiException e) {
+ Assert.assertTrue(true);
+ }
+ }
+
+ @Test(groups = "fast")
+ public void testRefund() throws Exception {
+ // An external payment refund would be e.g. a check that we trash
+ final String externalKey = UUID.randomUUID().toString();
+ final UUID paymentId = UUID.randomUUID();
+ final BigDecimal amount = BigDecimal.TEN;
+ plugin.processPayment(externalKey, paymentId, amount);
+
+ plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE), 1);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5")), 0);
+
+ // Try multiple refunds
+
+ plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE), 2);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5")), 0);
+
+ plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE), 3);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5")), 0);
+
+ plugin.processRefund(Mockito.mock(Account.class), paymentId, new BigDecimal("5"));
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN), 0);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE), 3);
+ Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5")), 1);
+ }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index 9a002cb..0c021a6 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -172,7 +172,7 @@ public class TestRetryService extends PaymentTestSuite {
setPaymentFailure(failureType);
boolean failed = false;
try {
- paymentProcessor.createPayment(account.getExternalKey(), invoice.getId(), amount, context, false);
+ paymentProcessor.createPayment(account, invoice.getId(), amount, context, false, false);
} catch (PaymentApiException e) {
failed = true;
}
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 8267e5d..7d00037 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
<packaging>pom</packaging>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
<url>http://github.com/ning/killbill</url>
server/pom.xml 2(+1 -1)
diff --git a/server/pom.xml b/server/pom.xml
index fd40af9..656702f 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-server</artifactId>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
index b6b73fb..4efe6cb 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -36,14 +36,18 @@ import com.ning.billing.jaxrs.json.AccountJson;
import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+import com.ning.billing.jaxrs.json.PaymentMethodJson;
import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
import com.ning.http.client.Response;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.ImmutableMap;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
public class TestInvoice extends TestJaxrsBase {
@@ -222,4 +226,71 @@ public class TestInvoice extends TestJaxrsBase {
assertTrue(cur.getAmount().compareTo(objFromJson.get(0).getAmount()) == 0);
}
}
+
+ @Test(groups = "slow")
+ public void testExternalPayment() throws Exception {
+ // Create an account with no payment method
+ final AccountJson accountJson = createAccount("eraahahildo", "sheqrgfhwe", "eraahahildo@yahoo.com");
+ assertNotNull(accountJson);
+
+ // Add a bundle, subscription and move the clock to get the first invoice
+ final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), "317199");
+ assertNotNull(bundleJson);
+ final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
+ assertNotNull(subscriptionJson);
+ clock.addMonths(1);
+ crappyWaitForLackOfProperSynchonization();
+
+ final String paymentsURI = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
+
+ // Verify we didn't get any payment
+ final Response noPaymentsResponse = doGet(paymentsURI, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(noPaymentsResponse.getStatusCode(), Status.OK.getStatusCode());
+ final String noPaymentsBaseJson = noPaymentsResponse.getResponseBody();
+ final List<PaymentJsonSimple> noPaymentsFromJson = mapper.readValue(noPaymentsBaseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+ assertEquals(noPaymentsFromJson.size(), 0);
+
+ // Get the invoices
+ final Map<String, String> queryParams = new HashMap<String, String>();
+ queryParams.put(JaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId());
+ final String invoicesURI = JaxrsResource.INVOICES_PATH;
+ final Response invoicesResponse = doGet(invoicesURI, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(invoicesResponse.getStatusCode(), Status.OK.getStatusCode());
+ final String invoicesBaseJson = invoicesResponse.getResponseBody();
+ final List<InvoiceJsonSimple> invoices = mapper.readValue(invoicesBaseJson, new TypeReference<List<InvoiceJsonSimple>>() {});
+ assertNotNull(invoices);
+ // 2 invoices but look for the non zero dollar one
+ assertEquals(invoices.size(), 2);
+ final String invoiceId = invoices.get(1).getInvoiceId();
+
+ // Post an external payment
+ final BigDecimal paidAmount = BigDecimal.TEN;
+ final PaymentJsonSimple payment = new PaymentJsonSimple(paidAmount, BigDecimal.ZERO, accountJson.getAccountId(),
+ invoiceId, null, null, null, null, 0,
+ null, null, null, null, null, null);
+ final String postJson = mapper.writeValueAsString(payment);
+ final String paymentURI = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.PAYMENTS;
+ final Response paymentResponse = doPost(paymentURI, postJson, ImmutableMap.<String, String>of("externalPayment", "true"), DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(paymentResponse.getStatusCode(), Status.CREATED.getStatusCode());
+
+ // Verify we indeed got the payment
+ final Response paymentsResponse = doGet(paymentsURI, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(paymentsResponse.getStatusCode(), Status.OK.getStatusCode());
+ final String paymentsBaseJson = paymentsResponse.getResponseBody();
+ final List<PaymentJsonSimple> paymentsFromJson = mapper.readValue(paymentsBaseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+ assertEquals(paymentsFromJson.size(), 1);
+ assertEquals(paymentsFromJson.get(0).getPaidAmount().compareTo(paidAmount), 0);
+
+ // Check the PaymentMethod from paymentMethodId returned in the Payment object
+ final String paymentMethodId = paymentsFromJson.get(0).getPaymentMethodId();
+ final String paymentMethodURI = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodId;
+
+ final Response paymentMethodResponse = doGet(paymentMethodURI, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(paymentMethodResponse.getStatusCode(), Status.OK.getStatusCode());
+ final PaymentMethodJson paymentMethodJson = mapper.readValue(paymentMethodResponse.getResponseBody(), PaymentMethodJson.class);
+ assertEquals(paymentMethodJson.getPaymentMethodId(), paymentMethodId);
+ assertEquals(paymentMethodJson.getAccountId(), accountJson.getAccountId());
+ assertEquals(paymentMethodJson.getPluginName(), ExternalPaymentProviderPlugin.PLUGIN_NAME);
+ assertNull(paymentMethodJson.getPluginInfo());
+ }
}
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index dcafe4e..298a452 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.25-SNAPSHOT</version>
+ <version>0.1.26-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>