killbill-memoizeit

Finish refund implementation Add basic tests for refund (server

7/4/2012 4:32:14 PM

Changes

Details

diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index e82b61c..d906f1c 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -230,7 +230,7 @@ public class TestAnalyticsService extends TestWithEmbeddedDB {
         invoiceCreationNotification = new DefaultInvoiceCreationEvent(invoice.getId(), account.getId(),
                                                                       INVOICE_AMOUNT, ACCOUNT_CURRENCY, clock.getUTCNow(), null);
 
-        paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoices.get(0).getId(), null, invoices.get(0).getBalance(), -1, PaymentStatus.UNKNOWN, null, new DateTime());
+        paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoices.get(0).getId(), null, invoices.get(0).getBalance(), -1, PaymentStatus.UNKNOWN, null, null, new DateTime());
 
         //STEPH talk to Pierre
         /*
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
index 8c5a1ef..8bbae11 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfoEvent.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -38,4 +38,6 @@ public interface PaymentInfoEvent extends Entity, BusEvent {
     public Integer getPaymentNumber();
 
     public PaymentStatus getStatus();
+
+    public String getExtPaymentRefId();
 }
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
index 3e8b563..eeb356a 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -38,4 +38,6 @@ public interface PaymentInfoPlugin {
     public String getGatewayError();
 
     public String getGatewayErrorCode();
+
+    public String getExternalReferenceId();
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index b09e347..7c344fe 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -315,12 +315,17 @@ public class DefaultInvoiceDao implements InvoiceDao {
                     throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_IS_POSITIVE);
                 }
 
-                // No that we check signs, let's work with positive numbers, this makes things simpler
+                // Now that we checked signs, let's work with positive numbers, this makes things simpler
                 final BigDecimal requestedPositiveAmount = requestedAmount.negate();
                 if (requestedPositiveAmount.compareTo(maxRefundAmount) > 0) {
                     throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedPositiveAmount, maxRefundAmount);
                 }
 
+                // Before we go further, check if that refund already got inserted
+                final InvoicePayment existingRefund = transactional.getPaymentsForCookieId(paymentCookieId.toString());
+                if (existingRefund != null) {
+                    return existingRefund;
+                }
 
                 final InvoicePayment refund = new DefaultInvoicePayment(UUID.randomUUID(), InvoicePaymentType.REFUND, paymentAttemptId,
                         payment.getInvoiceId(), context.getCreatedDate(), requestedPositiveAmount.negate(), payment.getCurrency(), paymentCookieId, payment.getId());
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 6ca968d..6bf0d10 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -83,6 +83,10 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
     @SqlQuery
     InvoicePayment getInvoicePayment(@Bind("paymentAttemptId") final String paymentAttemptId);
 
+    @SqlQuery
+    InvoicePayment getPaymentsForCookieId(@Bind("paymentCookieId") final String paymentCookieId);
+
+
     @SqlUpdate
     void notifyOfPaymentAttempt(@InvoicePaymentBinder final InvoicePayment invoicePayment,
                                 @CallContextBinder final CallContext context);
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index a028cf6..02dffc5 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -43,6 +43,12 @@ getById() ::= <<
   WHERE id = :id;
 >>
 
+getPaymentsForCookieId() ::= <<
+  SELECT <invoicePaymentFields()>
+  FROM invoice_payments
+  WHERE payment_cookie_id = :paymentCookieId;
+>>
+
 getPaymentsForInvoice() ::= <<
   SELECT <invoicePaymentFields()>
   FROM invoice_payments
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
index bf8c4a4..ebeb7d0 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
@@ -584,7 +584,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
 
-        invoiceDao.createRefund(paymentAttemptId, refund1, withAdjustment, null, context);
+        invoiceDao.createRefund(paymentAttemptId, refund1, withAdjustment, UUID.randomUUID(), context);
         balance = invoiceDao.getAccountBalance(accountId);
         if (withAdjustment) {
             assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
@@ -678,7 +678,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertEquals(cba.compareTo(new BigDecimal("10.00")), 0);
 
         // PARTIAL REFUND on the payment
-        invoiceDao.createRefund(paymentAttemptId, refundAmount, withAdjustment, null, context);
+        invoiceDao.createRefund(paymentAttemptId, refundAmount, withAdjustment, UUID.randomUUID(), context);
 
         balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(expectedFinalBalance), 0);
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
index 8c4b812..f65f6d7 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/RefundJson.java
@@ -25,26 +25,32 @@ import com.ning.billing.payment.api.Refund;
 
 public class RefundJson {
 
+    private final String refundId;
     private final String paymentId;
     private final BigDecimal refundAmount;
     private final Boolean isAdjusted;
 
     public RefundJson(Refund input) {
-        this(input.getPaymentId().toString(), input.getRefundAmount(), input.isAdjusted());
+        this(input.getId().toString(), input.getPaymentId().toString(), input.getRefundAmount(), input.isAdjusted());
     }
 
     @JsonCreator
-    public RefundJson(@JsonProperty("paymentId") String paymentId,
+    public RefundJson(@JsonProperty("refund_id") final String refundId,
+            @JsonProperty("paymentId") String paymentId,
             @JsonProperty("refundAmount") BigDecimal refundAmount,
-            @JsonProperty("isAdjusted") final Boolean isAdjusted) {
-        super();
+            @JsonProperty("adjusted") final Boolean isAdjusted) {
+        this.refundId = refundId;
         this.paymentId = paymentId;
         this.refundAmount = refundAmount;
         this.isAdjusted = isAdjusted;
     }
 
     public RefundJson() {
-        this(null, null, null);
+        this(null, null, null, null);
+    }
+
+    public String getRefundId() {
+        return refundId;
     }
 
     public String getPaymentId() {
@@ -58,4 +64,62 @@ public class RefundJson {
     public boolean isAdjusted() {
         return isAdjusted;
     }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((isAdjusted == null) ? 0 : isAdjusted.hashCode());
+        result = prime * result
+                + ((paymentId == null) ? 0 : paymentId.hashCode());
+        result = prime * result
+                + ((refundAmount == null) ? 0 : refundAmount.hashCode());
+        result = prime * result
+                + ((refundId == null) ? 0 : refundId.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (! this.equalsNoId(obj)) {
+            return false;
+        } else {
+            RefundJson other = (RefundJson) obj;
+            if (getRefundId() == null) {
+                return other.getRefundId() == null;
+            } else {
+                return getRefundId().equals(other.getRefundId());
+            }
+        }
+    }
+
+    public boolean equalsNoId(final Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        RefundJson other = (RefundJson) obj;
+        if (isAdjusted == null) {
+            if (other.isAdjusted != null)
+                return false;
+        } else if (!isAdjusted.equals(other.isAdjusted))
+            return false;
+        if (paymentId == null) {
+            if (other.paymentId != null)
+                return false;
+        } else if (!paymentId.equals(other.paymentId))
+            return false;
+        if (refundAmount == null) {
+            if (other.refundAmount != null)
+                return false;
+        } else if (!refundAmount.equals(other.refundAmount)) {
+            return false;
+        }
+        return true;
+    }
+
+
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 264c620..faaccb5 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -26,6 +26,7 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Response.Status;
 
 import java.util.ArrayList;
@@ -109,7 +110,8 @@ public class PaymentResource extends JaxRsResourceBase {
             @PathParam("paymentId") final String paymentId,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
-            @HeaderParam(HDR_COMMENT) final String comment) {
+            @HeaderParam(HDR_COMMENT) final String comment,
+            @javax.ws.rs.core.Context final UriInfo uriInfo) {
 
         try {
             final UUID paymentUuid = UUID.fromString(paymentId);
@@ -118,7 +120,7 @@ public class PaymentResource extends JaxRsResourceBase {
             final Account account = accountApi.getAccountById(payment.getAccountId());
 
             Refund result = paymentApi.createRefund(account, paymentUuid, json.getRefundAmount(), json.isAdjusted(), context.createContext(createdBy, reason, comment));
-            return uriBuilder.buildResponse(RefundResource.class, "getRefund", result.getId());
+            return uriBuilder.buildResponse(RefundResource.class, "getRefund", result.getId(), uriInfo.getBaseUri().toString());
         } catch (AccountApiException e) {
             if (e.getCode() == ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID.getCode()) {
                 return Response.status(Status.NO_CONTENT).build();
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/RefundResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/RefundResource.java
index 63e72c0..6a5a729 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/RefundResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/RefundResource.java
@@ -33,6 +33,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
@@ -71,8 +72,11 @@ public class RefundResource extends JaxRsResourceBase {
             Refund refund = paymentApi.getRefund(UUID.fromString(refundId));
             return Response.status(Status.OK).entity(new RefundJson(refund)).build();
         } catch (PaymentApiException e) {
-            // STEPH NO_CONTENT if oes not exist
-            return Response.status(Status.BAD_REQUEST).build();
+            if (e.getCode() == ErrorCode.PAYMENT_NO_SUCH_REFUND.getCode()) {
+                return Response.status(Status.NO_CONTENT).build();
+            } else {
+                return Response.status(Status.BAD_REQUEST).build();
+            }
         }
     }
 
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 a43b1b5..4968a24 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
@@ -81,8 +81,7 @@ public class DefaultPaymentApi implements PaymentApi {
 
     @Override
     public Refund getRefund(UUID refundId) throws PaymentApiException {
-        // TODO Auto-generated method stub
-        return null;
+        return refundProcessor.getRefund(refundId);
     }
 
     @Override
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
index a34f71e..e284d6a 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
@@ -36,6 +36,7 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
     private final PaymentStatus status;
     private final UUID userToken;
     private final DateTime effectiveDate;
+    private final String extPaymentRefId;
 
     @JsonCreator
     public DefaultPaymentInfoEvent(@JsonProperty("id") final UUID id,
@@ -45,6 +46,7 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
                                    @JsonProperty("amount") final BigDecimal amount,
                                    @JsonProperty("paymentNumber") final Integer paymentNumber,
                                    @JsonProperty("status") final PaymentStatus status,
+                                   @JsonProperty("extPaymentRefId") final String extPaymentRefId,
                                    @JsonProperty("userToken") final UUID userToken,
                                    @JsonProperty("effectiveDate") final DateTime effectiveDate) {
         super(id);
@@ -54,6 +56,7 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
         this.amount = amount;
         this.paymentNumber = paymentNumber;
         this.status = status;
+        this.extPaymentRefId = extPaymentRefId;
         this.userToken = userToken;
         this.effectiveDate = effectiveDate;
     }
@@ -61,8 +64,8 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
 
     public DefaultPaymentInfoEvent(final UUID accountId, final UUID invoiceId,
                                    final UUID paymentId, final BigDecimal amount, final Integer paymentNumber,
-                                   final PaymentStatus status, final UUID userToken, final DateTime effectiveDate) {
-        this(UUID.randomUUID(), accountId, invoiceId, paymentId, amount, paymentNumber, status, userToken, effectiveDate);
+                                   final PaymentStatus status, final String extPaymentRefId, final UUID userToken, final DateTime effectiveDate) {
+        this(UUID.randomUUID(), accountId, invoiceId, paymentId, amount, paymentNumber, status, extPaymentRefId, userToken, effectiveDate);
     }
 
     public DefaultPaymentInfoEvent(final DefaultPaymentInfoEvent src) {
@@ -73,6 +76,7 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
              src.amount,
              src.paymentNumber,
              src.status,
+             src.extPaymentRefId,
              src.userToken,
              src.effectiveDate);
     }
@@ -132,6 +136,11 @@ public class DefaultPaymentInfoEvent extends EntityBase implements PaymentInfoEv
         return status;
     }
 
+    @Override
+    public String getExtPaymentRefId() {
+        return extPaymentRefId;
+    }
+
 
     @Override
     public int hashCode() {
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 03c38ef..f22bb8e 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
@@ -223,7 +223,7 @@ public class PaymentProcessor extends ProcessorBase {
                 @Override
                 public Void doOperation() throws PaymentApiException {
 
-                    // Fetch gain with account lock this time
+                    // Fetch again with account lock this time
                     final PaymentModelDao payment = paymentDao.getPayment(paymentId);
                     boolean foundExpectedState = false;
                     for (final PaymentStatus cur : expectedPaymentStates) {
@@ -295,7 +295,7 @@ public class PaymentProcessor extends ProcessorBase {
             case PROCESSED:
                 // Update Payment/PaymentAttempt status
                 paymentStatus = PaymentStatus.SUCCESS;
-                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, null, attemptInput.getId(), context);
+                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, null, paymentPluginInfo.getExternalReferenceId(), attemptInput.getId(), context);
 
                 // Fetch latest objects
                 allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId());
@@ -311,7 +311,8 @@ public class PaymentProcessor extends ProcessorBase {
 
                 // Create Bus event
                 event = new DefaultPaymentInfoEvent(account.getId(),
-                        invoice.getId(), payment.getId(), payment.getAmount(), payment.getPaymentNumber(), paymentStatus, context.getUserToken(), payment.getEffectiveDate());
+                        invoice.getId(), payment.getId(), payment.getAmount(), payment.getPaymentNumber(), paymentStatus,
+                        paymentPluginInfo.getExternalReferenceId(), context.getUserToken(), payment.getEffectiveDate());
                 break;
 
             case ERROR:
@@ -326,7 +327,7 @@ public class PaymentProcessor extends ProcessorBase {
                     paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
                 }
 
-                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayError(), attemptInput.getId(), context);
+                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayError(), null, attemptInput.getId(), context);
 
                 log.info(String.format("Could not process payment for account %s, invoice %s, error = %s",
                         account.getId(), invoice.getId(), paymentPluginInfo.getGatewayError()));
@@ -347,7 +348,7 @@ public class PaymentProcessor extends ProcessorBase {
             paymentStatus = isInstantPayment ? PaymentStatus.PAYMENT_FAILURE_ABORTED : scheduleRetryOnPluginFailure(paymentInput.getId());
             // STEPH message might need truncation to fit??
 
-            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, e.getMessage(), attemptInput.getId(), context);
+            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus,  e.getMessage(), null, attemptInput.getId(), context);
 
             throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), e.getMessage());
 
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 54fbeb9..92c4544 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
@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -33,6 +34,7 @@ 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.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
@@ -123,7 +125,7 @@ public class RefundProcessor extends ProcessorBase {
                     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
+                        // If there is no such existng refund we create it
                         plugin.processRefund(account, paymentId, refundAmount);
                     }
                     paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, context);
@@ -153,6 +155,7 @@ public class RefundProcessor extends ProcessorBase {
         if (filteredInput.size() == 0) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_REFUND, refundId);
         }
+
         if (completePluginCompletedRefund(filteredInput)) {
             result = paymentDao.getRefund(refundId);
         }
@@ -200,23 +203,42 @@ public class RefundProcessor extends ProcessorBase {
 
     private boolean completePluginCompletedRefund(final List<RefundModelDao> refunds) throws PaymentApiException {
 
-        boolean fixedTheWorld = false;
+
+        final Collection<RefundModelDao> refundsToBeFixed = Collections2.filter(refunds, new Predicate<RefundModelDao>() {
+            @Override
+            public boolean apply(RefundModelDao in) {
+                return in.getRefundStatus() == RefundStatus.PLUGIN_COMPLETED;
+            }
+        });
+        if (refundsToBeFixed.size() == 0) {
+            return false;
+        }
+
         try {
-            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;
+            Account account = accountUserApi.getAccountById(refundsToBeFixed.iterator().next().getAccountId());
+            new WithAccountLock<Void>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void>() {
+
+                @Override
+                public Void doOperation() throws PaymentApiException {
+                    try {
+                        final CallContext context = factory.createCallContext("RefundProcessor", CallOrigin.INTERNAL, UserType.SYSTEM);
+                        for (RefundModelDao cur : refundsToBeFixed) {
+                            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);
+                            }
+                        }
+                    } catch (InvoiceApiException e) {
+                        throw new PaymentApiException(e);
                     }
+                    return null;
                 }
-            }
-        } catch (InvoiceApiException e) {
+            });
+            return true;
+        } catch (AccountApiException e) {
             throw new PaymentApiException(e);
         }
-        return fixedTheWorld;
     }
 
     private PaymentAttemptModelDao getPaymentAttempt(final UUID paymentId) {
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 3c39f60..4ecbe1f 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
@@ -143,31 +143,16 @@ public class AuditedPaymentDao implements PaymentDao {
         });
     }
 
-
-    @Override
-    public void updateStatusForPayment(final UUID paymentId,
-                                       final PaymentStatus paymentStatus, final CallContext context) {
-        paymentSqlDao.inTransaction(new Transaction<Void, PaymentSqlDao>() {
-
-            @Override
-            public Void inTransaction(final PaymentSqlDao transactional,
-                                      final TransactionStatus status) throws Exception {
-                updatePaymentStatusFromTransaction(paymentId, paymentStatus, context, transactional);
-                return null;
-            }
-        });
-    }
-
     @Override
     public void updateStatusForPaymentWithAttempt(final UUID paymentId,
-                                                  final PaymentStatus paymentStatus, final String paymentError, final UUID attemptId,
+                                                  final PaymentStatus paymentStatus, final String paymentError, final String extPaymentRefId, final UUID attemptId,
                                                   final CallContext context) {
         paymentSqlDao.inTransaction(new Transaction<Void, PaymentSqlDao>() {
 
             @Override
             public Void inTransaction(final PaymentSqlDao transactional,
                                       final TransactionStatus status) throws Exception {
-                updatePaymentStatusFromTransaction(paymentId, paymentStatus, context, transactional);
+                updatePaymentStatusFromTransaction(paymentId, paymentStatus, extPaymentRefId, context, transactional);
                 final PaymentAttemptSqlDao transPaymentAttemptSqlDao = transactional.become(PaymentAttemptSqlDao.class);
                 updatePaymentAttemptStatusFromTransaction(attemptId, paymentStatus, paymentError, context, transPaymentAttemptSqlDao);
                 return null;
@@ -186,8 +171,8 @@ public class AuditedPaymentDao implements PaymentDao {
         transactional.insertAuditFromTransaction(audit, context);
     }
 
-    private void updatePaymentStatusFromTransaction(final UUID paymentId, final PaymentStatus paymentStatus, final CallContext context, final PaymentSqlDao transactional) {
-        transactional.updatePaymentStatus(paymentId.toString(), paymentStatus.toString(), context);
+    private void updatePaymentStatusFromTransaction(final UUID paymentId, final PaymentStatus paymentStatus, final String extPaymentRefId, final CallContext context, final PaymentSqlDao transactional) {
+        transactional.updatePaymentStatusAndExtRef(paymentId.toString(), paymentStatus.toString(), extPaymentRefId, context);
         final PaymentModelDao savedPayment = transactional.getPayment(paymentId.toString());
         final Long recordId = transactional.getRecordId(savedPayment.getId().toString());
         final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.UPDATE);
@@ -235,8 +220,16 @@ public class AuditedPaymentDao implements PaymentDao {
             @Override
             public RefundModelDao inTransaction(RefundSqlDao transactional,
                     TransactionStatus status) throws Exception {
+
                 transactional.insertRefund(refundInfo, context);
-                return refundInfo;
+                final RefundModelDao savedRefund = transactional.getRefund(refundInfo.getId().toString());
+                final Long recordId = transactional.getRecordId(savedRefund.getId().toString());
+                final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.INSERT);
+                transactional.insertHistoryFromTransaction(history, context);
+                final Long historyRecordId = transactional.getHistoryRecordId(recordId);
+                final EntityAudit audit = new EntityAudit(TableName.REFUNDS, historyRecordId, ChangeType.INSERT);
+                transactional.insertAuditFromTransaction(audit, context);
+                return savedRefund;
             }
         });
     }
@@ -244,13 +237,21 @@ public class AuditedPaymentDao implements PaymentDao {
 
     @Override
     public void updateRefundStatus(final UUID refundId,
-            final RefundStatus status, final CallContext context) {
+            final RefundStatus refundStatus, 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());
+                transactional.updateStatus(refundId.toString(), refundStatus.toString());
+
+                final RefundModelDao savedRefund = transactional.getRefund(refundId.toString());
+                final Long recordId = transactional.getRecordId(savedRefund.getId().toString());
+                final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.UPDATE);
+                transactional.insertHistoryFromTransaction(history, context);
+                final Long historyRecordId = transactional.getHistoryRecordId(recordId);
+                final EntityAudit audit = new EntityAudit(TableName.REFUNDS, historyRecordId, ChangeType.UPDATE);
+                transactional.insertAuditFromTransaction(audit, context);
                 return null;
             }
         });
@@ -263,7 +264,7 @@ public class AuditedPaymentDao implements PaymentDao {
             @Override
             public RefundModelDao inTransaction(RefundSqlDao transactional,
                     TransactionStatus status) throws Exception {
-                return transactional.getById(refundId.toString());
+                return transactional.getRefund(refundId.toString());
             }
         });
     }
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 65254fb..711ae93 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
@@ -18,14 +18,9 @@ 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 {
 
@@ -34,10 +29,7 @@ public interface PaymentDao {
 
     public PaymentAttemptModelDao insertNewAttemptForPayment(final UUID paymentId, final PaymentAttemptModelDao attempt, final boolean scheduleTimeoutRetry, final CallContext context);
 
-
-    public void updateStatusForPayment(final UUID paymentId, final PaymentStatus paymentStatus, final CallContext context);
-
-    public void updateStatusForPaymentWithAttempt(final UUID paymentId, final PaymentStatus paymentStatus, final String paymentError, final UUID attemptId, final CallContext context);
+    public void updateStatusForPaymentWithAttempt(final UUID paymentId, final PaymentStatus paymentStatus, final String paymentError, final String extPaymentRefId, final UUID attemptId, final CallContext context);
 
     public PaymentAttemptModelDao getPaymentAttempt(final UUID attemptId);
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java
index a68de8c..d47cf05 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentHistoryBinder.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -49,6 +49,7 @@ public @interface PaymentHistoryBinder {
                     q.bind("amount", payment.getAmount());
                     q.bind("currency", payment.getCurrency().toString());
                     q.bind("paymentStatus", payment.getPaymentStatus().toString());
+                    q.bind("externalPaymentRefId", payment.getExtPaymentRefId());
                     q.bind("effectiveDate", getDate(payment.getEffectiveDate()));
                 }
             };
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 ab1fecc..bf19311 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
@@ -36,11 +36,12 @@ public class PaymentModelDao extends EntityBase {
     private final DateTime effectiveDate;
     private final Integer paymentNumber;
     private final PaymentStatus paymentStatus;
+    private final String extPaymentRefId;
 
 
     public PaymentModelDao(final UUID id, final UUID accountId, final UUID invoiceId, final UUID paymentMethodId,
                            final Integer paymentNumber, final BigDecimal amount, final Currency currency,
-                           final PaymentStatus paymentStatus, final DateTime effectiveDate) {
+                           final PaymentStatus paymentStatus, final DateTime effectiveDate, final String extPaymentRefId) {
         super(id);
         this.accountId = accountId;
         this.invoiceId = invoiceId;
@@ -50,16 +51,16 @@ public class PaymentModelDao extends EntityBase {
         this.currency = currency;
         this.paymentStatus = paymentStatus;
         this.effectiveDate = effectiveDate;
+        this.extPaymentRefId = extPaymentRefId;
     }
 
     public PaymentModelDao(final UUID accountId, final UUID invoiceId,
                            final BigDecimal amount, final Currency currency, final DateTime effectiveDate) {
-        this(UUID.randomUUID(), accountId, invoiceId, null, INVALID_PAYMENT_NUMBER, amount, currency, PaymentStatus.UNKNOWN, effectiveDate);
+        this(UUID.randomUUID(), accountId, invoiceId, null, INVALID_PAYMENT_NUMBER, amount, currency, PaymentStatus.UNKNOWN, effectiveDate, null);
     }
 
     public PaymentModelDao(final PaymentModelDao src, final PaymentStatus newPaymentStatus) {
-        this(src.getId(), src.getAccountId(), src.getInvoiceId(), null, src.getPaymentNumber(), src.getAmount(), src.getCurrency(), newPaymentStatus, src.getEffectiveDate())
-        ;
+        this(src.getId(), src.getAccountId(), src.getInvoiceId(), null, src.getPaymentNumber(), src.getAmount(), src.getCurrency(), newPaymentStatus, src.getEffectiveDate(), null);
     }
 
     public UUID getAccountId() {
@@ -93,4 +94,8 @@ public class PaymentModelDao extends EntityBase {
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
+
+    public String getExtPaymentRefId() {
+        return extPaymentRefId;
+    }
 }
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 f637976..9d88b8f 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
@@ -53,8 +53,8 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
                        @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    void updatePaymentStatus(@Bind("id") final String paymentId, @Bind("paymentStatus") final String paymentStatus,
-                             @CallContextBinder final CallContext context);
+    void updatePaymentStatusAndExtRef(@Bind("id") final String paymentId, @Bind("paymentStatus") final String paymentStatus,
+            @Bind("externalPaymentRefId") final String externalPaymentRefId, @CallContextBinder final CallContext context);
 
     @SqlUpdate
     void updatePaymentAmount(@Bind("id") final String paymentId, @Bind("amount") final BigDecimal amount,
@@ -87,6 +87,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
             stmt.bind("currency", payment.getCurrency().toString());
             stmt.bind("effectiveDate", getDate(payment.getEffectiveDate()));
             stmt.bind("paymentStatus", payment.getPaymentStatus().toString());
+            stmt.bind("externalPaymentRefId", payment.getExtPaymentRefId());
         }
     }
 
@@ -104,8 +105,9 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, UpdatableEn
             final DateTime effectiveDate = getDate(rs, "effective_date");
             final Currency currency = Currency.valueOf(rs.getString("currency"));
             final PaymentStatus paymentStatus = PaymentStatus.valueOf(rs.getString("payment_status"));
+            final String extPaymentRefId = rs.getString("external_payment_ref_id");
 
-            return new PaymentModelDao(id, accountId, invoiceId, paymentMethodId, paymentNumber, amount, currency, paymentStatus, effectiveDate);
+            return new PaymentModelDao(id, accountId, invoiceId, paymentMethodId, paymentNumber, amount, currency, paymentStatus, effectiveDate, extPaymentRefId);
         }
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/RefundHistoryBinder.java b/payment/src/main/java/com/ning/billing/payment/dao/RefundHistoryBinder.java
new file mode 100644
index 0000000..3110b2f
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundHistoryBinder.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.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import com.ning.billing.util.dao.BinderBase;
+import com.ning.billing.util.dao.EntityHistory;
+
+
+@BindingAnnotation(RefundHistoryBinder.RefundHistoryBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface RefundHistoryBinder {
+
+
+    public static class RefundHistoryBinderFactory extends BinderBase implements BinderFactory {
+        @Override
+        public Binder<RefundHistoryBinder, EntityHistory<RefundModelDao>> build(final Annotation annotation) {
+            return new Binder<RefundHistoryBinder, EntityHistory<RefundModelDao>>() {
+                @Override
+                public void bind(final SQLStatement<?> q, final RefundHistoryBinder bind, final EntityHistory<RefundModelDao> history) {
+                    q.bind("recordId", history.getValue());
+                    q.bind("changeType", history.getChangeType().toString());
+                    final RefundModelDao refund = history.getEntity();
+                    q.bind("id", refund.getId().toString());
+                    q.bind("accountId", refund.getAccountId().toString());
+                    q.bind("paymentId", refund.getPaymentId().toString());
+                    q.bind("amount", refund.getAmount());
+                    q.bind("currency", refund.getCurrency().toString());
+                    q.bind("isAdjusted", refund.isAdjsuted());
+                    q.bind("refundStatus", refund.getRefundStatus().toString());
+                }
+            };
+        }
+    }
+}
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
index c29c6ed..e6bea41 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundSqlDao.java
@@ -39,6 +39,7 @@ 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.EntityHistory;
 import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.entity.dao.UpdatableEntitySqlDao;
 
@@ -53,7 +54,7 @@ public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEnti
                        @CallContextBinder final CallContext context);
 
     @SqlUpdate
-    void updateStatus(@Bind("id") final String refundId, @Bind("refund_status") final String status);
+    void updateStatus(@Bind("id") final String refundId, @Bind("refundStatus") final String status);
 
     @SqlQuery
     RefundModelDao getRefund(@Bind("id") final String refundId);
@@ -64,6 +65,11 @@ public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEnti
     @SqlQuery
     List<RefundModelDao> getRefundsForAccount(@Bind("accountId") final String accountId);
 
+    @Override
+    @SqlUpdate
+    public void insertHistoryFromTransaction(@RefundHistoryBinder final EntityHistory<RefundModelDao> payment,
+                                             @CallContextBinder final CallContext context);
+
 
     public static final class RefundModelDaoBinder extends BinderBase implements Binder<Bind, RefundModelDao> {
         @Override
@@ -73,8 +79,8 @@ public interface RefundSqlDao extends Transactional<RefundSqlDao>, UpdatableEnti
             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());
+            stmt.bind("isAdjusted", refund.isAdjsuted());
+            stmt.bind("refundStatus", refund.getRefundStatus().toString());
         }
     }
 
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 325d03d..e4c20f3 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -70,4 +70,10 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     public String getGatewayErrorCode() {
         return null;
     }
+
+
+    @Override
+    public String getExternalReferenceId() {
+        return null;
+    }
 }
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 cf23c59..c43b64b 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
@@ -199,7 +199,6 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     @Override
     public int getNbRefundForPaymentAmount(Account account, UUID paymentId,
             BigDecimal refundAmount) throws PaymentPluginApiException {
-        // TODO Auto-generated method stub
         return 0;
     }
 
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index 2dc34b6..742d9f7 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -9,6 +9,7 @@ paymentFields(prefix) ::= <<
     <prefix>effective_date,
     <prefix>currency,
     <prefix>payment_status,
+    <prefix>external_payment_ref_id,
     <prefix>created_by,
     <prefix>created_date,
     <prefix>updated_by,
@@ -17,7 +18,7 @@ paymentFields(prefix) ::= <<
 
 insertPayment() ::= <<
     INSERT INTO payments (<paymentFields()>)
-    VALUES (:id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus, :userName, :createdDate, :userName, :createdDate);
+    VALUES (:id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus, :externalPaymentRefId, :userName, :createdDate, :userName, :createdDate);
 >>
 
 getPayment() ::= <<
@@ -42,9 +43,9 @@ getPaymentsForAccount() ::= <<
 >>
 
 
-updatePaymentStatus() ::= <<
+updatePaymentStatusAndExtRef() ::= <<
     UPDATE payments
-    SET payment_status = :paymentStatus
+    SET payment_status = :paymentStatus, external_payment_ref_id = :externalPaymentRefId
     WHERE id = :id;
 >>
 
@@ -71,6 +72,7 @@ historyFields(prefix) ::= <<
     <prefix>effective_date,
     <prefix>currency,
     <prefix>payment_status,
+    <prefix>external_payment_ref_id,
     <prefix>created_by,
     <prefix>created_date,
     <prefix>updated_by,
@@ -79,7 +81,7 @@ historyFields(prefix) ::= <<
 
 insertHistoryFromTransaction() ::= <<
     INSERT INTO payment_history (<historyFields()>)
-    VALUES (:recordId, :id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus, :userName, :createdDate, :userName, :updatedDate);
+    VALUES (:recordId, :id, :accountId, :invoiceId, :paymentMethodId, :amount, :effectiveDate, :currency, :paymentStatus, :externalPaymentRefId, :userName, :createdDate, :userName, :updatedDate);
 >>
 
 
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
new file mode 100644
index 0000000..d1e8719
--- /dev/null
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/RefundSqlDao.sql.stg
@@ -0,0 +1,92 @@
+group RefundSqlDao;
+
+refundFields(prefix) ::= <<
+<prefix>id,
+<prefix>account_id,
+<prefix>payment_id,
+<prefix>amount,  
+<prefix>currency,
+<prefix>is_adjusted,
+<prefix>refund_status,    
+<prefix>created_by,
+<prefix>created_date,
+<prefix>updated_by,
+<prefix>updated_date
+>>
+
+insertRefund() ::= <<
+    INSERT INTO refunds (<refundFields()>)
+    VALUES (:id, :accountId, :paymentId, :amount, :currency, :isAdjusted, :refundStatus, :userName, :createdDate, :userName, :createdDate);
+>>
+
+updateStatus() ::= <<
+    UPDATE refunds
+    SET refund_status = :refundStatus
+    WHERE id = :id;
+>>
+
+getRefund() ::= <<
+    SELECT <refundFields()>
+    FROM refunds
+    WHERE id = :id;
+>>
+
+getRefundsForPayment()  ::= <<
+    SELECT <refundFields()>
+    FROM refunds
+    WHERE payment_id = :paymentId;
+>>
+
+getRefundsForAccount()  ::= <<
+    SELECT <refundFields()>
+    FROM refunds
+    WHERE account_id = :accountId;
+>> 
+
+getRecordId() ::= <<
+    SELECT record_id
+    FROM refunds
+    WHERE id = :id;
+>>
+
+historyFields(prefix) ::= <<
+    <prefix>record_id,
+    <prefix>id,
+    <prefix>account_id,
+    <prefix>payment_id,
+    <prefix>amount,  
+    <prefix>currency,
+    <prefix>is_adjusted,
+    <prefix>refund_status,    
+    <prefix>created_by,
+    <prefix>created_date,
+    <prefix>updated_by,
+    <prefix>updated_date
+>>
+
+insertHistoryFromTransaction() ::= <<
+    INSERT INTO refund_history (<historyFields()>)
+    VALUES (:recordId, :id, :accountId, :paymentId, :amount, :currency,  :isAdjusted, :refundStatus, :userName, :createdDate, :userName, :createdDate);
+>>
+
+getHistoryRecordId() ::= <<
+    SELECT MAX(history_record_id)
+    FROM payment_method_history
+    WHERE record_id = :recordId;
+>>
+
+auditFields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments,
+    <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<auditFields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken);
+>>
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index 69db6a2..a652b58 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -1,23 +1,4 @@
 
-DROP TABLE IF EXISTS refunds; 
-CREATE TABLE refunds (
-    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-    id char(36) NOT NULL,
-    account_id char(36) COLLATE utf8_bin NOT NULL,
-    payment_id char(36) COLLATE utf8_bin NOT NULL,    
-    amount decimal(8,2),
-    currency char(3),   
-    is_adjusted tinyint(1),
-    refund_status varchar(50), 
-    created_by varchar(50) NOT NULL,
-    created_date datetime NOT NULL,
-    updated_by varchar(50) NOT NULL,
-    updated_date datetime NOT NULL,
-    PRIMARY KEY (record_id)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-CREATE UNIQUE INDEX refunds_id ON refunds(id);
-CREATE INDEX refunds_pay ON refunds(payment_id);
-CREATE INDEX refunds_accnt ON refunds(account_id);
 
 DROP TABLE IF EXISTS payments; 
 CREATE TABLE payments (
@@ -29,7 +10,8 @@ CREATE TABLE payments (
     amount decimal(8,2),
     currency char(3),    
     effective_date datetime,
-    payment_status varchar(50),  
+    payment_status varchar(50),
+    external_payment_ref_id varchar(64),
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -51,7 +33,8 @@ CREATE TABLE payment_history (
     amount decimal(8,2),
     currency char(3),    
     effective_date datetime,
-    payment_status varchar(50), 
+    payment_status varchar(50),
+    external_payment_ref_id  varchar(64),    
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -132,6 +115,46 @@ CREATE TABLE payment_method_history (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
 CREATE UNIQUE INDEX payment_method_history_record_id ON payment_method_history(record_id);
 
+DROP TABLE IF EXISTS refunds; 
+CREATE TABLE refunds (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    account_id char(36) COLLATE utf8_bin NOT NULL,
+    payment_id char(36) COLLATE utf8_bin NOT NULL,    
+    amount decimal(8,2),
+    currency char(3),   
+    is_adjusted tinyint(1),
+    refund_status varchar(50), 
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    PRIMARY KEY (record_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+CREATE UNIQUE INDEX refunds_id ON refunds(id);
+CREATE INDEX refunds_pay ON refunds(payment_id);
+CREATE INDEX refunds_accnt ON refunds(account_id);
+
+DROP TABLE IF EXISTS refund_history; 
+CREATE TABLE refund_history (
+    history_record_id int(11) unsigned NOT NULL AUTO_INCREMENT, 
+    record_id int(11) unsigned NOT NULL,
+    id char(36) NOT NULL,
+    account_id char(36) COLLATE utf8_bin NOT NULL,
+    payment_id char(36) COLLATE utf8_bin NOT NULL,    
+    amount decimal(8,2),
+    currency char(3),   
+    is_adjusted tinyint(1),
+    refund_status varchar(50), 
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    PRIMARY KEY (history_record_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+CREATE INDEX refund_history_record_id ON refund_history(record_id);
+
+
 
 
 
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
index 371f7b1..d634792 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -39,7 +39,7 @@ public class TestEventJson {
 
     @Test(groups = {"fast"})
     public void testPaymentInfoEvent() throws Exception {
-        final PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new BigDecimal(12.9), new Integer(13), PaymentStatus.SUCCESS, UUID.randomUUID(), new DateTime());
+        final PaymentInfoEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new BigDecimal(12.9), new Integer(13), PaymentStatus.SUCCESS, "ext-ref-12345", UUID.randomUUID(), new DateTime());
         final String json = mapper.writeValueAsString(e);
 
         final Class<?> clazz = Class.forName(DefaultPaymentInfoEvent.class.getName());
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index ce1a17c..80b75f4 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -53,7 +53,7 @@ public class MockPaymentDao implements PaymentDao {
 
     @Override
     public void updateStatusForPaymentWithAttempt(final UUID paymentId,
-                                                  final PaymentStatus paymentStatus, final String paymentError, final UUID attemptId,
+                                                  final PaymentStatus paymentStatus, final String paymentError,  final String extpaymentRefId, final UUID attemptId,
                                                   final CallContext context) {
         synchronized (this) {
             final PaymentModelDao entry = payments.remove(paymentId);
@@ -68,18 +68,6 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updateStatusForPayment(final UUID paymentId,
-                                       final PaymentStatus paymentStatus, final CallContext context) {
-        synchronized (this) {
-            final PaymentModelDao entry = payments.remove(paymentId);
-            if (entry != null) {
-                payments.put(paymentId, new PaymentModelDao(entry, paymentStatus));
-            }
-        }
-    }
-
-
-    @Override
     public PaymentAttemptModelDao getPaymentAttempt(final UUID attemptId) {
         return attempts.get(attemptId);
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 10c2ac5..cbe952a 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -24,6 +24,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
 import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.BeforeTest;
@@ -34,6 +35,7 @@ import com.ning.billing.dbi.DBIProvider;
 import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TestCallContext;
 import com.ning.billing.util.clock.Clock;
@@ -42,6 +44,8 @@ import com.ning.billing.util.io.IOUtils;
 
 import static junit.framework.Assert.assertNull;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
 
 public class TestPaymentDao {
     private static final CallContext context = new TestCallContext("PaymentTests");
@@ -89,6 +93,57 @@ public class TestPaymentDao {
     }
 
 
+
+
+    @Test(groups = {"slow"})
+    public void testRefund() {
+
+        final UUID accountId = UUID.randomUUID();
+        final UUID paymentId1 = UUID.randomUUID();
+        final BigDecimal amount1 = new BigDecimal(13);
+        final Currency currency = Currency.USD;
+
+        RefundModelDao refund1 =  new RefundModelDao(accountId, paymentId1, amount1, currency, true);
+
+        paymentDao.insertRefund(refund1, context);
+        RefundModelDao refundCheck = paymentDao.getRefund(refund1.getId());
+        assertNotNull(refundCheck);
+        assertEquals(refundCheck.getAccountId(), accountId);
+        assertEquals(refundCheck.getPaymentId(), paymentId1);
+        assertEquals(refundCheck.getAmount().compareTo(amount1), 0);
+        assertEquals(refundCheck.getCurrency(), currency);
+        assertEquals(refundCheck.isAdjsuted(), true);
+        assertEquals(refundCheck.getRefundStatus(), RefundStatus.CREATED);
+
+        final BigDecimal amount2 = new BigDecimal(7.00);
+        final UUID paymentId2 = UUID.randomUUID();
+
+        RefundModelDao refund2 =  new RefundModelDao(accountId, paymentId2, amount2, currency, true);
+        paymentDao.insertRefund(refund2, context);
+        paymentDao.updateRefundStatus(refund2.getId(), RefundStatus.COMPLETED, context);
+
+        List<RefundModelDao> refundChecks = paymentDao.getRefundsForPayment(paymentId1);
+        assertEquals(refundChecks.size(), 1);
+
+        refundChecks = paymentDao.getRefundsForPayment(paymentId2);
+        assertEquals(refundChecks.size(), 1);
+
+        refundChecks = paymentDao.getRefundsForAccount(accountId);
+        assertEquals(refundChecks.size(), 2);
+        for (RefundModelDao cur : refundChecks) {
+            if (cur.getPaymentId().equals(paymentId1)) {
+                assertEquals(cur.getAmount().compareTo(amount1), 0);
+                assertEquals(cur.getRefundStatus(), RefundStatus.CREATED);
+            } else if (cur.getPaymentId().equals(paymentId2)) {
+                assertEquals(cur.getAmount().compareTo(amount2), 0);
+                assertEquals(cur.getRefundStatus(), RefundStatus.COMPLETED);
+            } else {
+                fail("Unexpected refund");
+            }
+        }
+    }
+
+
     @Test(groups = {"slow"})
     public void testUpdateStatus() {
 
@@ -105,7 +160,7 @@ public class TestPaymentDao {
         final PaymentStatus paymentStatus = PaymentStatus.SUCCESS;
         final String paymentError = "No error";
 
-        paymentDao.updateStatusForPaymentWithAttempt(payment.getId(), paymentStatus, paymentError, attempt.getId(), context);
+        paymentDao.updateStatusForPaymentWithAttempt(payment.getId(), paymentStatus, paymentError, null, attempt.getId(), context);
 
         final List<PaymentModelDao> payments = paymentDao.getPaymentsForInvoice(invoiceId);
         assertEquals(payments.size(), 1);
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 74ef8c3..96c8668 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -33,6 +33,7 @@ import com.ning.billing.jaxrs.resources.CatalogResource;
 import com.ning.billing.jaxrs.resources.InvoiceResource;
 import com.ning.billing.jaxrs.resources.PaymentMethodResource;
 import com.ning.billing.jaxrs.resources.PaymentResource;
+import com.ning.billing.jaxrs.resources.RefundResource;
 import com.ning.billing.jaxrs.resources.SubscriptionResource;
 import com.ning.billing.jaxrs.resources.TagResource;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
@@ -58,7 +59,7 @@ public class KillbillServerModule extends AbstractModule {
         installKillbillModules();
     }
 
-    
+
     protected void configureDao() {
         // Load mysql driver if needed
         try {
@@ -79,6 +80,7 @@ public class KillbillServerModule extends AbstractModule {
         bind(CatalogResource.class).asEagerSingleton();
         bind(PaymentMethodResource.class).asEagerSingleton();
         bind(PaymentResource.class).asEagerSingleton();
+        bind(RefundResource.class).asEagerSingleton();
         bind(KillbillEventHandler.class).asEagerSingleton();
     }
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index 60aa56a..54ecd11 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -44,6 +44,7 @@ import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.PaymentJsonSimple;
 import com.ning.billing.jaxrs.json.PaymentMethodJson;
+import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
@@ -233,7 +234,7 @@ public class TestAccount extends TestJaxrsBase {
     }
 
     @Test(groups = "slow", enabled = true)
-    public void testAccountPayments() throws Exception {
+    public void testAccountPaymentsWithRefund() throws Exception {
 
         //clock.setTime(new DateTime(2012, 4, 25, 0, 3, 42, 0));
 
@@ -250,13 +251,21 @@ public class TestAccount extends TestJaxrsBase {
         crappyWaitForLackOfProperSynchonization();
 
 
-        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
+        String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
 
-        final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        final String baseJson = response.getResponseBody();
-        final List<PaymentJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+        String baseJson = response.getResponseBody();
+        List<PaymentJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<PaymentJsonSimple>>() {});
         Assert.assertEquals(objFromJson.size(), 1);
+
+        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.REFUNDS;
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        List<RefundJson> objRefundFromJson = mapper.readValue(baseJson, new TypeReference<List<RefundJson>>() {});
+        Assert.assertEquals(objRefundFromJson.size(), 0);
+
     }
 
     @Test(groups = "slow", enabled = true)
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
new file mode 100644
index 0000000..be1ec0d
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
+import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+
+import com.ning.billing.jaxrs.json.RefundJson;
+import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.http.client.Response;
+
+public class TestPayment extends TestJaxrsBase {
+
+    private static final Logger log = LoggerFactory.getLogger(TestPayment.class);
+
+    @Test(groups = "slow", enabled = true)
+    public void testPaymentWithRefund() throws Exception {
+        final AccountJson accountJson = createAccountWithDefaultPaymentMethod("eraahahildo", "sheqrgfhwe", "eraahahildo@yahoo.com");
+        assertNotNull(accountJson);
+
+        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();
+
+
+        String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
+
+        Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        String baseJson = response.getResponseBody();
+        List<PaymentJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+        Assert.assertEquals(objFromJson.size(), 1);
+
+        final String paymentId = objFromJson.get(0).getPaymentId();
+        final BigDecimal paymentAmount = objFromJson.get(0).getAmount();
+
+        uri = JaxrsResource.PAYMENTS_PATH + "/" + paymentId + "/" + JaxrsResource.REFUNDS;
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        List<RefundJson> objRefundFromJson = mapper.readValue(baseJson, new TypeReference<List<RefundJson>>() {});
+        Assert.assertEquals(objRefundFromJson.size(), 0);
+
+        // Issue the refund
+
+        RefundJson refundJson = new RefundJson(null, paymentId, paymentAmount.negate(), false);
+        baseJson = mapper.writeValueAsString(refundJson);
+        response = doPost(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+        final String locationCC = response.getHeader("Location");
+        Assert.assertNotNull(locationCC);
+
+        // Retrieves by Id based on Location returned
+        response = doGetWithUrl(locationCC, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        final RefundJson refundJsonCheck = mapper.readValue(baseJson, RefundJson.class);
+        Assert.assertTrue(refundJsonCheck.equalsNoId(refundJson));
+
+        uri = JaxrsResource.PAYMENTS_PATH + "/" + paymentId + "/" + JaxrsResource.REFUNDS;
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
+        objRefundFromJson = mapper.readValue(baseJson, new TypeReference<List<RefundJson>>() {});
+        Assert.assertEquals(objRefundFromJson.size(), 1);
+
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 4008f2e..a5038d5 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -31,6 +31,7 @@ public enum TableName {
     PAYMENT_METHODS("payment_methods"),
     SUBSCRIPTIONS("subscriptions"),
     SUBSCRIPTION_EVENTS("subscription_events"),
+    REFUNDS("refunds"),
     TAG_HISTORY("tag_history");
 
     private final String tableName;