killbill-memoizeit

Changes

pom.xml 2(+1 -1)

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectPaymentJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectPaymentJson.java
new file mode 100644
index 0000000..40485d6
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectPaymentJson.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.audit.AuditLog;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class DirectPaymentJson extends JsonBase {
+
+    private final String accountId;
+    private final String directPaymentId;
+    private final String paymentNumber;
+    private final BigDecimal authAmount;
+    private final BigDecimal capturedAmount;
+    private final BigDecimal refundedAmount;
+    private final String currency;
+    private final String paymentMethodId;
+    private final List<DirectTransactionJson> transactions;
+
+    @JsonCreator
+    public DirectPaymentJson(@JsonProperty("accountId") final String accountId,
+                             @JsonProperty("directPaymentId") final String directPaymentId,
+                             @JsonProperty("paymentNumber") final String paymentNumber,
+                             @JsonProperty("authAmount") final BigDecimal authAmount,
+                             @JsonProperty("capturedAmount") final BigDecimal capturedAmount,
+                             @JsonProperty("refundedAmount") final BigDecimal refundedAmount,
+                             @JsonProperty("currency") final String currency,
+                             @JsonProperty("paymentMethodId") final String paymentMethodId,
+                             @JsonProperty("transactions") final List<DirectTransactionJson> transactions,
+                             @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
+        super(auditLogs);
+        this.accountId = accountId;
+        this.directPaymentId = directPaymentId;
+        this.paymentNumber = paymentNumber;
+        this.authAmount = authAmount;
+        this.capturedAmount = capturedAmount;
+        this.refundedAmount = refundedAmount;
+        this.currency = currency;
+        this.paymentMethodId = paymentMethodId;
+        this.transactions = transactions;
+    }
+
+    public DirectPaymentJson(final DirectPayment dp, @Nullable final List<AuditLog> directPaymentLogs, @Nullable final List<AuditLog> directTransactionLogs) {
+        this(dp.getAccountId().toString(),
+              dp.getId().toString(),
+              dp.getPaymentNumber().toString(),
+              dp.getAuthAmount(),
+              dp.getCapturedAmount(),
+              dp.getRefundedAmount(),
+              dp.getCurrency() != null ? dp.getCurrency().toString() : null,
+              dp.getPaymentMethodId() != null ? dp.getPaymentMethodId().toString() : null,
+              getTransactions(dp.getTransactions(), dp.getId(), dp.getExternalKey(), directTransactionLogs),
+              toAuditLogJson(directPaymentLogs));
+    }
+
+
+    private static List<DirectTransactionJson> getTransactions(final List<DirectPaymentTransaction> transactions, final UUID directPaymentId, final String externalKey, @Nullable final List<AuditLog> directTransactionLogs) {
+        return ImmutableList.copyOf(Iterables.transform(transactions, new Function<DirectPaymentTransaction, DirectTransactionJson>() {
+            @Override
+            public DirectTransactionJson apply(final DirectPaymentTransaction input) {
+                return new DirectTransactionJson(input,directPaymentId, externalKey, directTransactionLogs);
+            }
+        }));
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public String getDirectPaymentId() {
+        return directPaymentId;
+    }
+
+    public String getPaymentNumber() {
+        return paymentNumber;
+    }
+
+    public BigDecimal getAuthAmount() {
+        return authAmount;
+    }
+
+    public BigDecimal getCapturedAmount() {
+        return capturedAmount;
+    }
+
+    public BigDecimal getRefundedAmount() {
+        return refundedAmount;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public String getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public List<DirectTransactionJson> getTransactions() {
+        return transactions;
+    }
+
+    @Override
+    public String toString() {
+        return "DirectPaymentJson{" +
+               "accountId='" + accountId + '\'' +
+               ", directPaymentId='" + directPaymentId + '\'' +
+               ", paymentNumber='" + paymentNumber + '\'' +
+               ", authAmount=" + authAmount +
+               ", capturedAmount=" + capturedAmount +
+               ", refundedAmount=" + refundedAmount +
+               ", currency='" + currency + '\'' +
+               ", paymentMethodId='" + paymentMethodId + '\'' +
+               ", transactions=" + transactions +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DirectPaymentJson)) {
+            return false;
+        }
+
+        final DirectPaymentJson that = (DirectPaymentJson) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (authAmount != null ? authAmount.compareTo(that.authAmount) != 0 : that.authAmount != null) {
+            return false;
+        }
+        if (capturedAmount != null ? capturedAmount.compareTo(that.capturedAmount) != 0 : that.capturedAmount != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
+            return false;
+        }
+        if (directPaymentId != null ? !directPaymentId.equals(that.directPaymentId) : that.directPaymentId != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (paymentNumber != null ? !paymentNumber.equals(that.paymentNumber) : that.paymentNumber != null) {
+            return false;
+        }
+        if (refundedAmount != null ? refundedAmount.compareTo(that.refundedAmount) != 0 : that.refundedAmount != null) {
+            return false;
+        }
+        if (transactions != null ? !transactions.equals(that.transactions) : that.transactions != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = accountId != null ? accountId.hashCode() : 0;
+        result = 31 * result + (directPaymentId != null ? directPaymentId.hashCode() : 0);
+        result = 31 * result + (paymentNumber != null ? paymentNumber.hashCode() : 0);
+        result = 31 * result + (authAmount != null ? authAmount.hashCode() : 0);
+        result = 31 * result + (capturedAmount != null ? capturedAmount.hashCode() : 0);
+        result = 31 * result + (refundedAmount != null ? refundedAmount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (transactions != null ? transactions.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectTransactionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectTransactionJson.java
new file mode 100644
index 0000000..29df0bf
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/DirectTransactionJson.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.util.audit.AuditLog;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class DirectTransactionJson extends JsonBase {
+
+    private final String directTransactionId;
+    private final String directPaymentId;
+    private final String transactionType;
+    private final DateTime effectiveDate;
+    private final Integer retryCount;
+    private final String status;
+    private final BigDecimal amount;
+    private final String currency;
+    private final String externalKey;
+    private final String gatewayErrorCode;
+    private final String gatewayErrorMsg;
+
+    @JsonCreator
+    public DirectTransactionJson(@JsonProperty("directTransactionId") final String directTransactionId,
+                                 @JsonProperty("directPaymentId") final String directPaymentId,
+                                 @JsonProperty("transactionType") final String transactionType,
+                                 @JsonProperty("amount") final BigDecimal amount,
+                                 @JsonProperty("currency") final String currency,
+                                 @JsonProperty("effectiveDate") final DateTime effectiveDate,
+                                 @JsonProperty("status") final String status,
+                                 @JsonProperty("retryCount") final Integer retryCount,
+                                 @JsonProperty("externalKey") final String externalKey,
+                                 @JsonProperty("gatewayErrorCode") final String gatewayErrorCode,
+                                 @JsonProperty("gatewayErrorMsg") final String gatewayErrorMsg,
+                                 @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
+        super(auditLogs);
+        this.directTransactionId = directTransactionId;
+        this.directPaymentId = directPaymentId;
+        this.transactionType = transactionType;
+        this.effectiveDate = effectiveDate;
+        this.retryCount = retryCount;
+        this.externalKey = externalKey;
+        this.status = status;
+        this.amount = amount;
+        this.currency = currency;
+        this.gatewayErrorCode = gatewayErrorCode;
+        this.gatewayErrorMsg = gatewayErrorMsg;
+    }
+
+    public DirectTransactionJson(final DirectPaymentTransaction dpt, final UUID directPaymentId, final String externalKey, @Nullable final List<AuditLog> directTransactionLogs) {
+        this(dpt.getId().toString(),
+             directPaymentId.toString(),
+             dpt.getTransactionType().toString(),
+             dpt.getAmount(),
+             dpt.getCurrency().toString(),
+             dpt.getEffectiveDate(),
+             dpt.getPaymentStatus().toString(),
+             1,
+             externalKey,
+             dpt.getGatewayErrorCode(),
+             dpt.getGatewayErrorMsg(),
+             toAuditLogJson(directTransactionLogs));
+    }
+
+    public String getDirectTransactionId() {
+        return directTransactionId;
+    }
+
+    public String getDirectPaymentId() {
+        return directPaymentId;
+    }
+
+    public String getTransactionType() {
+        return transactionType;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public Integer getRetryCount() {
+        return retryCount;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public String getGatewayErrorCode() {
+        return gatewayErrorCode;
+    }
+
+    public String getGatewayErrorMsg() {
+        return gatewayErrorMsg;
+    }
+
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    @Override
+    public String toString() {
+        return "DirectTransactionJson{" +
+               "directPaymentId=" + directPaymentId +
+               "directTransactionId=" + directTransactionId +
+               "transactionType=" + transactionType +
+               ", effectiveDate=" + effectiveDate +
+               ", retryCount=" + retryCount +
+               ", status='" + status + '\'' +
+               ", externalKey='" + externalKey + '\'' +
+               ", amount=" + amount +
+               ", currency='" + currency + '\'' +
+               ", gatewayErrorCode='" + gatewayErrorCode + '\'' +
+               ", gatewayErrorMsg='" + gatewayErrorMsg + '\'' +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DirectTransactionJson)) {
+            return false;
+        }
+
+        final DirectTransactionJson that = (DirectTransactionJson) o;
+
+        if (directPaymentId != null ? !directPaymentId.equals(that.directPaymentId) : that.directPaymentId != null) {
+            return false;
+        }
+        if (directTransactionId != null ? !directTransactionId.equals(that.directTransactionId) : that.directTransactionId != null) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
+        if (gatewayErrorCode != null ? !gatewayErrorCode.equals(that.gatewayErrorCode) : that.gatewayErrorCode != null) {
+            return false;
+        }
+        if (gatewayErrorMsg != null ? !gatewayErrorMsg.equals(that.gatewayErrorMsg) : that.gatewayErrorMsg != null) {
+            return false;
+        }
+        if (retryCount != null ? !retryCount.equals(that.retryCount) : that.retryCount != null) {
+            return false;
+        }
+        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+            return false;
+        }
+        if (status != null ? !status.equals(that.status) : that.status != null) {
+            return false;
+        }
+        if (transactionType.equals(that.transactionType)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = transactionType != null ? transactionType.hashCode() : 0;
+        result = 31 * result + (directPaymentId != null ? directPaymentId.hashCode() : 0);
+        result = 31 * result + (directTransactionId != null ? directTransactionId.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (retryCount != null ? retryCount.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
+        result = 31 * result + (gatewayErrorMsg != null ? gatewayErrorMsg.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 3d76f01..e4c0274 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -50,12 +50,11 @@ import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.AccountEmail;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.MutableAccountData;
-import org.killbill.billing.invoice.api.InvoiceApiException;
-import org.killbill.clock.Clock;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
@@ -65,6 +64,8 @@ import org.killbill.billing.jaxrs.json.AccountTimelineJson;
 import org.killbill.billing.jaxrs.json.BundleJson;
 import org.killbill.billing.jaxrs.json.ChargebackJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
+import org.killbill.billing.jaxrs.json.DirectPaymentJson;
+import org.killbill.billing.jaxrs.json.DirectTransactionJson;
 import org.killbill.billing.jaxrs.json.InvoiceEmailJson;
 import org.killbill.billing.jaxrs.json.InvoiceJson;
 import org.killbill.billing.jaxrs.json.OverdueStateJson;
@@ -77,11 +78,14 @@ import org.killbill.billing.overdue.OverdueApiException;
 import org.killbill.billing.overdue.OverdueState;
 import org.killbill.billing.overdue.OverdueUserApi;
 import org.killbill.billing.overdue.config.api.OverdueException;
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.DirectPaymentApi;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.Refund;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -94,11 +98,13 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.tag.ControlTagType;
+import org.killbill.clock.Clock;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
@@ -117,6 +123,7 @@ public class AccountResource extends JaxRsResourceBase {
     private final InvoiceUserApi invoiceApi;
     private final InvoicePaymentApi invoicePaymentApi;
     private final PaymentApi paymentApi;
+    private final DirectPaymentApi directPaymentApi;
     private final OverdueUserApi overdueApi;
 
     @Inject
@@ -125,6 +132,7 @@ public class AccountResource extends JaxRsResourceBase {
                            final InvoiceUserApi invoiceApi,
                            final InvoicePaymentApi invoicePaymentApi,
                            final PaymentApi paymentApi,
+                           final DirectPaymentApi directPaymentApi,
                            final TagUserApi tagUserApi,
                            final AuditUserApi auditUserApi,
                            final CustomFieldUserApi customFieldUserApi,
@@ -137,6 +145,7 @@ public class AccountResource extends JaxRsResourceBase {
         this.invoiceApi = invoiceApi;
         this.invoicePaymentApi = invoicePaymentApi;
         this.paymentApi = paymentApi;
+        this.directPaymentApi = directPaymentApi;
         this.overdueApi = overdueApi;
     }
 
@@ -177,7 +186,8 @@ public class AccountResource extends JaxRsResourceBase {
                                                         return getAccount(account, accountWithBalance, accountWithBalanceAndCBA, accountAuditLogs, tenantContext);
                                                     }
                                                 },
-                                                nextPageUri);
+                                                nextPageUri
+                                               );
     }
 
     @GET
@@ -204,7 +214,8 @@ public class AccountResource extends JaxRsResourceBase {
                                                         return getAccount(account, accountWithBalance, accountWithBalanceAndCBA, accountAuditLogs, tenantContext);
                                                     }
                                                 },
-                                                nextPageUri);
+                                                nextPageUri
+                                               );
     }
 
     @GET
@@ -588,6 +599,74 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
     /*
+     * ************************* DIRECT PAYMENTS *****************************
+     */
+    @GET
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + DIRECT_PAYMENTS)
+    @Produces(APPLICATION_JSON)
+    public Response getDirectPaymentsForAccount(@PathParam("accountId") final String accountIdStr,
+                                                @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                             @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
+
+        final UUID accountId = UUID.fromString(accountIdStr);
+        final List<DirectPayment> payments =  directPaymentApi.getAccountPayments(accountId, withPluginInfo, context.createContext(request));
+        final List<DirectPaymentJson> result = ImmutableList.copyOf(Iterables.transform(payments, new Function<DirectPayment, DirectPaymentJson>() {
+            @Override
+            public DirectPaymentJson apply(final DirectPayment input) {
+                // STEPH_DP audits
+                return new DirectPaymentJson(input, null, null);
+            }
+        }));
+        return Response.status(Response.Status.OK).entity(result).build();
+    }
+
+        @POST
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + DIRECT_PAYMENTS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response processDirectPayment(final DirectTransactionJson json,
+                                         @PathParam("accountId") final String accountIdStr,
+                                         @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                         @HeaderParam(HDR_REASON) final String reason,
+                                         @HeaderParam(HDR_COMMENT) final String comment,
+                                         @javax.ws.rs.core.Context final UriInfo uriInfo,
+                                         @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID accountId = UUID.fromString(accountIdStr);
+        final Account account = accountUserApi.getAccountById(accountId, callContext);
+
+        final TransactionType transactionType = TransactionType.valueOf(json.getTransactionType());
+        DirectPayment result;
+        switch (transactionType) {
+            case AUTHORIZE:
+                result = directPaymentApi.createAuthorization(account, json.getAmount(), json.getExternalKey(), callContext);
+                break;
+
+            case CAPTURE:
+                result = directPaymentApi.createCapture(account, UUID.fromString(json.getDirectPaymentId()), json.getAmount(), callContext);
+                break;
+
+            case CREDIT:
+                result = directPaymentApi.createCredit(account, UUID.fromString(json.getDirectPaymentId()), callContext);
+                break;
+
+            case PURCHASE:
+                result = directPaymentApi.createPurchase(account, json.getAmount(), json.getExternalKey(), callContext);
+                break;
+
+            case VOID:
+                result = directPaymentApi.createVoid(account, UUID.fromString(json.getDirectPaymentId()), callContext);
+                break;
+
+            default:
+                return Response.status(Status.PRECONDITION_FAILED).entity("Unknown transactionType " + transactionType).build();
+        }
+            // STEPH_DP needs to return 201 with Location
+        return Response.status(Response.Status.OK).entity(new DirectPaymentJson(result, null, null)).build();
+    }
+
+    /*
      * ************************** CHARGEBACKS ********************************
      */
     @GET
@@ -791,7 +870,8 @@ public class AccountResource extends JaxRsResourceBase {
                                                                                public boolean apply(final AccountEmail input) {
                                                                                    return input.getEmail().equals(json.getEmail());
                                                                                }
-                                                                           })
+                                                                           }
+                                                                          )
                                                     .orNull();
         if (existingEmail == null) {
             accountUserApi.addEmail(accountId, json.toAccountEmail(UUID.randomUUID()), callContext);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 35c1972..e4747ce 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -138,6 +138,9 @@ public interface JaxrsResource {
     public static final String PAYMENTS = "payments";
     public static final String PAYMENTS_PATH = PREFIX + "/" + PAYMENTS;
 
+    public static final String DIRECT_PAYMENTS = "directPayments";
+    public static final String DIRECT_PAYMENTS_PATH = PREFIX + "/" + DIRECT_PAYMENTS;
+
     public static final String REFUNDS = "refunds";
     public static final String REFUNDS_PATH = PREFIX + "/" + "refunds";
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index c52b05b..e9198e7 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -296,19 +296,6 @@ public class PaymentResource extends JaxRsResourceBase {
         throw new UnsupportedOperationException();
     }
 
-    @POST
-    @Path("/{paymentId:" + UUID_PATTERN + "}/")
-    @Consumes(APPLICATION_JSON)
-    @Produces(APPLICATION_JSON)
-    public Response processPayment(final PaymentJson json,
-                                   @PathParam("paymentId") final String paymentId,
-                                   @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                   @HeaderParam(HDR_REASON) final String reason,
-                                   @HeaderParam(HDR_COMMENT) final String comment,
-                                   @javax.ws.rs.core.Context final UriInfo uriInfo,
-                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
-        throw new UnsupportedOperationException();
-    }
 
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
diff --git a/osgi-bundles/bundles/jruby/src/main/java/org/killbill/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java b/osgi-bundles/bundles/jruby/src/main/java/org/killbill/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
index 6a88c2f..01f13e7 100644
--- a/osgi-bundles/bundles/jruby/src/main/java/org/killbill/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
+++ b/osgi-bundles/bundles/jruby/src/main/java/org/killbill/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
@@ -23,6 +23,9 @@ import java.util.List;
 import java.util.UUID;
 
 import org.jruby.Ruby;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.log.LogService;
@@ -67,6 +70,17 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
         super.stopPlugin(context);
     }
 
+    // STEPH_DP
+    @Override
+    public PaymentInfoPlugin authorizePayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final BigDecimal bigDecimal, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final BigDecimal bigDecimal, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
     @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
 
@@ -79,6 +93,11 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
     }
 
     @Override
+    public PaymentInfoPlugin voidPayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
     public PaymentInfoPlugin getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
 
         return callWithRuntimeAndChecking(new PluginCallback(VALIDATION_PLUGIN_TYPE.PAYMENT) {
@@ -209,4 +228,14 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
             }
         });
     }
+
+    @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
 }
diff --git a/osgi-bundles/bundles/logger/pom.xml b/osgi-bundles/bundles/logger/pom.xml
index 048e123..d5380cd 100644
--- a/osgi-bundles/bundles/logger/pom.xml
+++ b/osgi-bundles/bundles/logger/pom.xml
@@ -56,7 +56,7 @@
                 <configuration>
                     <instructions>
                         <Bundle-Activator>org.killbill.billing.osgi.bundles.logger.Activator</Bundle-Activator>
-                        <Export-Package />
+                        <Export-Package></Export-Package>
                         <Private-Package>org.killbill.billing.osgi.bundles.logger.*</Private-Package>
                         <!-- Optional resolution because exported by the Felix system bundle -->
                         <Import-Package>*;resolution:=optional</Import-Package>
diff --git a/osgi-bundles/tests/beatrix/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/beatrix/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
index 21267b2..6e7947b 100644
--- a/osgi-bundles/tests/beatrix/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/beatrix/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -27,6 +27,9 @@ import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.osgi.bundles.test.dao.TestDao;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentMethodInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -48,6 +51,16 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
     }
 
     @Override
+    public PaymentInfoPlugin authorizePayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final BigDecimal bigDecimal, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final BigDecimal bigDecimal, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
         testDao.insertProcessedPayment(kbPaymentId, kbPaymentMethodId, amount);
         return new PaymentInfoPlugin() {
@@ -104,6 +117,11 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
     }
 
     @Override
+    public PaymentInfoPlugin voidPayment(final UUID uuid, final UUID uuid2, final UUID uuid3, final CallContext callContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
     public PaymentInfoPlugin getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
         return null;
     }
@@ -233,4 +251,14 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
     @Override
     public void resetPaymentMethods(final UUID kbAccountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
     }
+
+    @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
 }
diff --git a/osgi-bundles/tests/payment/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/payment/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
index 9c713ba..46d5685 100644
--- a/osgi-bundles/tests/payment/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/payment/src/test/java/org/killbill/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -26,6 +26,9 @@ import org.joda.time.DateTime;
 
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentMethodInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
@@ -50,7 +53,23 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     }
 
     @Override
+    public PaymentInfoPlugin authorizePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
     public PaymentInfoPlugin processPayment(final UUID accountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
+
+    private PaymentInfoPlugin getPaymentInfoPluginResult(final UUID kbPaymentId, final BigDecimal amount, final Currency currency) throws PaymentPluginApiException {
         return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
             @Override
             public UUID getKbPaymentId() {
@@ -105,60 +124,17 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     }
 
     @Override
-    public PaymentInfoPlugin getPaymentInfo(final UUID accountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
-
-        final BigDecimal someAmount = new BigDecimal("12.45");
-        return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
-            @Override
-            public UUID getKbPaymentId() {
-                return kbPaymentId;
-            }
-
-            @Override
-            public BigDecimal getAmount() {
-                return someAmount;
-            }
-
-            @Override
-            public Currency getCurrency() {
-                return null;
-            }
-
-            @Override
-            public DateTime getCreatedDate() {
-                return new DateTime();
-            }
-
-            @Override
-            public DateTime getEffectiveDate() {
-                return new DateTime();
-            }
+    public PaymentInfoPlugin voidPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, BigDecimal.ZERO, null);
 
-            @Override
-            public PaymentPluginStatus getStatus() {
-                return PaymentPluginStatus.PROCESSED;
-            }
-
-            @Override
-            public String getGatewayError() {
-                return null;
-            }
-
-            @Override
-            public String getGatewayErrorCode() {
-                return null;
-            }
+    }
 
-            @Override
-            public String getFirstPaymentReferenceId() {
-                return null;
-            }
+    @Override
+    public PaymentInfoPlugin getPaymentInfo(final UUID accountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
 
-            @Override
-            public String getSecondPaymentReferenceId() {
-                return null;
-            }
-        });
+        final BigDecimal someAmount = new BigDecimal("12.45");
+        return getPaymentInfoPluginResult(kbPaymentId, someAmount, null);
     }
 
     @Override
@@ -337,6 +313,16 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     public void resetPaymentMethods(final UUID accountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
     }
 
+    @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
+
     private <T> T withRuntimeCheckForExceptions(final T result) throws PaymentPluginApiException {
         if (paymentPluginApiExceptionOnNextCalls != null) {
             throw paymentPluginApiExceptionOnNextCalls;
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPayment.java
new file mode 100644
index 0000000..a7a822a
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPayment.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.payment.api;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entity.EntityBase;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+public class DefaultDirectPayment extends EntityBase implements DirectPayment {
+
+    private final UUID accountId;
+    private final UUID paymentMethodId;
+    private final Integer paymentNumber;
+    private final String externalKey;
+    private final BigDecimal authAmount;
+    private final BigDecimal captureAmount;
+    private final BigDecimal refundAmount;
+    private final Currency currency;
+    private final PaymentStatus paymentStatus;
+    private final List<DirectPaymentTransaction> transactions;
+
+    public DefaultDirectPayment(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
+                                final UUID paymentMethodId,
+                                final Integer paymentNumber,
+                                final String externalKey,
+                                final List<DirectPaymentTransaction> transactions) {
+        super(id, createdDate, updatedDate);
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        this.paymentNumber = paymentNumber;
+        this.externalKey = externalKey;
+        this.transactions = transactions;
+        this.authAmount = getAmountForType(transactions, TransactionType.AUTHORIZE);
+        this.captureAmount = getAmountForType(transactions, TransactionType.CAPTURE);
+        this.refundAmount = getAmountForType(transactions, TransactionType.CREDIT);
+        this.currency = (transactions != null && transactions.size() > 0) ? transactions.get(0).getCurrency() : null;
+        this.paymentStatus = (transactions != null && transactions.size() > 0) ? transactions.get(transactions.size() - 1).getPaymentStatus() : null;
+    }
+
+    private static BigDecimal getAmountForType(final List<DirectPaymentTransaction> transactions, final TransactionType transactiontype) {
+        BigDecimal result = BigDecimal.ZERO;
+        final Iterable<DirectPaymentTransaction> filtered = Iterables.filter(transactions, new Predicate<DirectPaymentTransaction>() {
+            @Override
+            public boolean apply(final DirectPaymentTransaction input) {
+                return input.getTransactionType() == transactiontype;
+            }
+        });
+        for (DirectPaymentTransaction dpt : filtered) {
+            result = result.add(dpt.getAmount());
+        }
+        return result;
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    @Override
+    public Integer getPaymentNumber() {
+        return paymentNumber;
+    }
+
+    @Override
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    @Override
+    public BigDecimal getAuthAmount() {
+        return authAmount;
+    }
+
+    @Override
+    public BigDecimal getCapturedAmount() {
+        return captureAmount;
+    }
+
+    @Override
+    public BigDecimal getRefundedAmount() {
+        return refundAmount;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public PaymentStatus getPaymentStatus() {
+        return paymentStatus;
+    }
+
+    @Override
+    public List<DirectPaymentTransaction> getTransactions() {
+        return transactions;
+    }
+
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentTransaction.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentTransaction.java
new file mode 100644
index 0000000..27c927b
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultDirectPaymentTransaction.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * The Billing Project 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 org.killbill.billing.payment.api;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
+
+public class DefaultDirectPaymentTransaction extends EntityBase implements DirectPaymentTransaction {
+
+    private final UUID directTransactionId;
+    private final TransactionType transactionType;
+    private final DateTime effectiveDate;
+    private final PaymentStatus status;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final String gatewayErrorCode;
+    private final String gatewayErrorMsg;
+    private final PaymentInfoPlugin infoPlugin;
+    private final Integer retryCount;
+
+    public DefaultDirectPaymentTransaction(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID directTransactionId, final TransactionType transactionType,
+                                           final DateTime effectiveDate, final Integer retryCount, final PaymentStatus status, final BigDecimal amount, final Currency currency,
+                                           final String gatewayErrorCode, final String gatewayErrorMsg, final PaymentInfoPlugin infoPlugin) {
+        super(id, createdDate, updatedDate);
+        this.directTransactionId = directTransactionId;
+        this.transactionType = transactionType;
+        this.effectiveDate = effectiveDate;
+        this.retryCount = retryCount;
+        this.status = status;
+        this.amount = amount;
+        this.currency = currency;
+        this.gatewayErrorCode = gatewayErrorCode;
+        this.gatewayErrorMsg = gatewayErrorMsg;
+        this.infoPlugin = infoPlugin;
+    }
+
+    @Override
+    public UUID getDirectPaymentId() {
+        return directTransactionId;
+    }
+
+    @Override
+    public TransactionType getTransactionType() {
+        return transactionType;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public String getGatewayErrorCode() {
+        return gatewayErrorCode;
+    }
+
+    @Override
+    public String getGatewayErrorMsg() {
+        return gatewayErrorMsg;
+    }
+
+    @Override
+    public PaymentStatus getPaymentStatus() {
+        return status;
+    }
+
+    @Override
+    public PaymentInfoPlugin getPaymentInfoPlugin() {
+        return infoPlugin;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java
new file mode 100644
index 0000000..b8438b6
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/svcs/DefaultDirectPaymentApi.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * The Billing Project 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 org.killbill.billing.payment.api.svcs;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.DirectPaymentApi;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.core.DirectPaymentProcessor;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.clock.Clock;
+
+public class DefaultDirectPaymentApi implements DirectPaymentApi {
+
+    private final DirectPaymentProcessor directPaymentProcessor;
+    private final InternalCallContextFactory internalCallContextFactory;
+    private final Clock clock;
+
+    @Inject
+    public DefaultDirectPaymentApi(final DirectPaymentProcessor directPaymentProcessor, final InternalCallContextFactory internalCallContextFactory, final Clock clock) {
+        this.directPaymentProcessor = directPaymentProcessor;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.clock = clock;
+    }
+
+    @Override
+    public DirectPayment createAuthorization(final Account account, final BigDecimal amount, final String externalKey, final CallContext callContext) throws PaymentApiException {
+        return directPaymentProcessor.createAuthorization(account, amount, externalKey, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+    }
+
+    @Override
+    public DirectPayment createCapture(final Account account, final UUID directPaymentId, final BigDecimal amount, final CallContext callContext) throws PaymentApiException {
+        return directPaymentProcessor.createCapture(account, directPaymentId, amount, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+    }
+
+    @Override
+    public DirectPayment createPurchase(final Account account, final BigDecimal amount, final String externalKey, final CallContext callContext) throws PaymentApiException {
+        return directPaymentProcessor.createPurchase(account, amount, externalKey, internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
+    }
+
+    @Override
+    public DirectPayment createVoid(final Account account, final UUID directPaymentId, final CallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    @Override
+    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final CallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    @Override
+    public List<DirectPayment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final TenantContext tenantContext) throws PaymentApiException {
+        return directPaymentProcessor.getAccountPayments(accountId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+    }
+
+    @Override
+    public DirectPayment getPayment(final UUID directPaymentId, final boolean withPluginInfo, final TenantContext tenantContext) throws PaymentApiException {
+        return directPaymentProcessor.getPayment(directPaymentId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(tenantContext));
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
new file mode 100644
index 0000000..a27eb32
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/DirectPaymentProcessor.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.payment.core;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.DefaultDirectPayment;
+import org.killbill.billing.payment.api.DefaultDirectPaymentTransaction;
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentStatus;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.dao.DirectPaymentModelDao;
+import org.killbill.billing.payment.dao.DirectPaymentTransactionModelDao;
+import org.killbill.billing.payment.dao.PaymentDao;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
+import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
+import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.killbill.commons.locker.GlobalLocker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
+import com.google.inject.name.Named;
+
+import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+
+public class DirectPaymentProcessor extends ProcessorBase {
+
+    private final Clock clock;
+
+    private final PaymentConfig paymentConfig;
+
+    private final PluginDispatcher<Payment> paymentPluginDispatcher;
+    private final PluginDispatcher<Void> voidPluginDispatcher;
+
+    private static final Logger log = LoggerFactory.getLogger(DirectPaymentProcessor.class);
+
+    @Inject
+    public DirectPaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+                                  final AccountInternalApi accountUserApi,
+                                  final InvoiceInternalApi invoiceApi,
+                                  final TagInternalApi tagUserApi,
+                                  final PaymentDao paymentDao,
+                                  final NonEntityDao nonEntityDao,
+                                  final PersistentBus eventBus,
+                                  final Clock clock,
+                                  final GlobalLocker locker,
+                                  final PaymentConfig paymentConfig,
+                                  @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
+        super(pluginRegistry, accountUserApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
+        this.clock = clock;
+        this.paymentConfig = paymentConfig;
+        final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
+        this.paymentPluginDispatcher = new PluginDispatcher<Payment>(paymentPluginTimeoutSec, executor);
+        this.voidPluginDispatcher = new PluginDispatcher<Void>(paymentPluginTimeoutSec, executor);
+    }
+
+    public DirectPayment createAuthorization(final Account account, final BigDecimal amount, final String externalKey, final InternalCallContext callContext) throws PaymentApiException {
+
+        final PaymentPluginApi plugin = getPaymentProviderPlugin(account, callContext);
+
+        DateTime utcNow = clock.getUTCNow();
+        final DirectPaymentModelDao pmd = new DirectPaymentModelDao(utcNow, utcNow, account.getId(), account.getPaymentMethodId(), externalKey);
+        final DirectPaymentTransactionModelDao ptmd = new DirectPaymentTransactionModelDao(utcNow, utcNow, pmd.getId(),
+                                                                                                                 TransactionType.AUTHORIZE, utcNow, PaymentStatus.UNKNOWN,
+                                                                                                                 amount, account.getCurrency(), null, null);
+
+        final DirectPaymentModelDao inserted = paymentDao.insertDirectPaymentWithFirstTransaction(pmd, ptmd, callContext);
+        final UUID tenantId = nonEntityDao.retrieveIdFromObject(callContext.getTenantRecordId(), ObjectType.TENANT);
+
+        PaymentStatus paymentStatus;
+        PaymentInfoPlugin infoPlugin;
+        try {
+
+
+            try {
+                infoPlugin = plugin.authorizePayment(account.getId(), pmd.getId(), ptmd.getId(), amount, account.getCurrency(), callContext.toCallContext(tenantId));
+            } 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", pmd.getId());
+                throw new PaymentPluginApiException(formatError, e);
+            }
+
+            switch (infoPlugin.getStatus()) {
+                case PROCESSED:
+                case PENDING:
+                    // Update Payment/PaymentAttempt status
+                    paymentStatus = infoPlugin.getStatus() == PaymentPluginStatus.PROCESSED ? PaymentStatus.SUCCESS : PaymentStatus.PENDING;
+                    paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
+                                                                             ptmd.getId(), infoPlugin.getGatewayErrorCode(), null, callContext);
+                    break;
+
+                case ERROR:
+                    paymentStatus = PaymentStatus.PLUGIN_FAILURE_ABORTED;
+                    paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
+                                                                             ptmd.getId(), infoPlugin.getGatewayErrorCode(), infoPlugin.getGatewayError(), callContext);
+                    break;
+
+                case UNDEFINED:
+                default:
+                    final String formatError = String.format("Plugin return status %s for payment %s", infoPlugin.getStatus(), pmd.getId());
+                    // This caught right below as a retryable Plugin failure
+                    throw new PaymentPluginApiException("", formatError);
+            }
+        } catch (PaymentPluginApiException e) {
+            paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
+            infoPlugin = null;
+            paymentDao.updateDirectPaymentAndTransactionOnCompletion(pmd.getId(), paymentStatus, amount, account.getCurrency(),
+                                                                     ptmd.getId(), null, e.getMessage(), callContext);
+        } finally {
+        }
+
+        DirectPaymentTransaction transaction = new DefaultDirectPaymentTransaction(ptmd.getId(), utcNow, utcNow, pmd.getId(), ptmd.getTransactionType(), utcNow, 0,
+                                                                                paymentStatus, amount, account.getCurrency(),
+                                                                                ((infoPlugin != null) ? infoPlugin.getGatewayErrorCode() : null),
+                                                                                ((infoPlugin != null) ? infoPlugin.getGatewayError() : null),
+                                                                                infoPlugin);
+        final List<DirectPaymentTransaction> transactions = Collections.singletonList(transaction);
+        final DirectPayment result = new DefaultDirectPayment(inserted.getId(), utcNow, utcNow, account.getId(), account.getPaymentMethodId(), inserted.getPaymentNumber(), externalKey, transactions);
+        return result;
+    }
+
+    public DirectPayment createCapture(final Account account, final UUID directPaymentId, final BigDecimal amount, final InternalCallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    public DirectPayment createPurchase(final Account account, final BigDecimal amount, final String externalKey, final InternalCallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    public DirectPayment createVoid(final Account account, final UUID directPaymentId, final InternalCallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    public DirectPayment createCredit(final Account account, final UUID directPaymentId, final InternalCallContext callContext) throws PaymentApiException {
+        return null;
+    }
+
+    public List<DirectPayment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final InternalTenantContext tenantContext) throws PaymentApiException {
+
+        final List<DirectPaymentModelDao> paymentsModelDao = paymentDao.getDirectPaymentsForAccount(accountId, tenantContext);
+        final List<DirectPaymentTransactionModelDao> transactionsModelDao = paymentDao.getDirectTransactionsForAccount(accountId, tenantContext);
+
+        final Iterable<DirectPayment> payments = Iterables.transform(paymentsModelDao, new Function<DirectPaymentModelDao, DirectPayment>() {
+
+            final Ordering<DirectPaymentTransaction> perPaymentTransactionOrdering = Ordering.<DirectPaymentTransaction>from(new Comparator<DirectPaymentTransaction>() {
+                @Override
+                public int compare(final DirectPaymentTransaction o1, final DirectPaymentTransaction o2) {
+                    return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+                }
+            });
+
+            @Override
+            public DirectPayment apply(final DirectPaymentModelDao curDirectPaymentModelDao) {
+
+                final Iterable<DirectPaymentTransactionModelDao> filteredTransactions = Iterables.filter(transactionsModelDao, new Predicate<DirectPaymentTransactionModelDao>() {
+                    @Override
+                    public boolean apply(final DirectPaymentTransactionModelDao curDirectPaymentTransactionModelDao) {
+                        return curDirectPaymentTransactionModelDao.getDirectPaymentId().equals(curDirectPaymentModelDao.getId());
+                    }
+                });
+
+                final Iterable<DirectPaymentTransaction> transactions = Iterables.transform(filteredTransactions, new Function<DirectPaymentTransactionModelDao, DirectPaymentTransaction>() {
+                    @Override
+                    public DirectPaymentTransaction apply(final DirectPaymentTransactionModelDao input) {
+                        return new DefaultDirectPaymentTransaction(input.getId(), input.getCreatedDate(), input.getUpdatedDate(), input.getDirectPaymentId(),
+                                                                   input.getTransactionType(), input.getEffectiveDate(), 0, input.getPaymentStatus(), input.getAmount(), input.getCurrency(),
+                                                                   // STEPH_DP fill in details plugin info if required
+                                                                   input.getGatewayErrorCode(), input.getGatewayErrorMsg(), null);
+                    }
+                });
+
+                final List<DirectPaymentTransaction> sortedTransactions = perPaymentTransactionOrdering.immutableSortedCopy(transactions);
+                return new DefaultDirectPayment(curDirectPaymentModelDao.getId(), curDirectPaymentModelDao.getCreatedDate(), curDirectPaymentModelDao.getUpdatedDate(), curDirectPaymentModelDao.getAccountId(),
+                                                curDirectPaymentModelDao.getPaymentMethodId(), curDirectPaymentModelDao.getPaymentNumber(), curDirectPaymentModelDao.getExternalKey(), sortedTransactions);
+            }
+        });
+        return ImmutableList.copyOf(payments);
+    }
+
+    public DirectPayment getPayment(final UUID directPaymentId, final boolean withPluginInfo, final InternalTenantContext tenantContext) throws PaymentApiException {
+        return null;
+    }
+
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index f21c20d..b15f20c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -24,6 +24,9 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.skife.jdbi.v2.IDBI;
 
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -46,6 +49,7 @@ import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 
@@ -71,6 +75,82 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
+    public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(final DirectPaymentModelDao directPayment, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
+
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<DirectPaymentModelDao>() {
+
+            @Override
+            public DirectPaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final DirectPaymentSqlDao directPaymentSqlDao = entitySqlDaoWrapperFactory.become(DirectPaymentSqlDao.class);
+                directPaymentSqlDao.create(directPayment, context);
+                entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class).create(directPaymentTransaction, context);
+                return  directPaymentSqlDao.getById(directPayment.getId().toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus,
+                                                              final BigDecimal processedAmount, final Currency processedCurrency,
+                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg,  final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class).updateTransactionStatus(directTransactionId.toString(), paymentStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
+                return null;
+            }
+        });
+
+    }
+
+    @Override
+    public DirectPaymentModelDao getDirectPayment(final UUID directPaymentId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<DirectPaymentModelDao>() {
+            @Override
+            public DirectPaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(DirectPaymentSqlDao.class).getById(directPaymentId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public DirectPaymentTransactionModelDao getDirectPaymentTransaction(final UUID directTransactionId, final InternalTenantContext context) {
+        // getByAccountRecordId
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<DirectPaymentTransactionModelDao>() {
+            @Override
+            public DirectPaymentTransactionModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class).getById(directTransactionId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public List<DirectPaymentModelDao> getDirectPaymentsForAccount(final UUID accountId, final InternalTenantContext context) {
+        Preconditions.checkArgument(context.getAccountRecordId() != null);
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<DirectPaymentModelDao>>() {
+            @Override
+            public List<DirectPaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                List<DirectPaymentModelDao> payments = entitySqlDaoWrapperFactory.become(DirectPaymentSqlDao.class).getByAccountRecordId(context);
+                return payments;
+            }
+        });
+    }
+
+    @Override
+    public List<DirectPaymentTransactionModelDao> getDirectTransactionsForAccount(final UUID accountId, final InternalTenantContext context) {
+        Preconditions.checkArgument(context.getAccountRecordId() != null);
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<DirectPaymentTransactionModelDao>>() {
+            @Override
+            public List<DirectPaymentTransactionModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                List<DirectPaymentTransactionModelDao> transactions = entitySqlDaoWrapperFactory.become(DirectTransactionSqlDao.class).getByAccountRecordId(context);
+                return transactions;
+            }
+        });
+    }
+
+
+    @Override
     public PaymentModelDao insertPaymentWithFirstAttempt(final PaymentModelDao payment, final PaymentAttemptModelDao attempt, final InternalCallContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<PaymentModelDao>() {
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentModelDao.java
new file mode 100644
index 0000000..20a5d85
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentModelDao.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.payment.dao;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.util.dao.TableName;
+import org.killbill.billing.util.entity.dao.EntityModelDao;
+
+public class DirectPaymentModelDao extends EntityBase implements EntityModelDao<DirectPayment> {
+
+    public static final Integer INVALID_PAYMENT_NUMBER = new Integer(-17);
+
+    private UUID accountId;
+    private Integer paymentNumber;
+    private UUID paymentMethodId;
+    private String externalKey;
+
+    public DirectPaymentModelDao() { /* For the DAO mapper */ }
+
+    public DirectPaymentModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
+                                 final UUID paymentMethodId, final Integer paymentNumber, final String externalKey) {
+        super(id, createdDate, updatedDate);
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        this.paymentNumber = paymentNumber;
+        this.externalKey = externalKey;
+    }
+
+    public DirectPaymentModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
+                                 final UUID paymentMethodId, final String externalKey) {
+        this(UUID.randomUUID(), createdDate, updatedDate, accountId, paymentMethodId, INVALID_PAYMENT_NUMBER, externalKey);
+    }
+
+    public UUID getAccountId() { return accountId; }
+
+    public void setAccountId(final UUID accountId) {
+        this.accountId = accountId;
+    }
+
+    public Integer getPaymentNumber() {
+        return paymentNumber;
+    }
+
+    public void setPaymentNumber(final Integer paymentNumber) {
+        this.paymentNumber = paymentNumber;
+    }
+
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public void setPaymentMethodId(final UUID paymentMethodId) {
+        this.paymentMethodId = paymentMethodId;
+    }
+
+    public String getExternalKey() {
+        return externalKey;
+    }
+
+    public void setExternalKey(final String externalKey) {
+        this.externalKey = externalKey;
+    }
+
+    @Override
+    public String toString() {
+        return "DirectPaymentModelDao{" +
+               "accountId=" + accountId +
+               ", paymentNumber=" + paymentNumber +
+               ", paymentMethodId=" + paymentMethodId +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DirectPaymentModelDao)) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final DirectPaymentModelDao that = (DirectPaymentModelDao) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (paymentNumber != null ? !paymentNumber.equals(that.paymentNumber) : that.paymentNumber != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (paymentNumber != null ? paymentNumber.hashCode() : 0);
+        result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.DIRECT_PAYMENTS;
+    }
+
+    @Override
+    public TableName getHistoryTableName() {
+        return TableName.DIRECT_PAYMENT_HISTORY;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java
new file mode 100644
index 0000000..1714d4f
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentSqlDao.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.payment.dao;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.List;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.api.DirectPayment;
+import org.killbill.billing.payment.api.Refund;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.entity.dao.Audited;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.killbill.commons.jdbi.statement.SmartFetchSize;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+
+@EntitySqlDaoStringTemplate
+public interface DirectPaymentSqlDao extends EntitySqlDao<DirectPaymentModelDao, DirectPayment> {
+
+
+    /*
+    @SqlQuery
+    @SmartFetchSize(shouldStream = true)
+    public Iterator<DirectPaymentModelDao> getByPluginName(@Bind("pluginName") final String pluginName,
+                                                    @Bind("offset") final Long offset,
+                                                    @Bind("rowCount") final Long rowCount,
+                                                    @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Long getCountByPluginName(@Bind("pluginName") final String pluginName,
+                                     @BindBean final InternalTenantContext context);
+                                     */
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentTransactionModelDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentTransactionModelDao.java
new file mode 100644
index 0000000..cbdd328
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DirectPaymentTransactionModelDao.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * Groupon 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 org.killbill.billing.payment.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.payment.api.PaymentStatus;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.util.dao.TableName;
+import org.killbill.billing.util.entity.dao.EntityModelDao;
+
+public class DirectPaymentTransactionModelDao extends EntityBase implements EntityModelDao<DirectPaymentTransaction> {
+
+    private UUID directPaymentId;
+    private TransactionType transactionType;
+    private DateTime effectiveDate;
+    private PaymentStatus paymentStatus;
+    private BigDecimal amount;
+    private Currency currency;
+    private String gatewayErrorCode;
+    private String gatewayErrorMsg;
+
+    public DirectPaymentTransactionModelDao() { /* For the DAO mapper */ }
+
+    public DirectPaymentTransactionModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
+                                            final UUID directPaymentId, final TransactionType transactionType, final DateTime effectiveDate,
+                                            final PaymentStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
+        super(id, createdDate, updatedDate);
+        this.directPaymentId = directPaymentId;
+        this.transactionType = transactionType;
+        this.effectiveDate = effectiveDate;
+        this.paymentStatus = paymentStatus;
+        this.amount = amount;
+        this.currency = currency;
+        this.gatewayErrorCode = gatewayErrorCode;
+        this.gatewayErrorMsg = gatewayErrorMsg;
+    }
+
+    public DirectPaymentTransactionModelDao(@Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
+                                            final UUID directPaymentId, final TransactionType transactionType, final DateTime effectiveDate,
+                                            final PaymentStatus paymentStatus, final BigDecimal amount, final Currency currency, final String gatewayErrorCode, final String gatewayErrorMsg) {
+        this(UUID.randomUUID(), createdDate, updatedDate, directPaymentId, transactionType, effectiveDate, paymentStatus, amount, currency, gatewayErrorCode, gatewayErrorMsg);
+    }
+
+        public UUID getDirectPaymentId() {
+        return directPaymentId;
+    }
+
+    public void setDirectPaymentId(final UUID directPaymentId) {
+        this.directPaymentId = directPaymentId;
+    }
+
+    public TransactionType getTransactionType() {
+        return transactionType;
+    }
+
+    public void setTransactionType(final TransactionType transactionType) {
+        this.transactionType = transactionType;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public void setEffectiveDate(final DateTime effectiveDate) {
+        this.effectiveDate = effectiveDate;
+    }
+
+    public PaymentStatus getPaymentStatus() {
+        return paymentStatus;
+    }
+
+    public void setPaymentStatus(final PaymentStatus paymentStatus) {
+        this.paymentStatus = paymentStatus;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public void setAmount(final BigDecimal amount) {
+        this.amount = amount;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public void setCurrency(final Currency currency) {
+        this.currency = currency;
+    }
+
+    public String getGatewayErrorCode() {
+        return gatewayErrorCode;
+    }
+
+    public void setGatewayErrorCode(final String gatewayErrorCode) {
+        this.gatewayErrorCode = gatewayErrorCode;
+    }
+
+    public String getGatewayErrorMsg() {
+        return gatewayErrorMsg;
+    }
+
+    public void setGatewayErrorMsg(final String gatewayErrorMsg) {
+        this.gatewayErrorMsg = gatewayErrorMsg;
+    }
+
+    @Override
+    public String toString() {
+        return "DirectPaymentTransactionModelDao{" +
+               "directPaymentId=" + directPaymentId +
+               ", transactionType=" + transactionType +
+               ", effectiveDate=" + effectiveDate +
+               ", paymentStatus=" + paymentStatus +
+               ", amount=" + amount +
+               ", currency=" + currency +
+               ", gatewayErrorCode='" + gatewayErrorCode + '\'' +
+               ", gatewayErrorMsg='" + gatewayErrorMsg + '\'' +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DirectPaymentTransactionModelDao)) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final DirectPaymentTransactionModelDao that = (DirectPaymentTransactionModelDao) o;
+
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (directPaymentId != null ? !directPaymentId.equals(that.directPaymentId) : that.directPaymentId != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
+        if (gatewayErrorCode != null ? !gatewayErrorCode.equals(that.gatewayErrorCode) : that.gatewayErrorCode != null) {
+            return false;
+        }
+        if (gatewayErrorMsg != null ? !gatewayErrorMsg.equals(that.gatewayErrorMsg) : that.gatewayErrorMsg != null) {
+            return false;
+        }
+        if (paymentStatus != that.paymentStatus) {
+            return false;
+        }
+        if (transactionType != that.transactionType) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (directPaymentId != null ? directPaymentId.hashCode() : 0);
+        result = 31 * result + (transactionType != null ? transactionType.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (paymentStatus != null ? paymentStatus.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
+        result = 31 * result + (gatewayErrorMsg != null ? gatewayErrorMsg.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.DIRECT_TRANSACTIONS;
+    }
+
+    @Override
+    public TableName getHistoryTableName() {
+        return TableName.DIRECT_TRANSACTION_HISTORY;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DirectTransactionSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DirectTransactionSqlDao.java
new file mode 100644
index 0000000..0c2f5a1
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DirectTransactionSqlDao.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ *
+ * The Billing Project 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 org.killbill.billing.payment.dao;
+
+import java.util.List;
+
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.payment.api.DirectPaymentTransaction;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.entity.dao.Audited;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+
+@EntitySqlDaoStringTemplate
+public interface DirectTransactionSqlDao extends EntitySqlDao<DirectPaymentTransactionModelDao, DirectPaymentTransaction> {
+
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    void updateTransactionStatus(@Bind("id") final String transactionId,
+                                    @Bind("paymentStatus") final String paymentStatus,
+                                    @Bind("gatewayErrorCode") final String gatewayErrorCode,
+                                    @Bind("gatewayErrorMsg") final String gatewayErrorMsg,
+                                    @BindBean final InternalCallContext context);
+
+    /*
+    @SqlQuery
+    @SmartFetchSize(shouldStream = true)
+    public Iterator<DirectPaymentTransactionModelDao> getByPluginName(@Bind("pluginName") final String pluginName,
+                                                    @Bind("offset") final Long offset,
+                                                    @Bind("rowCount") final Long rowCount,
+                                                    @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Long getCountByPluginName(@Bind("pluginName") final String pluginName,
+                                     @BindBean final InternalTenantContext context);
+                                     */
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index 7649fc0..12606f9 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -29,6 +29,20 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface PaymentDao {
 
+    public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(DirectPaymentModelDao directPayment, DirectPaymentTransactionModelDao directPaymentTransaction, InternalCallContext context);
+
+    public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus,
+                                                              final BigDecimal processedAmount, final Currency processedCurrency,
+                                                              final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg,  final InternalCallContext context);
+
+    public DirectPaymentModelDao getDirectPayment(UUID directPaymentId, InternalTenantContext context);
+
+    public DirectPaymentTransactionModelDao getDirectPaymentTransaction(UUID directTransactionId, InternalTenantContext context);
+
+    public List<DirectPaymentModelDao> getDirectPaymentsForAccount(UUID accountId, InternalTenantContext context);
+
+    public List<DirectPaymentTransactionModelDao> getDirectTransactionsForAccount(final UUID accountId, final InternalTenantContext context);
+
     public PaymentModelDao insertPaymentWithFirstAttempt(PaymentModelDao paymentInfo, PaymentAttemptModelDao attempt, InternalCallContext context);
 
     public PaymentAttemptModelDao updatePaymentWithNewAttempt(UUID paymentId, PaymentAttemptModelDao attempt, InternalCallContext context);
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index a221bbd..397927f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -20,6 +20,9 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 
+import org.killbill.billing.payment.api.DirectPaymentApi;
+import org.killbill.billing.payment.api.svcs.DefaultDirectPaymentApi;
+import org.killbill.billing.payment.core.DirectPaymentProcessor;
 import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
@@ -89,6 +92,7 @@ public class PaymentModule extends AbstractModule {
         });
         bind(ExecutorService.class).annotatedWith(Names.named(PLUGIN_EXECUTOR_NAMED)).toInstance(pluginExecutorService);
         bind(PaymentProcessor.class).asEagerSingleton();
+        bind(DirectPaymentProcessor.class).asEagerSingleton();
         bind(RefundProcessor.class).asEagerSingleton();
         bind(PaymentMethodProcessor.class).asEagerSingleton();
     }
@@ -103,6 +107,7 @@ public class PaymentModule extends AbstractModule {
 
         bind(PaymentInternalApi.class).to(DefaultPaymentInternalApi.class).asEagerSingleton();
         bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
+        bind(DirectPaymentApi.class).to(DefaultDirectPaymentApi.class).asEagerSingleton();
         bind(InvoiceHandler.class).asEagerSingleton();
         bind(PaymentTagHandler.class).asEagerSingleton();
         bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index fd1deed..5cdebfe 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -25,6 +25,9 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.killbill.clock.Clock;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.plugin.api.NoOpPaymentPluginApi;
@@ -90,15 +93,26 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public PaymentInfoPlugin authorizePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getInternalNoopPaymentInfoResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getInternalNoopPaymentInfoResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
-        if (makeNextInvoiceFailWithException.getAndSet(false)) {
-            throw new PaymentPluginApiException("", "test error");
-        }
+        return getInternalNoopPaymentInfoResult(kbPaymentId, amount, currency);
+    }
 
-        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
-        payments.put(kbPaymentId.toString(), result);
-        return result;
+    @Override
+    public PaymentInfoPlugin voidPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, CallContext context)
+            throws PaymentPluginApiException {
+        return getInternalNoopPaymentInfoResult(kbPaymentId, BigDecimal.ZERO, null);
     }
 
     @Override
@@ -213,6 +227,16 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
         final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbAccountId, kbPaymentId, context);
         if (paymentInfoPlugin == null) {
@@ -260,4 +284,16 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
 
         return new DefaultPagination<RefundInfoPlugin>(offset, limit, (long) results.size(), (long) refunds.values().size(), results.iterator());
     }
+
+    private PaymentInfoPlugin getInternalNoopPaymentInfoResult(final UUID kbPaymentId, final BigDecimal amount, final Currency currency) throws PaymentPluginApiException {
+        if (makeNextInvoiceFailWithException.getAndSet(false)) {
+            throw new PaymentPluginApiException("", "test error");
+        }
+
+        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        payments.put(kbPaymentId.toString(), result);
+        return result;
+    }
+
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 6c5579a..dc5a304 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -22,6 +22,9 @@ import java.util.List;
 import java.util.UUID;
 
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.killbill.clock.Clock;
 import org.killbill.billing.payment.api.PaymentMethodKVInfo;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
@@ -59,11 +62,26 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     }
 
     @Override
+    public PaymentInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext callContext) throws PaymentPluginApiException {
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+    }
+
+    @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(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
     }
 
     @Override
+    public PaymentInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final CallContext callContext) throws PaymentPluginApiException {
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, BigDecimal.ZERO, null, 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(kbPaymentId, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
     }
@@ -118,4 +136,14 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     @Override
     public void resetPaymentMethods(final UUID kbAccountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
     }
+
+    @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
 }
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg
new file mode 100644
index 0000000..1845370
--- /dev/null
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectPaymentSqlDao.sql.stg
@@ -0,0 +1,35 @@
+group DirectPaymentSqlDao: EntitySqlDao;
+
+tableName() ::= "direct_payments"
+
+historyTableName() ::= "direct_payment_history"
+
+extraTableFieldsWithComma(prefix) ::= <<
+, <prefix>record_id as payment_number
+>>
+
+defaultOrderBy(prefix) ::= <<
+order by <prefix>created_date ASC, <recordIdField(prefix)> ASC
+>>
+
+tableFields(prefix) ::= <<
+  <prefix>account_id
+, <prefix>payment_method_id
+, <prefix>external_key
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
+>>
+
+tableValues() ::= <<
+:accountId
+, :paymentMethodId
+, :externalKey
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/DirectTransactionSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectTransactionSqlDao.sql.stg
new file mode 100644
index 0000000..0a5f8b6
--- /dev/null
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/DirectTransactionSqlDao.sql.stg
@@ -0,0 +1,55 @@
+group DirectTransactionSqlDao: EntitySqlDao;
+
+tableName() ::= "direct_transactions"
+
+historyTableName() ::= "direct_transaction_history"
+
+defaultOrderBy(prefix) ::= <<
+order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
+>>
+
+
+tableFields(prefix) ::= <<
+  <prefix>transaction_type
+, <prefix>effective_date
+, <prefix>payment_status
+, <prefix>amount
+, <prefix>currency
+, <prefix>direct_payment_id
+, <prefix>gateway_error_code
+, <prefix>gateway_error_msg
+, <prefix>created_by
+, <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
+>>
+
+tableValues() ::= <<
+  :transactionType
+, :effectiveDate
+, :paymentStatus
+, :amount
+, :currency
+, :directPaymentId
+, :gatewayErrorCode
+, :gatewayErrorMsg
+, :createdBy
+, :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+
+updateTransactionStatus() ::= <<
+update <tableName()>
+set payment_status = :paymentStatus
+, gateway_error_code = :gatewayErrorCode
+, gateway_error_msg = :gatewayErrorMsg
+, updated_by = :updatedBy
+, updated_date = :createdDate
+where id = :id
+<AND_CHECK_TENANT()>
+;
+>>
+
+
diff --git a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
index cbeda72..de818a2 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
+++ b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
@@ -191,6 +191,93 @@ CREATE INDEX refund_history_target_record_id ON refund_history(target_record_id)
 CREATE INDEX refund_history_tenant_account_record_id ON refund_history(tenant_record_id, account_record_id);
 
 
+DROP TABLE IF EXISTS direct_payments;
+CREATE TABLE direct_payments (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    account_id char(36) NOT NULL,
+    payment_method_id char(36) NOT NULL,
+    external_key varchar(255),
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY (record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX direct_payments_id ON direct_payments(id);
+CREATE UNIQUE INDEX direct_payments_key ON direct_payments(external_key);
+CREATE INDEX direct_payments_accnt ON direct_payments(account_id);
+CREATE INDEX direct_payments_tenant_account_record_id ON direct_payments(tenant_record_id, account_record_id);
+
+
+DROP TABLE IF EXISTS direct_payment_history;
+CREATE TABLE direct_payment_history (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
+    account_id char(36) NOT NULL,
+    payment_method_id char(36) NOT NULL,
+    external_key varchar(255),
+    change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE INDEX direct_payment_history_target_record_id ON direct_payment_history(target_record_id);
+CREATE INDEX direct_payment_history_tenant_account_record_id ON direct_payment_history(tenant_record_id, account_record_id);
 
 
+DROP TABLE IF EXISTS direct_transactions;
+CREATE TABLE direct_transactions (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    transaction_type varchar(32) NOT NULL,
+    effective_date datetime NOT NULL,
+    payment_status varchar(50),
+    amount numeric(15,9),
+    currency char(3),
+    direct_payment_id char(36) NOT NULL,
+    gateway_error_code varchar(32),
+    gateway_error_msg varchar(256),
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY (record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX direct_transactions_id ON direct_transactions(id);
+CREATE INDEX direct_transactions_direct_id ON direct_transactions(direct_payment_id);
+CREATE INDEX direct_transactions_tenant_account_record_id ON direct_transactions(tenant_record_id, account_record_id);
 
+DROP TABLE IF EXISTS direct_transaction_history;
+CREATE TABLE direct_transaction_history (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    id char(36) NOT NULL,
+    target_record_id int(11) unsigned NOT NULL,
+    transaction_type varchar(32) NOT NULL,
+    effective_date datetime NOT NULL,
+    payment_status varchar(50),
+    amount numeric(15,9),
+    currency char(3),
+    direct_payment_id char(36) NOT NULL,
+    gateway_error_code varchar(32),
+    gateway_error_msg varchar(256),
+    change_type char(6) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
+    account_record_id int(11) unsigned default null,
+    tenant_record_id int(11) unsigned default null,
+    PRIMARY KEY (record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE INDEX direct_transaction_history_target_record_id ON direct_transaction_history(target_record_id);
+CREATE INDEX direct_transaction_history_tenant_account_record_id ON direct_transaction_history(tenant_record_id, account_record_id);
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index 892bc51..60282d0 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -41,6 +41,36 @@ public class MockPaymentDao implements PaymentDao {
     private final Map<UUID, PaymentAttemptModelDao> attempts = new HashMap<UUID, PaymentAttemptModelDao>();
 
     @Override
+    public DirectPaymentModelDao insertDirectPaymentWithFirstTransaction(final DirectPaymentModelDao directPayment, final DirectPaymentTransactionModelDao directPaymentTransaction, final InternalCallContext context) {
+        return null;
+    }
+
+    @Override
+    public void updateDirectPaymentAndTransactionOnCompletion(final UUID directPaymentId, final PaymentStatus paymentStatus, final BigDecimal processedAmount, final Currency processedCurrency, final UUID directTransactionId, final String gatewayErrorCode, final String gatewayErrorMsg, final InternalCallContext context) {
+
+    }
+
+    @Override
+    public DirectPaymentModelDao getDirectPayment(final UUID directPaymentId, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
+    public DirectPaymentTransactionModelDao getDirectPaymentTransaction(final UUID directTransactionId, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
+    public List<DirectPaymentModelDao> getDirectPaymentsForAccount(final UUID accountId, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
+    public List<DirectPaymentTransactionModelDao> getDirectTransactionsForAccount(final UUID accountId, final InternalTenantContext context) {
+        return null;
+    }
+
+    @Override
     public PaymentModelDao insertPaymentWithFirstAttempt(final PaymentModelDao paymentInfo, final PaymentAttemptModelDao attempt,
                                                          final InternalCallContext context) {
         synchronized (this) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index d12107b..b23b434 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -17,11 +17,11 @@
 package org.killbill.billing.payment.dao;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.payment.api.TransactionType;
 import org.testng.annotations.Test;
 
 import org.killbill.billing.catalog.api.Currency;
@@ -31,6 +31,7 @@ import org.killbill.billing.payment.api.RefundStatus;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.fail;
@@ -284,4 +285,67 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(deletedPaymentMethod.getId(), paymentMethodId);
         assertEquals(deletedPaymentMethod.getPluginName(), pluginName);
     }
+
+    @Test(groups = "slow")
+    public void testDirectPayment() {
+
+        final UUID paymentMethodId = UUID.randomUUID();
+        final UUID accountId = UUID.randomUUID();
+        final String externalName = "fo0";
+
+        DateTime utcNow = clock.getUTCNow();
+        final DirectPaymentModelDao dpmd = new DirectPaymentModelDao(utcNow, utcNow, accountId, paymentMethodId, externalName);
+        final DirectPaymentTransactionModelDao dptmd = new DirectPaymentTransactionModelDao(utcNow, utcNow, dpmd.getId(), TransactionType.AUTHORIZE,
+                                                                                            utcNow, PaymentStatus.UNKNOWN, BigDecimal.TEN, Currency.USD, null, null);
+        DirectPaymentModelDao savedDirectPayment = paymentDao.insertDirectPaymentWithFirstTransaction(dpmd, dptmd, internalCallContext);
+        assertNotNull(savedDirectPayment);
+        assertEquals(savedDirectPayment.getAccountId(), accountId);
+        assertEquals(savedDirectPayment.getPaymentMethodId(), paymentMethodId);
+        assertEquals(savedDirectPayment.getExternalKey(), externalName);
+
+        savedDirectPayment = paymentDao.getDirectPayment(dpmd.getId(), internalCallContext);
+        assertNotNull(savedDirectPayment);
+        assertNotNull(savedDirectPayment.getPaymentNumber());
+        assertEquals(savedDirectPayment.getAccountId(), accountId);
+        assertEquals(savedDirectPayment.getPaymentMethodId(), paymentMethodId);
+        assertEquals(savedDirectPayment.getExternalKey(), externalName);
+
+        DirectPaymentTransactionModelDao savedTransaction = paymentDao.getDirectPaymentTransaction(dptmd.getId(), internalCallContext);
+        assertNotNull(savedTransaction);
+        assertEquals(savedTransaction.getDirectPaymentId(), dpmd.getId());
+        assertEquals(savedTransaction.getTransactionType(), TransactionType.AUTHORIZE);
+        assertEquals(savedTransaction.getAmount().compareTo(BigDecimal.TEN), 0);
+        assertEquals(savedTransaction.getEffectiveDate().compareTo(utcNow), 0);
+        assertEquals(savedTransaction.getPaymentStatus(), PaymentStatus.UNKNOWN);
+        assertEquals(savedTransaction.getCurrency(), Currency.USD);
+        assertNull(savedTransaction.getGatewayErrorCode());
+        assertNull(savedTransaction.getGatewayErrorMsg());
+
+        paymentDao.updateDirectPaymentAndTransactionOnCompletion(dpmd.getId(), PaymentStatus.SUCCESS, BigDecimal.TEN, Currency.USD, dptmd.getId(), "100", "Excellent", internalCallContext);
+
+
+        savedDirectPayment = paymentDao.getDirectPayment(dpmd.getId(), internalCallContext);
+        assertNotNull(savedDirectPayment);
+        assertEquals(savedDirectPayment.getAccountId(), accountId);
+        assertEquals(savedDirectPayment.getPaymentMethodId(), paymentMethodId);
+        assertEquals(savedDirectPayment.getExternalKey(), externalName);
+
+        savedTransaction = paymentDao.getDirectPaymentTransaction(dptmd.getId(), internalCallContext);
+        assertNotNull(savedTransaction);
+        assertEquals(savedTransaction.getDirectPaymentId(), dpmd.getId());
+        assertEquals(savedTransaction.getTransactionType(), TransactionType.AUTHORIZE);
+        assertEquals(savedTransaction.getAmount().compareTo(BigDecimal.TEN), 0);
+        assertEquals(savedTransaction.getEffectiveDate().compareTo(utcNow), 0);
+        assertEquals(savedTransaction.getPaymentStatus(), PaymentStatus.SUCCESS);
+        assertEquals(savedTransaction.getCurrency(), Currency.USD);
+        assertEquals(savedTransaction.getGatewayErrorCode(), "100");
+        assertEquals(savedTransaction.getGatewayErrorMsg(), "Excellent");
+
+        List<DirectPaymentModelDao> perAccountPayments = paymentDao.getDirectPaymentsForAccount(accountId, internalCallContext);
+        assertEquals(perAccountPayments.size(), 1);
+
+        List<DirectPaymentTransactionModelDao> perAccountTransactions = paymentDao.getDirectTransactionsForAccount(accountId, internalCallContext);
+        assertEquals(perAccountTransactions.size(), 1);
+
+    }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index 5b98fc5..e2341ac 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -25,9 +25,11 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.clock.Clock;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.TestPaymentMethodPlugin;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageDescriptorFields;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
+import org.killbill.billing.payment.plugin.api.HostedPaymentPageNotification;
 import org.killbill.billing.payment.plugin.api.NoOpPaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentMethodInfoPlugin;
@@ -39,6 +41,7 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
+import org.killbill.clock.Clock;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
@@ -96,15 +99,26 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public PaymentInfoPlugin authorizePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
+    public PaymentInfoPlugin capturePayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, Currency currency, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
+
+    @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
-        if (makeNextInvoiceFailWithException.getAndSet(false)) {
-            throw new PaymentPluginApiException("", "test error");
-        }
+        return getPaymentInfoPluginResult(kbPaymentId, amount, currency);
+    }
 
-        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
-        payments.put(kbPaymentId.toString(), result);
-        return result;
+    @Override
+    public PaymentInfoPlugin voidPayment(UUID kbAccountId, UUID kbPaymentId, UUID kbPaymentMethodId, CallContext context)
+            throws PaymentPluginApiException {
+        return getPaymentInfoPluginResult(kbPaymentId, BigDecimal.ZERO, null);
     }
 
     @Override
@@ -187,6 +201,16 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID uuid, final HostedPaymentPageDescriptorFields hostedPaymentPageDescriptorFields, final TenantContext tenantContext) {
+        return null;
+    }
+
+    @Override
+    public HostedPaymentPageNotification processNotification(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
         final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbAccountId, kbPaymentId, context);
         if (paymentInfoPlugin == null) {
@@ -225,4 +249,15 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         }));
         return DefaultPagination.<RefundInfoPlugin>build(offset, limit, results);
     }
+
+    private PaymentInfoPlugin getPaymentInfoPluginResult(final UUID kbPaymentId, final BigDecimal amount, final Currency currency) throws PaymentPluginApiException {
+        if (makeNextInvoiceFailWithException.getAndSet(false)) {
+            throw new PaymentPluginApiException("", "test error");
+        }
+
+        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        payments.put(kbPaymentId.toString(), result);
+        return result;
+    }
 }

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 6cf7f98..610a1c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.5.33</version>
+        <version>0.6.34-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.10.3-SNAPSHOT</version>
diff --git a/util/src/main/java/org/killbill/billing/util/dao/TableName.java b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
index 9157f35..4e00bb2 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/TableName.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
@@ -41,6 +41,10 @@ public enum TableName {
     PAYMENTS("payments", ObjectType.PAYMENT, PAYMENT_HISTORY),
     PAYMENT_METHOD_HISTORY("payment_method_history"),
     PAYMENT_METHODS("payment_methods", ObjectType.PAYMENT_METHOD, PAYMENT_METHOD_HISTORY),
+    DIRECT_PAYMENT_HISTORY("direct_payment_history"),
+    DIRECT_PAYMENTS("direct_payments", ObjectType.DIRECT_PAYMENT, DIRECT_PAYMENT_HISTORY),
+    DIRECT_TRANSACTION_HISTORY("direct_transaction_history"),
+    DIRECT_TRANSACTIONS("direct_transactions", ObjectType.DIRECT_TRANSACTION, DIRECT_TRANSACTION_HISTORY),
     SUBSCRIPTIONS("subscriptions", ObjectType.SUBSCRIPTION),
     SUBSCRIPTION_EVENTS("subscription_events", ObjectType.SUBSCRIPTION_EVENT),
     REFUND_HISTORY("refund_history"),