killbill-aplcache
Changes
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)
Details
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;
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/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);
+ }
+}