killbill-aplcache

Payement refund implementation

7/3/2012 1:42:52 AM

Details

diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
new file mode 100644
index 0000000..8c4b812
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2011 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.jaxrs.json;
+
+import java.math.BigDecimal;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.ning.billing.payment.api.Refund;
+
+
+public class RefundJson {
+
+    private final String paymentId;
+    private final BigDecimal refundAmount;
+    private final Boolean isAdjusted;
+
+    public RefundJson(Refund input) {
+        this(input.getPaymentId().toString(), input.getRefundAmount(), input.isAdjusted());
+    }
+
+    @JsonCreator
+    public RefundJson(@JsonProperty("paymentId") String paymentId,
+            @JsonProperty("refundAmount") BigDecimal refundAmount,
+            @JsonProperty("isAdjusted") final Boolean isAdjusted) {
+        super();
+        this.paymentId = paymentId;
+        this.refundAmount = refundAmount;
+        this.isAdjusted = isAdjusted;
+    }
+
+    public RefundJson() {
+        this(null, null, null);
+    }
+
+    public String getPaymentId() {
+        return paymentId;
+    }
+
+    public BigDecimal getRefundAmount() {
+        return refundAmount;
+    }
+
+    public boolean isAdjusted() {
+        return isAdjusted;
+    }
+}
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 ad55ff9..a43b1b5 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
@@ -78,13 +78,35 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
 
+
+    @Override
+    public Refund getRefund(UUID refundId) throws PaymentApiException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
     @Override
-    public Refund createRefund(final Account account, final UUID paymentId, final CallContext context)
+    public Refund createRefund(Account account, UUID paymentId,
+            BigDecimal refundAmount, boolean isAdjusted, CallContext context)
             throws PaymentApiException {
-        return refundProcessor.createRefund(account, paymentId, context);
+        return refundProcessor.createRefund(account, paymentId, refundAmount, isAdjusted, context);
+
     }
 
     @Override
+    public List<Refund> getAccountRefunds(Account account)
+            throws PaymentApiException {
+        return refundProcessor.getAccountRefunds(account);
+    }
+
+    @Override
+    public List<Refund> getPaymentRefunds(UUID paymentId)
+            throws PaymentApiException {
+        return refundProcessor.getPaymentRefunds(paymentId);
+    }
+
+
+    @Override
     public Set<String> getAvailablePlugins() {
         return methodProcessor.getAvailablePlugins();
     }
@@ -118,6 +140,7 @@ public class DefaultPaymentApi implements PaymentApi {
         return methodProcessor.getPaymentMethods(account, withPluginDetail);
     }
 
+    @Override
     public PaymentMethod getPaymentMethodById(final UUID paymentMethodId)
             throws PaymentApiException {
         return methodProcessor.getPaymentMethodById(paymentMethodId);
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
new file mode 100644
index 0000000..729e701
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultRefund.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010-2011 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.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.EntityBase;
+
+public class DefaultRefund extends EntityBase implements Refund {
+
+    private final UUID paymentId;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final boolean isAdjusted;
+
+    public DefaultRefund(final UUID id, final UUID paymentId, final BigDecimal amount,
+            final Currency currency, final boolean isAdjusted) {
+        super(id);
+        this.paymentId = paymentId;
+        this.amount = amount;
+        this.currency = currency;
+        this.isAdjusted = isAdjusted;
+    }
+
+    @Override
+    public UUID getPaymentId() {
+        return paymentId;
+    }
+
+    @Override
+    public BigDecimal getRefundAmount() {
+        return amount;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public boolean isAdjusted() {
+        return isAdjusted;
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 4f21a9b..54fbeb9 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -16,52 +16,217 @@
 package com.ning.billing.payment.core;
 
 import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
 import com.google.inject.name.Named;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.payment.api.DefaultRefund;
 import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.payment.core.ProcessorBase.WithAccountLock;
+import com.ning.billing.payment.core.ProcessorBase.WithAccountLockCallback;
+import com.ning.billing.payment.dao.PaymentAttemptModelDao;
 import com.ning.billing.payment.dao.PaymentDao;
+import com.ning.billing.payment.dao.RefundModelDao;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.globallocker.GlobalLocker;
 
 import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
 
 public class RefundProcessor extends ProcessorBase {
 
+    private final static Logger log = LoggerFactory.getLogger(RefundProcessor.class);
+
+    private final InvoicePaymentApi invoicePaymentApi;
+    private final CallContextFactory factory;
+
     @Inject
     public RefundProcessor(final PaymentProviderPluginRegistry pluginRegistry,
-                           final AccountUserApi accountUserApi,
-                           final Bus eventBus,
-                           final PaymentDao paymentDao,
-                           final GlobalLocker locker,
-                           @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
+            final AccountUserApi accountUserApi,
+            final InvoicePaymentApi invoicePaymentApi,
+            final Bus eventBus,
+            final CallContextFactory factory,
+            final PaymentDao paymentDao,
+            final GlobalLocker locker,
+            @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
         super(pluginRegistry, accountUserApi, eventBus, paymentDao, locker, executor);
+        this.invoicePaymentApi = invoicePaymentApi;
+        this.factory = factory;
     }
 
-    public Refund createRefund(final Account account, final UUID paymentId, final CallContext context)
-            throws PaymentApiException {
-        /*
+
+    public Refund createRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final boolean isAdjusted, final CallContext context)
+    throws PaymentApiException {
+
+        return new WithAccountLock<Refund>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Refund>() {
+
+            @Override
+            public Refund doOperation() throws PaymentApiException {
+                try {
+
+
+                    final PaymentAttemptModelDao successfulAttempt = getPaymentAttempt(paymentId);
+                    if (successfulAttempt == null) {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentId);
+                    }
+                    if (successfulAttempt.getRequestedAmount().compareTo(refundAmount) < 0) {
+                        throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_TOO_LARGE);
+                    }
+
+                    // Look for that refund entry and count any 'similar' refund left in CREATED state (same amount, same paymentId)
+                    int foundPluginCompletedRefunds = 0;
+                    RefundModelDao refundInfo = null;
+                    List<RefundModelDao> existingRefunds = paymentDao.getRefundsForPayment(paymentId);
+                    for (RefundModelDao cur : existingRefunds) {
+                        if (cur.getAmount().compareTo(refundAmount) == 0) {
+                            if (cur.getRefundStatus() == RefundStatus.CREATED) {
+                                if (refundInfo == null) {
+                                    refundInfo = cur;
+                                }
+                            } else {
+                                foundPluginCompletedRefunds++;
+                            }
+                        }
+                    }
+                    if (refundInfo == null) {
+                        refundInfo = new RefundModelDao(account.getId(), paymentId, refundAmount, account.getCurrency(), isAdjusted);
+                        paymentDao.insertRefund(refundInfo, context);
+                    }
+
+                    final PaymentPluginApi plugin = getPaymentProviderPlugin(account);
+                    int nbExistingRefunds = plugin.getNbRefundForPaymentAmount(account, paymentId, refundAmount);
+                    if (nbExistingRefunds > foundPluginCompletedRefunds) {
+                        log.info("Found existing plugin refund for paymentId {}, skip plugin", paymentId);
+                    } else {
+                        // If there is no such exitng refund we create it
+                        plugin.processRefund(account, paymentId, refundAmount);
+                    }
+                    paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, context);
+
+                    invoicePaymentApi.createRefund(successfulAttempt.getId(), refundAmount, isAdjusted, refundInfo.getId(), context);
+
+                    paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.COMPLETED, context);
+
+                    return new DefaultRefund(refundInfo.getId(), paymentId, refundInfo.getAmount(), account.getCurrency(), isAdjusted);
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), e.getMessage());
+                } catch (InvoiceApiException e) {
+                    throw new PaymentApiException(e);
+                }
+            }
+        });
+    }
+
+
+    public Refund getRefund(final UUID refundId)
+    throws PaymentApiException {
+        RefundModelDao result = paymentDao.getRefund(refundId);
+        if (result == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_REFUND, refundId);
+        }
+        List<RefundModelDao> filteredInput = filterUncompletedPluginRefund(Collections.singletonList(result));
+        if (filteredInput.size() == 0) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_REFUND, refundId);
+        }
+        if (completePluginCompletedRefund(filteredInput)) {
+            result = paymentDao.getRefund(refundId);
+        }
+        return new DefaultRefund(result.getId(), result.getPaymentId(), result.getAmount(), result.getCurrency(), result.isAdjsuted());
+    }
+
+
+    public List<Refund> getAccountRefunds(final Account account)
+    throws PaymentApiException {
+        List<RefundModelDao> result = paymentDao.getRefundsForAccount(account.getId());
+        if (completePluginCompletedRefund(result)) {
+            result = paymentDao.getRefundsForAccount(account.getId());
+        }
+        List<RefundModelDao> filteredInput = filterUncompletedPluginRefund(result);
+        return toRefunds(filteredInput);
+    }
+
+    public List<Refund> getPaymentRefunds(final UUID paymentId)
+    throws PaymentApiException {
+        List<RefundModelDao> result = paymentDao.getRefundsForPayment(paymentId);
+        if (completePluginCompletedRefund(result)) {
+            result = paymentDao.getRefundsForPayment(paymentId);
+        }
+        List<RefundModelDao> filteredInput = filterUncompletedPluginRefund(result);
+        return toRefunds(filteredInput);
+    }
+
+    public List<Refund> toRefunds(final List<RefundModelDao> in) {
+        return new ArrayList<Refund>(Collections2.transform(in, new Function<RefundModelDao, Refund>() {
+            @Override
+            public Refund apply(RefundModelDao cur) {
+                return new DefaultRefund(cur.getId(), cur.getPaymentId(), cur.getAmount(), cur.getCurrency(), cur.isAdjsuted());
+            }
+        }));
+    }
+
+    private List<RefundModelDao> filterUncompletedPluginRefund(final List<RefundModelDao> input) {
+        return new ArrayList<RefundModelDao>(Collections2.filter(input, new Predicate<RefundModelDao>() {
+            @Override
+            public boolean apply(RefundModelDao in) {
+                return in.getRefundStatus() != RefundStatus.CREATED;
+            }
+        }));
+    }
+
+    private boolean completePluginCompletedRefund(final List<RefundModelDao> refunds) throws PaymentApiException {
+
+        boolean fixedTheWorld = false;
         try {
-            
-        final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
-        List<PaymentInfoPlugin> result = plugin.processRefund(account);
-        List<PaymentInfoEvent> info =  new LinkedList<PaymentInfoEvent>();
-        int i = 0;
-        for (PaymentInfoPlugin cur : result) {
-            // STEPH
-            //info.add(new DefaultPaymentInfoEvent(cur, account.getId(), invoiceIds.get(i)));
+            final CallContext context = factory.createCallContext("RefundProcessor", CallOrigin.INTERNAL, UserType.SYSTEM);
+            for (RefundModelDao cur : refunds) {
+                if (cur.getRefundStatus() == RefundStatus.PLUGIN_COMPLETED) {
+                    final PaymentAttemptModelDao successfulAttempt = getPaymentAttempt(cur.getPaymentId());
+                    if (successfulAttempt != null) {
+                        invoicePaymentApi.createRefund(successfulAttempt.getId(), cur.getAmount(), cur.isAdjsuted(), cur.getId(), context);
+                        paymentDao.updateRefundStatus(cur.getId(), RefundStatus.COMPLETED, context);
+                        fixedTheWorld = true;
+                    }
+                }
+            }
+        } catch (InvoiceApiException e) {
+            throw new PaymentApiException(e);
         }
-        return info;
-        } catch (PaymentPluginApiException e) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_REFUND, account.getId(), e.getMessage());
+        return fixedTheWorld;
+    }
+
+    private PaymentAttemptModelDao getPaymentAttempt(final UUID paymentId) {
+        List<PaymentAttemptModelDao> attempts = paymentDao.getAttemptsForPayment(paymentId);
+        for (PaymentAttemptModelDao cur : attempts) {
+            if (cur.getPaymentStatus() == PaymentStatus.SUCCESS) {
+                return cur;
+            }
         }
-        */
         return null;
     }
+
 }
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 5dad606..3c39f60 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -25,6 +25,7 @@ import org.skife.jdbi.v2.TransactionStatus;
 
 import com.google.inject.Inject;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
 import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
@@ -34,9 +35,11 @@ import com.ning.billing.util.dao.TableName;
 
 public class AuditedPaymentDao implements PaymentDao {
 
+
     private final PaymentSqlDao paymentSqlDao;
     private final PaymentAttemptSqlDao paymentAttemptSqlDao;
     private final PaymentMethodSqlDao paymentMethodSqlDao;
+    private final RefundSqlDao refundSqlDao;
     //private final TimedoutPaymentRetryServiceScheduler timedoutSchduler;
 
     @Inject
@@ -44,6 +47,7 @@ public class AuditedPaymentDao implements PaymentDao {
         this.paymentSqlDao = dbi.onDemand(PaymentSqlDao.class);
         this.paymentAttemptSqlDao = dbi.onDemand(PaymentAttemptSqlDao.class);
         this.paymentMethodSqlDao = dbi.onDemand(PaymentMethodSqlDao.class);
+        this.refundSqlDao = dbi.onDemand(RefundSqlDao.class);
         // this.timedoutSchduler = timedoutSchduler;
     }
 
@@ -92,10 +96,10 @@ public class AuditedPaymentDao implements PaymentDao {
         }).size();
     }
 
-    
+
     private void scheduleTimeoutRetryFromTransaction(final UUID paymentId, final PaymentAttemptSqlDao transactional, final boolean scheduleTimeoutRetry) {
 
-        if (scheduleTimeoutRetry) { 
+        if (scheduleTimeoutRetry) {
             int retryAttempt = getNbTimedoutAttemptsFromTransaction(paymentId, transactional) + 1;
             timedoutSchduler.scheduleRetryFromTransaction(paymentId, retryAttempt, transactional);
         }
@@ -225,6 +229,74 @@ public class AuditedPaymentDao implements PaymentDao {
     }
 
     @Override
+    public RefundModelDao insertRefund(final RefundModelDao refundInfo, final CallContext context) {
+        return refundSqlDao.inTransaction(new Transaction<RefundModelDao, RefundSqlDao>() {
+
+            @Override
+            public RefundModelDao inTransaction(RefundSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                transactional.insertRefund(refundInfo, context);
+                return refundInfo;
+            }
+        });
+    }
+
+
+    @Override
+    public void updateRefundStatus(final UUID refundId,
+            final RefundStatus status, final CallContext context) {
+        refundSqlDao.inTransaction(new Transaction<Void, RefundSqlDao>() {
+
+            @Override
+            public Void inTransaction(RefundSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                transactional.updateStatus(refundId.toString(), status.toString());
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public RefundModelDao getRefund(final UUID refundId) {
+        return refundSqlDao.inTransaction(new Transaction<RefundModelDao, RefundSqlDao>() {
+
+            @Override
+            public RefundModelDao inTransaction(RefundSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                return transactional.getById(refundId.toString());
+            }
+        });
+    }
+
+
+    @Override
+    public List<RefundModelDao> getRefundsForPayment(final UUID paymentId) {
+        return refundSqlDao.inTransaction(new Transaction<List<RefundModelDao>, RefundSqlDao>() {
+
+            @Override
+            public List<RefundModelDao> inTransaction(RefundSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                return transactional.getRefundsForPayment(paymentId.toString());
+            }
+        });
+    }
+
+
+    @Override
+    public List<RefundModelDao> getRefundsForAccount(final UUID accountId) {
+        return refundSqlDao.inTransaction(new Transaction<List<RefundModelDao>, RefundSqlDao>() {
+
+            @Override
+            public List<RefundModelDao> inTransaction(RefundSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                return transactional.getRefundsForAccount(accountId.toString());
+            }
+        });
+    }
+
+
+
+    @Override
     public PaymentMethodModelDao getPaymentMethod(final UUID paymentMethodId) {
         return paymentMethodSqlDao.inTransaction(new Transaction<PaymentMethodModelDao, PaymentMethodSqlDao>() {
             @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 6580217..65254fb 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -18,8 +18,14 @@ package com.ning.billing.payment.dao;
 import java.util.List;
 import java.util.UUID;
 
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.payment.dao.RefundSqlDao.RefundModelDaoBinder;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
 
 public interface PaymentDao {
 
@@ -43,6 +49,16 @@ public interface PaymentDao {
 
     public List<PaymentAttemptModelDao> getAttemptsForPayment(final UUID paymentId);
 
+    public RefundModelDao insertRefund(RefundModelDao refundInfo, final CallContext context);
+
+    public void updateRefundStatus(UUID refundId, RefundStatus status, final CallContext context);
+
+    public RefundModelDao getRefund(UUID refundId);
+
+    public List<RefundModelDao> getRefundsForPayment(final UUID paymentId);
+
+    public List<RefundModelDao> getRefundsForAccount(final UUID accountId);
+
     public PaymentMethodModelDao insertPaymentMethod(final PaymentMethodModelDao paymentMethod, final CallContext context);
 
     public PaymentMethodModelDao getPaymentMethod(final UUID paymentMethodId);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
index 6ee7ea1..ab1fecc 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentModelDao.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 4173ac2..f637976 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
new file mode 100644
index 0000000..286fe51
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2011 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.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.EntityBase;
+
+
+public class RefundModelDao extends EntityBase {
+
+    private final UUID accountId;
+    private final UUID paymentId;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final boolean isAdjusted;
+    private final RefundStatus refundStatus;
+
+    public RefundModelDao(final UUID accountId, final UUID paymentId,
+            final BigDecimal amount, final Currency currency, final boolean isAdjusted) {
+        this(UUID.randomUUID(), accountId, paymentId, amount, currency, isAdjusted, RefundStatus.CREATED);
+    }
+
+    public RefundModelDao(final UUID id, final UUID accountId, final UUID paymentId,
+            final BigDecimal amount, final Currency currency, final boolean isAdjusted, final RefundStatus refundStatus) {
+        super(id);
+        this.accountId = accountId;
+        this.paymentId = paymentId;
+        this.amount = amount;
+        this.currency = currency;
+        this.refundStatus = refundStatus;
+        this.isAdjusted = isAdjusted;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public UUID getPaymentId() {
+        return paymentId;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public RefundStatus getRefundStatus() {
+        return refundStatus;
+    }
+
+    public boolean isAdjsuted() {
+        return isAdjusted;
+    }
+
+    public enum RefundStatus {
+        CREATED,
+        PLUGIN_COMPLETED,
+        COMPLETED,
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
new file mode 100644
index 0000000..c29c6ed
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2010-2011 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.dao;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
+
+
+@ExternalizedSqlViaStringTemplate3()
+@RegisterMapper(RefundSqlDao.RefundModelDaoMapper.class)
+public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEntitySqlDao<RefundModelDao>, Transmogrifier, CloseMe {
+
+
+    @SqlUpdate
+    void insertRefund(@Bind(binder = RefundModelDaoBinder.class) final RefundModelDao refundInfo,
+                       @CallContextBinder final CallContext context);
+
+    @SqlUpdate
+    void updateStatus(@Bind("id") final String refundId, @Bind("refund_status") final String status);
+
+    @SqlQuery
+    RefundModelDao getRefund(@Bind("id") final String refundId);
+
+    @SqlQuery
+    List<RefundModelDao> getRefundsForPayment(@Bind("paymentId") final String paymentId);
+
+    @SqlQuery
+    List<RefundModelDao> getRefundsForAccount(@Bind("accountId") final String accountId);
+
+
+    public static final class RefundModelDaoBinder extends BinderBase implements Binder<Bind, RefundModelDao> {
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") final SQLStatement stmt, final Bind bind, final RefundModelDao refund) {
+            stmt.bind("id", refund.getId().toString());
+            stmt.bind("accountId", refund.getAccountId().toString());
+            stmt.bind("paymentId", refund.getPaymentId().toString());
+            stmt.bind("amount", refund.getAmount());
+            stmt.bind("currency", refund.getCurrency().toString());
+            stmt.bind("is_adjusted", refund.isAdjsuted());
+            stmt.bind("refund_status", refund.getRefundStatus().toString());
+        }
+    }
+
+    public static class RefundModelDaoMapper extends MapperBase implements ResultSetMapper<RefundModelDao> {
+
+        @Override
+        public RefundModelDao map(final int index, final ResultSet rs, final StatementContext ctx)
+                throws SQLException {
+            final UUID id = getUUID(rs, "id");
+            final UUID accountId = getUUID(rs, "account_id");
+            final UUID paymentId = getUUID(rs, "payment_id");
+            final BigDecimal amount = rs.getBigDecimal("amount");
+            final boolean isAdjusted = rs.getBoolean("is_adjusted");
+            final Currency currency = Currency.valueOf(rs.getString("currency"));
+            final RefundStatus refundStatus = RefundStatus.valueOf(rs.getString("refund_status"));
+            return new RefundModelDao(id, accountId, paymentId, amount, currency, isAdjusted, refundStatus);
+        }
+    }
+}
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 5591937..cf23c59 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
@@ -192,8 +192,15 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
-    public List<PaymentInfoPlugin> processRefund(final Account account)
-            throws PaymentPluginApiException {
-        return null;
+    public void processRefund(Account account, UUID paymentId,
+            BigDecimal refundAmout) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public int getNbRefundForPaymentAmount(Account account, UUID paymentId,
+            BigDecimal refundAmount) throws PaymentPluginApiException {
+        // TODO Auto-generated method stub
+        return 0;
     }
+
 }