killbill-memoizeit

Modify payment subsystem to record processed amount/currency

10/28/2013 6:21:33 PM

Changes

currency/pom.xml 2(+1 -1)

Details

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index 353f7b2..f328c2a 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.7.1-SNAPSHOT</version>
+        <version>0.7.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index b0fd9ff..718ca6b 100644
--- a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -57,6 +57,11 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
             }
 
             @Override
+            public Currency getCurrency() {
+                return currency;
+            }
+
+            @Override
             public DateTime getCreatedDate() {
                 return new DateTime();
             }
diff --git a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index c9059b0..f5c0871 100644
--- a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -58,6 +58,11 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
             }
 
             @Override
+            public Currency getCurrency() {
+                return currency;
+            }
+
+            @Override
             public DateTime getCreatedDate() {
                 return new DateTime();
             }
@@ -105,6 +110,11 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
             }
 
             @Override
+            public Currency getCurrency() {
+                return null;
+            }
+
+            @Override
             public DateTime getCreatedDate() {
                 return new DateTime();
             }
@@ -152,6 +162,11 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
             }
 
             @Override
+            public Currency getCurrency() {
+                return null;
+            }
+
+            @Override
             public DateTime getCreatedDate() {
                 return null;
             }
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 bfc5550..37a8c8a 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
@@ -438,7 +438,8 @@ public class PaymentProcessor extends ProcessorBase {
         final PaymentStatus paymentStatus = PaymentStatus.AUTO_PAY_OFF;
 
         final PaymentModelDao paymentInfo = new PaymentModelDao(account.getId(), invoice.getId(), paymentMethodId, requestedAmount, invoice.getCurrency(), clock.getUTCNow(), paymentStatus);
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), paymentInfo.getId(), paymentMethodId, paymentStatus, clock.getUTCNow(), requestedAmount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), paymentInfo.getId(), paymentMethodId, paymentStatus, clock.getUTCNow(),
+                                                                          requestedAmount, invoice.getCurrency());
 
         paymentDao.insertPaymentWithFirstAttempt(paymentInfo, attempt, context);
         return fromPaymentModelDao(paymentInfo, null, context);
@@ -451,7 +452,8 @@ public class PaymentProcessor extends ProcessorBase {
         final PaymentStatus paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED ;
 
         final PaymentModelDao paymentInfo = new PaymentModelDao(account.getId(), invoice.getId(), MISSING_PAYMENT_METHOD_ID, requestedAmount, invoice.getCurrency(), clock.getUTCNow(), paymentStatus);
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), paymentInfo.getId(), MISSING_PAYMENT_METHOD_ID, paymentStatus, clock.getUTCNow(), requestedAmount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), paymentInfo.getId(), MISSING_PAYMENT_METHOD_ID, paymentStatus, clock.getUTCNow(),
+                                                                          requestedAmount, invoice.getCurrency());
 
         paymentDao.insertPaymentWithFirstAttempt(paymentInfo, attempt, context);
         return fromPaymentModelDao(paymentInfo, null, context);
@@ -461,7 +463,8 @@ public class PaymentProcessor extends ProcessorBase {
     private Payment processNewPaymentWithAccountLocked(final UUID paymentMethodId, final PaymentPluginApi plugin, final Account account, final Invoice invoice,
                                                        final BigDecimal requestedAmount, final boolean isInstantPayment, final InternalCallContext context) throws PaymentApiException {
         final PaymentModelDao payment = new PaymentModelDao(account.getId(), invoice.getId(), paymentMethodId, requestedAmount.setScale(2, RoundingMode.HALF_UP), invoice.getCurrency(), clock.getUTCNow());
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), paymentMethodId, clock.getUTCNow(), requestedAmount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), paymentMethodId, clock.getUTCNow(),
+                                                                          requestedAmount, invoice.getCurrency());
 
         final PaymentModelDao savedPayment = paymentDao.insertPaymentWithFirstAttempt(payment, attempt, context);
         return processPaymentWithAccountLocked(plugin, account, invoice, savedPayment, attempt, isInstantPayment, context);
@@ -484,9 +487,10 @@ public class PaymentProcessor extends ProcessorBase {
             default:
                 throw new IllegalStateException("Unexpected payment status for retry " + payment.getPaymentStatus());
         }
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), account.getPaymentMethodId(), clock.getUTCNow(), requestedAmount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), account.getPaymentMethodId(), clock.getUTCNow(),
+                                                                          requestedAmount, invoice.getCurrency());
         paymentDao.updatePaymentWithNewAttempt(payment.getId(), attempt, context);
-        paymentDao.updatePaymentAndAttemptOnCompletion(payment.getId(), paymentStatus, attempt.getId(), null, terminalStateReason, context);
+        paymentDao.updatePaymentAndAttemptOnCompletion(payment.getId(), paymentStatus, requestedAmount, account.getCurrency(), attempt.getId(), null, terminalStateReason, context);
 
         final List<PaymentAttemptModelDao> allAttempts = paymentDao.getAttemptsForPayment(payment.getId(), context);
         return new DefaultPayment(payment, null, allAttempts, Collections.<RefundModelDao>emptyList());
@@ -495,19 +499,22 @@ public class PaymentProcessor extends ProcessorBase {
 
     private Payment processRetryPaymentWithAccountLocked(final PaymentPluginApi plugin, final Account account, final Invoice invoice, final PaymentModelDao payment,
                                                          final BigDecimal requestedAmount, final InternalCallContext context) throws PaymentApiException {
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), account.getPaymentMethodId(), clock.getUTCNow(), requestedAmount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), account.getPaymentMethodId(), clock.getUTCNow(),
+                                                                          requestedAmount, invoice.getCurrency());
         paymentDao.updatePaymentWithNewAttempt(payment.getId(), attempt, context);
         return processPaymentWithAccountLocked(plugin, account, invoice, payment, attempt, false, context);
     }
 
 
     private Payment processPaymentWithAccountLocked(final PaymentPluginApi plugin, final Account account, final Invoice invoice,
-                                                    final PaymentModelDao paymentInput, final PaymentAttemptModelDao attemptInput, final boolean isInstantPayment, final InternalCallContext context) throws PaymentApiException {
+                                                    final PaymentModelDao paymentInput, final PaymentAttemptModelDao attemptInput, final boolean isInstantPayment, final InternalCallContext context)
+            throws PaymentApiException {
 
 
         List<PaymentAttemptModelDao> allAttempts = null;
         if (paymentConfig.isPaymentOff()) {
-            paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), PaymentStatus.PAYMENT_SYSTEM_OFF, attemptInput.getId(), null, null, context);
+            paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), PaymentStatus.PAYMENT_SYSTEM_OFF,
+                                                           attemptInput.getRequestedAmount(), account.getCurrency(), attemptInput.getId(), null, null, context);
             allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
             return new DefaultPayment(paymentInput, null, allAttempts, Collections.<RefundModelDao>emptyList());
         }
@@ -518,7 +525,8 @@ public class PaymentProcessor extends ProcessorBase {
         final PaymentInfoPlugin paymentPluginInfo;
         try {
             try {
-                paymentPluginInfo = plugin.processPayment(account.getId(), paymentInput.getId(), attemptInput.getPaymentMethodId(), attemptInput.getRequestedAmount(), account.getCurrency(), context.toCallContext());
+                paymentPluginInfo = plugin.processPayment(account.getId(), paymentInput.getId(), attemptInput.getPaymentMethodId(),
+                                                          attemptInput.getRequestedAmount(), account.getCurrency(), context.toCallContext());
             } catch (RuntimeException e) {
                 // Handle case of plugin RuntimeException to be handled the same as a Plugin failure (PaymentPluginApiException)
                 final String formatError = String.format("Plugin threw RuntimeException for payment %s", paymentInput.getId());
@@ -528,13 +536,16 @@ public class PaymentProcessor extends ProcessorBase {
                 case PROCESSED:
                     // Update Payment/PaymentAttempt status
                     paymentStatus = PaymentStatus.SUCCESS;
-
-                    paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, attemptInput.getId(), paymentPluginInfo.getGatewayErrorCode(), null, context);
+                    // In case of success we are using the amount/currency as returned by the plugin-- if plugin decides to mess with amount and currency, we track it there
+                    paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, paymentPluginInfo.getAmount(), paymentPluginInfo.getCurrency(),
+                                                                   attemptInput.getId(), paymentPluginInfo.getGatewayErrorCode(), null, context);
 
                     // Fetch latest objects
                     allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
 
                     payment = paymentDao.getPayment(paymentInput.getId(), context);
+                    // NOTE that we are not using the amount/currency as returned by the plugin but the requested amount/currency; if plugin decide to change the currency
+                    // at the time of payment, we want to stay consistent with the currency on the account
                     invoiceApi.notifyOfPayment(invoice.getId(),
                             payment.getAmount(),
                             payment.getCurrency(),
@@ -561,7 +572,8 @@ public class PaymentProcessor extends ProcessorBase {
                         paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
                     }
 
-                    paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, attemptInput.getId(), paymentPluginInfo.getGatewayErrorCode(), paymentPluginInfo.getGatewayError(), context);
+                    paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, attemptInput.getRequestedAmount(), account.getCurrency(),
+                                                                   attemptInput.getId(), paymentPluginInfo.getGatewayErrorCode(), paymentPluginInfo.getGatewayError(), context);
 
                     log.info(String.format("Could not process payment for account %s, invoice %s, error = %s",
                             account.getId(), invoice.getId(), paymentPluginInfo.getGatewayError()));
@@ -584,7 +596,8 @@ public class PaymentProcessor extends ProcessorBase {
             paymentStatus = isInstantPayment ? PaymentStatus.PAYMENT_FAILURE_ABORTED : scheduleRetryOnPluginFailure(paymentInput.getId(), context);
             // STEPH message might need truncation to fit??
 
-            paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, attemptInput.getId(), null, e.getMessage(), context);
+            paymentDao.updatePaymentAndAttemptOnCompletion(paymentInput.getId(), paymentStatus, attemptInput.getRequestedAmount(), account.getCurrency(),
+                                                           attemptInput.getId(), null, e.getMessage(), context);
 
             event = new DefaultPaymentPluginErrorEvent(account.getId(), invoice.getId(), paymentInput.getId(), e.getMessage(),
                     context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()
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 4c34460..b4b631b 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
@@ -120,24 +120,24 @@ public class RefundProcessor extends ProcessorBase {
                         throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentId);
                     }
 
-                    final RefundModelDao refundInfo = new RefundModelDao(account.getId(), paymentId, refundAmount, account.getCurrency(), isAdjusted);
+                    final RefundModelDao refundInfo = new RefundModelDao(account.getId(), paymentId, refundAmount, account.getCurrency(), refundAmount, account.getCurrency(), isAdjusted);
                     paymentDao.insertRefund(refundInfo, context);
 
                     final PaymentPluginApi plugin = getPaymentProviderPlugin(payment.getPaymentMethodId(), context);
                     final RefundInfoPlugin refundInfoPlugin = plugin.processRefund(account.getId(), paymentId, refundAmount, account.getCurrency(), context.toCallContext());
 
                     if (refundInfoPlugin.getStatus() == RefundPluginStatus.PROCESSED) {
-                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, context);
+                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, refundInfoPlugin.getAmount(), refundInfoPlugin.getCurrency(), context);
 
                         invoiceApi.createRefund(paymentId, refundAmount, isAdjusted, invoiceItemIdsWithAmounts, refundInfo.getId(), context);
 
-                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.COMPLETED, context);
+                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.COMPLETED, refundInfoPlugin.getAmount(), refundInfoPlugin.getCurrency(), context);
 
                         return new DefaultRefund(refundInfo.getId(), refundInfo.getCreatedDate(), refundInfo.getUpdatedDate(),
                                                  paymentId, refundInfo.getAmount(), account.getCurrency(),
                                                  isAdjusted, refundInfo.getCreatedDate());
                     } else {
-                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_ERRORED, context);
+                        paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_ERRORED, refundAmount, account.getCurrency(), context);
                         throw new PaymentPluginApiException("Refund error for RefundInfo: " + refundInfo.toString(),
                                                             String.format("Gateway error: %s, Gateway error code: %s, Reference id: %s", refundInfoPlugin.getGatewayError(), refundInfoPlugin.getGatewayErrorCode(), refundInfoPlugin.getReferenceId()));
                     }
@@ -278,7 +278,7 @@ public class RefundProcessor extends ProcessorBase {
 
                             // TODO - we currently don't save the items to be adjusted. If we crash, they won't be adjusted...
                             invoiceApi.createRefund(cur.getPaymentId(), cur.getAmount(), cur.isAdjusted(), ImmutableMap.<UUID, BigDecimal>of(), cur.getId(), context);
-                            paymentDao.updateRefundStatus(cur.getId(), RefundStatus.COMPLETED, context);
+                            paymentDao.updateRefundStatus(cur.getId(), RefundStatus.COMPLETED, cur.getProcessedAmount(), cur.getProcessedCurrency(), context);
                         }
                     } catch (InvoiceApiException e) {
                         throw new PaymentApiException(e);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index 7c0b657..ca5509a 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.payment.dao;
 
+import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
@@ -27,6 +28,7 @@ import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entity.EntityPersistenceException;
 import com.ning.billing.payment.api.PaymentStatus;
@@ -100,6 +102,8 @@ public class DefaultPaymentDao implements PaymentDao {
     @Override
     public void updatePaymentAndAttemptOnCompletion(final UUID paymentId,
                                                     final PaymentStatus paymentStatus,
+                                                    final BigDecimal processedAmount,
+                                                    final Currency processedCurrency,
                                                     final UUID attemptId,
                                                     final String gatewayErrorCode,
                                                     final String gatewayErrorMsg,
@@ -108,7 +112,7 @@ public class DefaultPaymentDao implements PaymentDao {
 
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatus(paymentId.toString(), paymentStatus.toString(), context);
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatus(paymentId.toString(), processedAmount, processedCurrency, paymentStatus.toString(), context);
                 entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).updatePaymentAttemptStatus(attemptId.toString(), paymentStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
                 return null;
             }
@@ -147,11 +151,11 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updateRefundStatus(final UUID refundId, final RefundStatus refundStatus, final InternalCallContext context) {
+    public void updateRefundStatus(final UUID refundId, final RefundStatus refundStatus, final BigDecimal processedAmount, final Currency processedCurrency, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(RefundSqlDao.class).updateStatus(refundId.toString(), refundStatus.toString(), context);
+                entitySqlDaoWrapperFactory.become(RefundSqlDao.class).updateStatus(refundId.toString(), refundStatus.toString(), processedAmount, processedCurrency, context);
                 return null;
             }
         });
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
index cbed1f1..d1c8504 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentAttemptModelDao.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentAttempt;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.util.dao.TableName;
@@ -40,6 +41,7 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
     private String gatewayErrorCode;
     private String gatewayErrorMsg;
     private BigDecimal requestedAmount;
+    private Currency requestedCurrency;
 
     public PaymentAttemptModelDao() { /* For the DAO mapper */ }
 
@@ -47,7 +49,8 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
                                   final UUID accountId, final UUID invoiceId,
                                   final UUID paymentId, final UUID paymentMethodId,
                                   final PaymentStatus processingStatus, final DateTime effectiveDate,
-                                  final BigDecimal requestedAmount, final String gatewayErrorCode, final String gatewayErrorMsg) {
+                                  final BigDecimal requestedAmount, Currency requestedCurrency,
+                                  final String gatewayErrorCode, final String gatewayErrorMsg) {
         super(id, createdDate, updatedDate);
         this.accountId = accountId;
         this.invoiceId = invoiceId;
@@ -56,21 +59,24 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         this.processingStatus = processingStatus;
         this.effectiveDate = effectiveDate;
         this.requestedAmount = requestedAmount;
+        this.requestedCurrency = requestedCurrency;
         this.gatewayErrorCode = gatewayErrorCode;
         this.gatewayErrorMsg = gatewayErrorMsg;
     }
 
-    public PaymentAttemptModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentId, final UUID paymentMethodId, final PaymentStatus paymentStatus, final DateTime effectiveDate, final BigDecimal requestedAmount) {
-        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentId, paymentMethodId, paymentStatus, effectiveDate, requestedAmount, null, null);
+    public PaymentAttemptModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentId, final UUID paymentMethodId, final PaymentStatus paymentStatus, final DateTime effectiveDate,
+                                  final BigDecimal requestedAmount, final Currency requestedCurrency) {
+        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentId, paymentMethodId, paymentStatus, effectiveDate, requestedAmount, requestedCurrency, null, null);
     }
 
-    public PaymentAttemptModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentId, final UUID paymentMethodId, final DateTime effectiveDate, final BigDecimal requestedAmount) {
-        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentId, paymentMethodId, PaymentStatus.UNKNOWN, effectiveDate, requestedAmount, null, null);
+    public PaymentAttemptModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentId, final UUID paymentMethodId, final DateTime effectiveDate,
+                                  final BigDecimal requestedAmount, final Currency requestedCurrency) {
+        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentId, paymentMethodId, PaymentStatus.UNKNOWN, effectiveDate, requestedAmount, requestedCurrency, null, null);
     }
 
     public PaymentAttemptModelDao(final PaymentAttemptModelDao src, final PaymentStatus newProcessingStatus, final String gatewayErrorCode, final String gatewayErrorMsg) {
         this(src.getId(), src.getCreatedDate(), src.getUpdatedDate(), src.getAccountId(), src.getInvoiceId(), src.getPaymentId(), src.getPaymentMethodId(),
-             newProcessingStatus, src.getEffectiveDate(), src.getRequestedAmount(), gatewayErrorCode, gatewayErrorMsg);
+             newProcessingStatus, src.getEffectiveDate(), src.getRequestedAmount(), src.getRequestedCurrency(), gatewayErrorCode, gatewayErrorMsg);
     }
 
     public UUID getAccountId() {
@@ -109,6 +115,10 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         return requestedAmount;
     }
 
+    public Currency getRequestedCurrency() {
+        return requestedCurrency;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -121,6 +131,7 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         sb.append(", gatewayErrorCode='").append(gatewayErrorCode).append('\'');
         sb.append(", gatewayErrorMsg='").append(gatewayErrorMsg).append('\'');
         sb.append(", requestedAmount=").append(requestedAmount);
+        sb.append(", requestedCurrency=").append(requestedCurrency);
         sb.append('}');
         return sb.toString();
     }
@@ -166,7 +177,9 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         if (requestedAmount != null ? !requestedAmount.equals(that.requestedAmount) : that.requestedAmount != null) {
             return false;
         }
-
+        if (requestedCurrency != that.requestedCurrency) {
+            return false;
+        }
         return true;
     }
 
@@ -182,6 +195,7 @@ public class PaymentAttemptModelDao extends EntityBase implements EntityModelDao
         result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
         result = 31 * result + (gatewayErrorMsg != null ? gatewayErrorMsg.hashCode() : 0);
         result = 31 * result + (requestedAmount != null ? requestedAmount.hashCode() : 0);
+        result = 31 * result + (requestedCurrency != null ? requestedCurrency.hashCode() : 0);
         return result;
     }
 
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 505df70..575e6ca 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
@@ -16,11 +16,13 @@
 
 package com.ning.billing.payment.dao;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.callcontext.InternalCallContext;
@@ -34,6 +36,7 @@ public interface PaymentDao {
     public PaymentAttemptModelDao updatePaymentWithNewAttempt(UUID paymentId, PaymentAttemptModelDao attempt, InternalCallContext context);
 
     public void updatePaymentAndAttemptOnCompletion(UUID paymentId, PaymentStatus paymentStatus,
+                                                    BigDecimal processedAmount, Currency processedCurrency,
                                                     UUID attemptId, String gatewayErrorMsg, String gatewayErrorCode, InternalCallContext context);
 
     public PaymentAttemptModelDao getPaymentAttempt(UUID attemptId, InternalTenantContext context);
@@ -50,7 +53,7 @@ public interface PaymentDao {
 
     public RefundModelDao insertRefund(RefundModelDao refundInfo, InternalCallContext context);
 
-    public void updateRefundStatus(UUID refundId, RefundStatus status, InternalCallContext context);
+    public void updateRefundStatus(UUID refundId, RefundStatus status, BigDecimal processedAmount, Currency processedCurrency, InternalCallContext context);
 
     public RefundModelDao getRefund(UUID refundId, InternalTenantContext context);
 
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 585035c..f7fa1e9 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
@@ -39,6 +39,8 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
     private UUID paymentMethodId;
     private BigDecimal amount;
     private Currency currency;
+    private BigDecimal processedAmount;
+    private Currency processedCurrency;
     private DateTime effectiveDate;
     private Integer paymentNumber;
     private PaymentStatus paymentStatus;
@@ -49,7 +51,7 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
 
     public PaymentModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
                            final UUID invoiceId, final UUID paymentMethodId,
-                           final Integer paymentNumber, final BigDecimal amount, final Currency currency,
+                           final Integer paymentNumber, final BigDecimal amount, final Currency currency, final BigDecimal processedAmount, final Currency processedCurrency,
                            final PaymentStatus paymentStatus, final DateTime effectiveDate, final String extFirstPaymentRefId, final String extSecondPaymentRefId) {
         super(id, createdDate, updatedDate);
         this.accountId = accountId;
@@ -58,6 +60,8 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         this.paymentNumber = paymentNumber;
         this.amount = amount;
         this.currency = currency;
+        this.processedAmount = processedAmount;
+        this.processedCurrency = processedCurrency;
         this.paymentStatus = paymentStatus;
         this.effectiveDate = effectiveDate;
         this.extFirstPaymentRefId = extFirstPaymentRefId;
@@ -66,17 +70,17 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
 
     public PaymentModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentMethodId,
                            final BigDecimal amount, final Currency currency, final DateTime effectiveDate, final PaymentStatus paymentStatus) {
-        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentMethodId, INVALID_PAYMENT_NUMBER, amount, currency, paymentStatus, effectiveDate, null, null);
+        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentMethodId, INVALID_PAYMENT_NUMBER, amount, currency, amount, currency, paymentStatus, effectiveDate, null, null);
     }
 
     public PaymentModelDao(final UUID accountId, final UUID invoiceId, final UUID paymentMethodId,
                            final BigDecimal amount, final Currency currency, final DateTime effectiveDate) {
-        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentMethodId, INVALID_PAYMENT_NUMBER, amount, currency, PaymentStatus.UNKNOWN, effectiveDate, null, null);
+        this(UUID.randomUUID(), null, null, accountId, invoiceId, paymentMethodId, INVALID_PAYMENT_NUMBER, amount, currency, amount, currency, PaymentStatus.UNKNOWN, effectiveDate, null, null);
     }
 
     public PaymentModelDao(final PaymentModelDao src, final PaymentStatus newPaymentStatus) {
         this(src.getId(), src.getCreatedDate(), src.getUpdatedDate(), src.getAccountId(), src.getInvoiceId(), src.getPaymentMethodId(),
-             src.getPaymentNumber(), src.getAmount(), src.getCurrency(), newPaymentStatus, src.getEffectiveDate(), null, null);
+             src.getPaymentNumber(), src.getAmount(), src.getCurrency(), src.getProcessedAmount(), src.getProcessedCurrency(), newPaymentStatus, src.getEffectiveDate(), null, null);
     }
 
     public UUID getAccountId() {
@@ -99,14 +103,22 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         return amount;
     }
 
-    public PaymentStatus getPaymentStatus() {
-        return paymentStatus;
-    }
-
     public Currency getCurrency() {
         return currency;
     }
 
+    public BigDecimal getProcessedAmount() {
+        return processedAmount;
+    }
+
+    public Currency getProcessedCurrency() {
+        return processedCurrency;
+    }
+
+    public PaymentStatus getPaymentStatus() {
+        return paymentStatus;
+    }
+
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
@@ -160,6 +172,12 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         if (currency != that.currency) {
             return false;
         }
+        if (processedAmount != null ? !processedAmount.equals(that.processedAmount) : that.processedAmount != null) {
+            return false;
+        }
+        if (processedCurrency != that.processedCurrency) {
+            return false;
+        }
         if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
             return false;
         }
@@ -181,7 +199,6 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         if (paymentStatus != that.paymentStatus) {
             return false;
         }
-
         return true;
     }
 
@@ -193,6 +210,8 @@ public class PaymentModelDao extends EntityBase implements EntityModelDao<Paymen
         result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (processedAmount != null ? processedAmount.hashCode() : 0);
+        result = 31 * result + (processedCurrency != null ? processedCurrency.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (paymentNumber != null ? paymentNumber.hashCode() : 0);
         result = 31 * result + (paymentStatus != null ? paymentStatus.hashCode() : 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 19a367c..8a1749f 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
@@ -25,6 +25,7 @@ import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.callcontext.InternalCallContext;
@@ -39,6 +40,8 @@ public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
     void updatePaymentStatus(@Bind("id") final String paymentId,
+                             @Bind("processedAmount") final BigDecimal processedAmount,
+                             @Bind("processedCurrency") final Currency processedCurrency,
                              @Bind("paymentStatus") final String paymentStatus,
                              @BindBean final InternalCallContext context);
 
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
index 8f98085..eb4d052 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/RefundModelDao.java
@@ -35,24 +35,29 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
     private UUID paymentId;
     private BigDecimal amount;
     private Currency currency;
+    private BigDecimal processedAmount;
+    private Currency processedCurrency;
     private boolean isAdjusted;
     private RefundStatus refundStatus;
 
     public RefundModelDao() { /* For the DAO mapper */ }
 
-    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, null, null);
+    public RefundModelDao(final UUID accountId, final UUID paymentId, final BigDecimal amount, final Currency currency,
+                          final BigDecimal processedAmount, final Currency processedCurrency, final boolean isAdjusted) {
+        this(UUID.randomUUID(), accountId, paymentId, amount, currency, processedAmount, processedCurrency, isAdjusted, RefundStatus.CREATED, null, null);
     }
 
     public RefundModelDao(final UUID id, final UUID accountId, final UUID paymentId, final BigDecimal amount,
-                          final Currency currency, final boolean isAdjusted, final RefundStatus refundStatus,
+                          final Currency currency, final BigDecimal processedAmount, final Currency processedCurrency,
+                          final boolean isAdjusted, final RefundStatus refundStatus,
                           @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate) {
         super(id, createdDate, updatedDate);
         this.accountId = accountId;
         this.paymentId = paymentId;
         this.amount = amount;
         this.currency = currency;
+        this.processedAmount = processedAmount;
+        this.processedCurrency = processedCurrency;
         this.refundStatus = refundStatus;
         this.isAdjusted = isAdjusted;
     }
@@ -73,6 +78,14 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         return currency;
     }
 
+    public BigDecimal getProcessedAmount() {
+        return processedAmount;
+    }
+
+    public Currency getProcessedCurrency() {
+        return processedCurrency;
+    }
+
     public RefundStatus getRefundStatus() {
         return refundStatus;
     }
@@ -102,6 +115,8 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         sb.append(", paymentId=").append(paymentId);
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
+        sb.append(", processedAmount=").append(processedAmount);
+        sb.append(", processedCurrency=").append(processedCurrency);
         sb.append(", isAdjusted=").append(isAdjusted);
         sb.append(", refundStatus=").append(refundStatus);
         sb.append(", createdDate=").append(createdDate);
@@ -130,12 +145,18 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
             return false;
         }
+        if (processedAmount != null ? !processedAmount.equals(that.processedAmount) : that.processedAmount != null) {
+            return false;
+        }
         if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
             return false;
         }
         if (currency != that.currency) {
             return false;
         }
+        if (processedCurrency != that.processedCurrency) {
+            return false;
+        }
         if (paymentId != null ? !paymentId.equals(that.paymentId) : that.paymentId != null) {
             return false;
         }
@@ -155,6 +176,8 @@ public class RefundModelDao extends EntityBase implements EntityModelDao<Refund>
         result = 31 * result + (paymentId != null ? paymentId.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (processedAmount != null ? processedAmount.hashCode() : 0);
+        result = 31 * result + (processedCurrency != null ? processedCurrency.hashCode() : 0);
         result = 31 * result + (isAdjusted ? 1 : 0);
         result = 31 * result + (refundStatus != null ? refundStatus.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
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 ffaa446..4c3f0cb 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
@@ -16,6 +16,7 @@
 
 package com.ning.billing.payment.dao;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -23,6 +24,7 @@ import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.callcontext.InternalCallContext;
@@ -38,6 +40,8 @@ public interface RefundSqlDao extends EntitySqlDao<RefundModelDao, Refund> {
     @Audited(ChangeType.UPDATE)
     void updateStatus(@Bind("id") final String refundId,
                       @Bind("refundStatus") final String status,
+                      @Bind("processedAmount") final BigDecimal processedAmount,
+                      @Bind("processedCurrency") final Currency processedCurrency,
                       @BindBean final InternalCallContext context);
 
     @SqlQuery
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 b479d2c..e7590c8 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
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
 import com.ning.billing.payment.plugin.api.PaymentPluginStatus;
 
@@ -30,14 +31,16 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     private final DateTime createdDate;
     private final PaymentPluginStatus status;
     private final String error;
+    private final Currency currency;
 
-    public DefaultNoOpPaymentInfoPlugin(final BigDecimal amount, final DateTime effectiveDate,
+    public DefaultNoOpPaymentInfoPlugin(final BigDecimal amount, final Currency currency, final DateTime effectiveDate,
                                         final DateTime createdDate, final PaymentPluginStatus status, final String error) {
         this.amount = amount;
         this.effectiveDate = effectiveDate;
         this.createdDate = createdDate;
         this.status = status;
         this.error = error;
+        this.currency = currency;
     }
 
     @Override
@@ -46,6 +49,11 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     }
 
     @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
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 f33a754..9de86b6 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
@@ -96,7 +96,7 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
         payments.put(kbPaymentId.toString(), result);
         return result;
     }
@@ -205,7 +205,7 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
                                                                   refundAmount, kbPaymentId.toString(), paymentInfoPlugin.getAmount(), PLUGIN_NAME));
         }
 
-        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
         refunds.put(kbPaymentId.toString(), refundInfoPlugin);
 
         return refundInfoPlugin;
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java
index d5a59a4..c3294d4 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java
@@ -20,20 +20,23 @@ import java.math.BigDecimal;
 
 import org.joda.time.DateTime;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
 import com.ning.billing.payment.plugin.api.RefundPluginStatus;
 
 public class DefaultNoOpRefundInfoPlugin implements RefundInfoPlugin {
 
     private final BigDecimal amount;
+    private final Currency currency;
     private final DateTime effectiveDate;
     private final DateTime createdDate;
     private final RefundPluginStatus status;
     private final String error;
 
-    public DefaultNoOpRefundInfoPlugin(final BigDecimal amount, final DateTime effectiveDate,
+    public DefaultNoOpRefundInfoPlugin(final BigDecimal amount, final Currency currency, final DateTime effectiveDate,
                                        final DateTime createdDate, final RefundPluginStatus status, final String error) {
         this.amount = amount;
+        this.currency = currency;
         this.effectiveDate = effectiveDate;
         this.createdDate = createdDate;
         this.status = status;
@@ -46,6 +49,11 @@ public class DefaultNoOpRefundInfoPlugin implements RefundInfoPlugin {
     }
 
     @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index a2abb55..8658da9 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -60,17 +60,17 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
 
     @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+        return new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
     }
 
     @Override
     public PaymentInfoPlugin getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(BigDecimal.ZERO, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+        return new DefaultNoOpPaymentInfoPlugin(BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
     }
 
     @Override
     public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpRefundInfoPlugin(BigDecimal.ZERO, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+        return new DefaultNoOpRefundInfoPlugin(BigDecimal.ZERO, currency, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
     }
 
     @Override
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 c7121a5..6ad9b50 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
@@ -10,8 +10,10 @@ tableFields(prefix) ::= <<
 , <prefix>invoice_id
 , <prefix>payment_method_id
 , <prefix>amount
-, <prefix>effective_date
 , <prefix>currency
+, <prefix>processed_amount
+, <prefix>processed_currency
+, <prefix>effective_date
 , <prefix>payment_status
 , <prefix>created_by
 , <prefix>created_date
@@ -24,8 +26,10 @@ tableValues() ::= <<
 , :invoiceId
 , :paymentMethodId
 , :amount
-, :effectiveDate
 , :currency
+, :processedAmount
+, :processedCurrency
+, :effectiveDate
 , :paymentStatus
 , :createdBy
 , :createdDate
@@ -72,6 +76,8 @@ order by effective_date desc limit 1
 updatePaymentStatus() ::= <<
 update payments
 set payment_status = :paymentStatus
+, processed_amount = :processedAmount
+, processed_currency = :processedCurrency
 where id = :id
 <AND_CHECK_TENANT()>
 ;
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
index 68c06fb..aa8daa6 100644
--- 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
@@ -9,6 +9,8 @@ tableFields(prefix) ::= <<
 , <prefix>payment_id
 , <prefix>amount
 , <prefix>currency
+, <prefix>processed_amount
+, <prefix>processed_currency
 , <prefix>is_adjusted
 , <prefix>refund_status
 , <prefix>created_by
@@ -22,6 +24,8 @@ tableValues() ::= <<
 , :paymentId
 , :amount
 , :currency
+, :processedAmount
+, :processedCurrency
 , :isAdjusted
 , :refundStatus
 , :createdBy
@@ -33,6 +37,8 @@ tableValues() ::= <<
 updateStatus(refundStatus) ::= <<
 update <tableName()>
 set refund_status = :refundStatus
+, processed_amount = :processedAmount
+, processed_currency = :processedCurrency
 where id = :id
 <AND_CHECK_TENANT()>
 ;
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 5da9aa0..37f5a12 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -8,7 +8,9 @@ CREATE TABLE payments (
     invoice_id char(36) NOT NULL,
     payment_method_id char(36) NOT NULL,
     amount numeric(10,4),
-    currency char(3),    
+    currency char(3),
+    processed_amount numeric(10,4),
+    processed_currency char(3),
     effective_date datetime,
     payment_status varchar(50),
     created_by varchar(50) NOT NULL,
@@ -34,6 +36,8 @@ CREATE TABLE payment_history (
     payment_method_id char(36) NOT NULL,
     amount numeric(10,4),
     currency char(3),    
+    processed_amount numeric(10,4),
+    processed_currency char(3),
     effective_date datetime,
     payment_status varchar(50),
     ext_first_payment_ref_id varchar(128),
@@ -60,6 +64,7 @@ CREATE TABLE payment_attempts (
     gateway_error_msg varchar(256),
     processing_status varchar(50),
     requested_amount numeric(10,4),
+    requested_currency numeric(10,4),
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -83,6 +88,7 @@ CREATE TABLE payment_attempt_history (
     gateway_error_msg varchar(256),
     processing_status varchar(50),
     requested_amount numeric(10,4),
+    requested_currency numeric(10,4),
     change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
@@ -142,6 +148,8 @@ CREATE TABLE refunds (
     payment_id char(36) NOT NULL,
     amount numeric(10,4),
     currency char(3),
+    processed_amount numeric(10,4),
+    processed_currency char(3),
     is_adjusted tinyint(1),
     refund_status varchar(50), 
     created_by varchar(50) NOT NULL,
@@ -166,6 +174,8 @@ CREATE TABLE refund_history (
     payment_id char(36) NOT NULL,
     amount numeric(10,4),
     currency char(3),
+    processed_amount numeric(10,4),
+    processed_currency char(3),
     is_adjusted tinyint(1),
     refund_status varchar(50), 
     change_type char(6) NOT NULL,
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 418301b..fc75d31 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
@@ -16,6 +16,7 @@
 
 package com.ning.billing.payment.dao;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -27,6 +28,7 @@ import java.util.UUID;
 
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.util.entity.Pagination;
@@ -57,7 +59,9 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updatePaymentAndAttemptOnCompletion(final UUID paymentId, final PaymentStatus paymentStatus, final UUID attemptId, final String gatewayErrorCode,
+    public void updatePaymentAndAttemptOnCompletion(final UUID paymentId, final PaymentStatus paymentStatus,
+                                                    BigDecimal processedAmount, Currency processedCurrency,
+                                                    final UUID attemptId, final String gatewayErrorCode,
                                                     final String gatewayErrorMsg,
                                                     final InternalCallContext context) {
         synchronized (this) {
@@ -183,9 +187,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public void updateRefundStatus(final UUID refundId, final RefundStatus status, final InternalCallContext context) {
+    public void updateRefundStatus(final UUID refundId, final RefundStatus status, final BigDecimal processedAmount, final Currency processedCurrency, final InternalCallContext context) {
+        return;
     }
 
+
     @Override
     public RefundModelDao getRefund(final UUID refundId, final InternalTenantContext context) {
         return null;
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 29a5162..70033b8 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
@@ -45,7 +45,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final BigDecimal amount1 = new BigDecimal(13);
         final Currency currency = Currency.USD;
 
-        final RefundModelDao refund1 = new RefundModelDao(accountId, paymentId1, amount1, currency, true);
+        final RefundModelDao refund1 = new RefundModelDao(accountId, paymentId1, amount1, currency, amount1, currency, true);
 
         paymentDao.insertRefund(refund1, internalCallContext);
         final RefundModelDao refundCheck = paymentDao.getRefund(refund1.getId(), internalCallContext);
@@ -60,9 +60,9 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final BigDecimal amount2 = new BigDecimal(7.00);
         final UUID paymentId2 = UUID.randomUUID();
 
-        RefundModelDao refund2 = new RefundModelDao(accountId, paymentId2, amount2, currency, true);
+        RefundModelDao refund2 = new RefundModelDao(accountId, paymentId2, amount2, currency, amount2, currency, true);
         paymentDao.insertRefund(refund2, internalCallContext);
-        paymentDao.updateRefundStatus(refund2.getId(), RefundStatus.COMPLETED, internalCallContext);
+        paymentDao.updateRefundStatus(refund2.getId(), RefundStatus.COMPLETED, amount2, currency, internalCallContext);
 
         List<RefundModelDao> refundChecks = paymentDao.getRefundsForPayment(paymentId1, internalCallContext);
         assertEquals(refundChecks.size(), 1);
@@ -95,7 +95,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final DateTime effectiveDate = clock.getUTCNow();
 
         final PaymentModelDao payment = new PaymentModelDao(accountId, invoiceId, paymentMethodId, amount, currency, effectiveDate);
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, effectiveDate, amount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, effectiveDate, amount, currency);
         PaymentModelDao savedPayment = paymentDao.insertPaymentWithFirstAttempt(payment, attempt, internalCallContext);
         assertEquals(savedPayment.getEffectiveDate().compareTo(effectiveDate), 0);
 
@@ -103,7 +103,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final String gatewayErrorCode = "OK";
 
         clock.addDays(1);
-        paymentDao.updatePaymentAndAttemptOnCompletion(payment.getId(), paymentStatus, attempt.getId(), gatewayErrorCode, null, internalCallContext);
+        paymentDao.updatePaymentAndAttemptOnCompletion(payment.getId(), paymentStatus, amount, currency, attempt.getId(), gatewayErrorCode, null, internalCallContext);
 
         final List<PaymentModelDao> payments = paymentDao.getPaymentsForInvoice(invoiceId, internalCallContext);
         assertEquals(payments.size(), 1);
@@ -139,7 +139,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final DateTime effectiveDate = clock.getUTCNow();
 
         final PaymentModelDao payment = new PaymentModelDao(accountId, invoiceId, paymentMethodId, amount, currency, effectiveDate);
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, clock.getUTCNow(), amount);
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, clock.getUTCNow(), amount, currency);
 
         PaymentModelDao savedPayment = paymentDao.insertPaymentWithFirstAttempt(payment, attempt, internalCallContext);
         assertEquals(savedPayment.getId(), payment.getId());
@@ -191,7 +191,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final DateTime effectiveDate = clock.getUTCNow();
 
         final PaymentModelDao payment = new PaymentModelDao(accountId, invoiceId, paymentMethodId, amount, currency, effectiveDate);
-        final PaymentAttemptModelDao firstAttempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, effectiveDate, amount);
+        final PaymentAttemptModelDao firstAttempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), paymentMethodId, effectiveDate, amount, currency);
         PaymentModelDao savedPayment = paymentDao.insertPaymentWithFirstAttempt(payment, firstAttempt, internalCallContext);
 
         final PaymentModelDao lastPayment = paymentDao.getLastPaymentForPaymentMethod(accountId, paymentMethodId, internalCallContext);
@@ -210,7 +210,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final DateTime newEffectiveDate = clock.getUTCNow();
         final UUID newPaymentMethodId = UUID.randomUUID();
         final BigDecimal newAmount = new BigDecimal(15.23).setScale(2, RoundingMode.HALF_EVEN);
-        final PaymentAttemptModelDao secondAttempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), newPaymentMethodId, newEffectiveDate, newAmount);
+        final PaymentAttemptModelDao secondAttempt = new PaymentAttemptModelDao(accountId, invoiceId, payment.getId(), newPaymentMethodId, newEffectiveDate, newAmount, currency);
         paymentDao.updatePaymentWithNewAttempt(payment.getId(), secondAttempt, internalCallContext);
 
         final List<PaymentModelDao> payments = paymentDao.getPaymentsForInvoice(invoiceId, internalCallContext);
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 0d383c6..f4e4ec9 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -102,7 +102,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
         payments.put(kbPaymentId.toString(), result);
         return result;
     }
@@ -190,7 +190,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
                                                                   refundAmount, kbPaymentId.toString(), paymentInfoPlugin.getAmount(), PLUGIN_NAME));
         }
 
-        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
         refunds.put(kbPaymentId.toString(), refundInfoPlugin);
 
         return refundInfoPlugin;
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
index 13ea610..f74c0e0 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
@@ -23,6 +23,7 @@ import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.payment.PaymentTestSuiteNoDB;
 import com.ning.billing.payment.plugin.api.PaymentPluginStatus;
 
@@ -36,15 +37,15 @@ public class TestDefaultNoOpPaymentInfoPlugin extends PaymentTestSuiteNoDB {
         final PaymentPluginStatus status = PaymentPluginStatus.UNDEFINED;
         final String error = UUID.randomUUID().toString();
 
-        final DefaultNoOpPaymentInfoPlugin info = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin info = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
                                                                                    status, error);
         Assert.assertEquals(info, info);
 
-        final DefaultNoOpPaymentInfoPlugin sameInfo = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin sameInfo = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
                                                                                        status, error);
         Assert.assertEquals(sameInfo, info);
 
-        final DefaultNoOpPaymentInfoPlugin otherInfo = new DefaultNoOpPaymentInfoPlugin(amount, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin otherInfo = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
                                                                                         status, UUID.randomUUID().toString());
         Assert.assertNotEquals(otherInfo, info);
     }
diff --git a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
index f5c4896..bf99755 100644
--- a/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/com/ning/billing/subscription/api/transfer/TestTransfer.java
@@ -51,7 +51,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
     protected static final Logger log = LoggerFactory.getLogger(TestTransfer.class);
 
 
-    @Test(groups = "slow", enabled = false)
+    @Test(groups = "slow", enabled = true)
     public void testTransferMigratedSubscriptionWithCTDInFuture() throws Exception {
 
         final UUID newAccountId = UUID.randomUUID();