killbill-uncached

jaxrs: add endpoint to trigger invoice notification This

6/14/2012 6:32:15 PM

Details

diff --git a/api/src/main/java/com/ning/billing/config/InvoiceConfig.java b/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
index 407f4d3..02f7d6e 100644
--- a/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
+++ b/api/src/main/java/com/ning/billing/config/InvoiceConfig.java
@@ -19,9 +19,8 @@ package com.ning.billing.config;
 import org.skife.config.Config;
 import org.skife.config.Default;
 
-public interface InvoiceConfig extends NotificationConfig, KillbillConfig  {
-
-    @Override    
+public interface InvoiceConfig extends NotificationConfig, KillbillConfig {
+    @Override
     @Config("killbill.invoice.engine.notifications.sleep")
     @Default("500")
     public long getSleepTimeMs();
@@ -34,4 +33,8 @@ public interface InvoiceConfig extends NotificationConfig, KillbillConfig  {
     @Config("killbill.invoice.maxNumberOfMonthsInFuture")
     @Default("36")
     public int getNumberOfMonthsInFuture();
+
+    @Config("killbill.invoice.emailNotificationsEnabled")
+    @Default("false")
+    public boolean isEmailNotificationsEnabled();
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
index b0fa991..bacd1cb 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
@@ -40,6 +40,7 @@ import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.notification.DefaultNextBillingDateNotifier;
 import com.ning.billing.invoice.notification.DefaultNextBillingDatePoster;
+import com.ning.billing.invoice.notification.EmailInvoiceNotifier;
 import com.ning.billing.invoice.notification.NextBillingDateNotifier;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
@@ -47,36 +48,29 @@ import com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFacto
 import com.ning.billing.util.template.translation.TranslatorConfig;
 
 public class DefaultInvoiceModule extends AbstractModule implements InvoiceModule {
+    InvoiceConfig config;
+
     protected void installInvoiceDao() {
         bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
     }
 
-    /* (non-Javadoc)
-     * @see com.ning.billing.invoice.glue.InvoiceModule#installInvoiceUserApi()
-     */
     @Override
     public void installInvoiceUserApi() {
         bind(InvoiceUserApi.class).to(DefaultInvoiceUserApi.class).asEagerSingleton();
     }
 
-    /* (non-Javadoc)
-    * @see com.ning.billing.invoice.glue.InvoiceModule#installInvoiceUserApi()
-    */
     @Override
     public void installInvoiceTestApi() {
         bind(InvoiceTestApi.class).to(DefaultInvoiceTestApi.class).asEagerSingleton();
     }
 
-    /* (non-Javadoc)
-     * @see com.ning.billing.invoice.glue.InvoiceModule#installInvoicePaymentApi()
-     */
     @Override
     public void installInvoicePaymentApi() {
         bind(InvoicePaymentApi.class).to(DefaultInvoicePaymentApi.class).asEagerSingleton();
     }
 
     protected void installConfig() {
-        final InvoiceConfig config = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
+        config = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
         bind(InvoiceConfig.class).toInstance(config);
     }
 
@@ -84,9 +78,6 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
         bind(InvoiceService.class).to(DefaultInvoiceService.class).asEagerSingleton();
     }
 
-    /* (non-Javadoc)
-    * @see com.ning.billing.invoice.glue.InvoiceModule#installInvoiceMigrationApi()
-    */
     @Override
     public void installInvoiceMigrationApi() {
         bind(InvoiceMigrationApi.class).to(DefaultInvoiceMigrationApi.class).asEagerSingleton();
@@ -98,7 +89,14 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
         final TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
         bind(TranslatorConfig.class).toInstance(config);
         bind(InvoiceFormatterFactory.class).to(DefaultInvoiceFormatterFactory.class).asEagerSingleton();
-        bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
+    }
+
+    protected void installInvoiceNotifier() {
+        if (config.isEmailNotificationsEnabled()) {
+            bind(InvoiceNotifier.class).to(EmailInvoiceNotifier.class).asEagerSingleton();
+        } else {
+            bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
+        }
     }
 
     protected void installInvoiceListener() {
@@ -111,8 +109,10 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
 
     @Override
     protected void configure() {
-        installInvoiceService();
         installConfig();
+
+        installInvoiceService();
+        installInvoiceNotifier();
         installNotifiers();
         installInvoiceListener();
         installInvoiceGenerator();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index b0718db..f7aa751 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -80,6 +80,11 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
         public int getNumberOfMonthsInFuture() {
             return 36;
         }
+
+        @Override
+        public boolean isEmailNotificationsEnabled() {
+            return false;
+        }
     };
 
     @BeforeClass(alwaysRun = true)
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index 7441173..fe953c0 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -85,6 +85,11 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
             }
 
             @Override
+            public boolean isEmailNotificationsEnabled() {
+                return false;
+            }
+
+            @Override
             public boolean isNotificationProcessingOff() {
                 throw new UnsupportedOperationException();
             }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 85f8831..3cce218 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -44,6 +44,7 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
@@ -75,6 +76,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     private final PaymentApi paymentApi;
     private final Context context;
     private final JaxrsUriBuilder uriBuilder;
+    private final InvoiceNotifier invoiceNotifier;
 
     @Inject
     public InvoiceResource(final AccountUserApi accountApi,
@@ -84,20 +86,21 @@ public class InvoiceResource extends JaxRsResourceBase {
                            final JaxrsUriBuilder uriBuilder,
                            final TagUserApi tagUserApi,
                            final TagHelper tagHelper,
-                           final CustomFieldUserApi customFieldUserApi) {
+                           final CustomFieldUserApi customFieldUserApi,
+                           final InvoiceNotifier invoiceNotifier) {
         super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
         this.accountApi = accountApi;
         this.invoiceApi = invoiceApi;
         this.paymentApi = paymentApi;
         this.context = context;
         this.uriBuilder = uriBuilder;
+        this.invoiceNotifier = invoiceNotifier;
     }
 
     @GET
     @Produces(APPLICATION_JSON)
     public Response getInvoices(@QueryParam(QUERY_ACCOUNT_ID) final String accountId) {
         try {
-
             Preconditions.checkNotNull(accountId, "% query parameter must be specified", QUERY_ACCOUNT_ID);
             accountApi.getAccountById(UUID.fromString(accountId));
             final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(UUID.fromString(accountId));
@@ -114,7 +117,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     }
 
     @GET
-    @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}")
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
     public Response getInvoice(@PathParam("invoiceId") final String invoiceId, @QueryParam("withItems") @DefaultValue("false") final boolean withItems) {
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId));
@@ -137,7 +140,6 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         @HeaderParam(HDR_REASON) final String reason,
                                         @HeaderParam(HDR_COMMENT) final String comment) {
         try {
-
             Preconditions.checkNotNull(accountId, "% needs to be specified", QUERY_ACCOUNT_ID);
             Preconditions.checkNotNull(targetDate, "% needs to be specified", QUERY_TARGET_DATE);
 
@@ -161,7 +163,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     }
 
     @GET
-    @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}/" + PAYMENTS)
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
     public Response getPayments(@PathParam("invoiceId") final String invoiceId) {
         try {
@@ -179,7 +181,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
-    @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}/" + PAYMENTS)
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + PAYMENTS)
     public Response createInstantPayment(final PaymentJsonSimple payment,
                                          @QueryParam(QUERY_PAYMENT_EXTERNAL) @DefaultValue("false") final Boolean externalPayment,
                                          @HeaderParam(HDR_CREATED_BY) final String createdBy,
@@ -200,6 +202,34 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
+    @POST
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response triggerEmailNotificationForInvoice(@PathParam("invoiceId") final String invoiceId,
+                                                       @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                                       @HeaderParam(HDR_REASON) final String reason,
+                                                       @HeaderParam(HDR_COMMENT) final String comment) {
+        final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId));
+        if (invoice == null) {
+            return Response.status(Status.NOT_FOUND).build();
+        }
+
+        try {
+            final Account account = accountApi.getAccountById(invoice.getAccountId());
+
+            // Send the email (synchronous send)
+            invoiceNotifier.notify(account, invoice);
+
+            return Response.status(Status.OK).build();
+        } catch (AccountApiException e) {
+            return Response.status(Status.NOT_FOUND).build();
+        } catch (InvoiceApiException e) {
+            // Sending failed
+            return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
+        }
+    }
+
     @GET
     @Path(CUSTOM_FIELD_URI)
     @Produces(APPLICATION_JSON)
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoiceNotification.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoiceNotification.java
new file mode 100644
index 0000000..53d4845
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoiceNotification.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.jaxrs;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
+import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
+import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.http.client.Response;
+
+public class TestInvoiceNotification extends TestJaxrsBase {
+    @Test(groups = "slow")
+    public void testTriggerNotification() throws Exception {
+        final AccountJson accountJson = createScenarioWithOneInvoice();
+
+        final String uri = JaxrsResource.INVOICES_PATH;
+        final Response response = doGet(uri, ImmutableMap.<String, String>of(JaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId()), DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+        final String baseJson = response.getResponseBody();
+        final List<InvoiceJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<InvoiceJsonSimple>>() {});
+        Assert.assertEquals(objFromJson.size(), 1);
+
+        final InvoiceJsonSimple invoice = objFromJson.get(0);
+        final Response triggerResponse = doPost(uri + "/" + invoice.getInvoiceId() + "/" + JaxrsResource.EMAIL_NOTIFICATIONS,
+                                                null, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(triggerResponse.getStatusCode(), javax.ws.rs.core.Response.Status.OK.getStatusCode());
+    }
+
+    private AccountJson createScenarioWithOneInvoice() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final AccountJson accountJson = createAccount(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        Assert.assertNotNull(accountJson);
+
+        final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), UUID.randomUUID().toString());
+        Assert.assertNotNull(bundleJson);
+
+        final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
+        Assert.assertNotNull(subscriptionJson);
+
+        return accountJson;
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 577c58f..4c4c0c9 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -18,6 +18,7 @@ package com.ning.billing.jaxrs;
 import static org.testng.Assert.assertNotNull;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -30,9 +31,13 @@ import java.util.Map.Entry;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.Nullable;
 import javax.ws.rs.core.Response.Status;
 
 import com.google.common.io.Resources;
+import com.ning.billing.invoice.api.InvoiceNotifier;
+import com.ning.billing.invoice.notification.EmailInvoiceNotifier;
+import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
@@ -44,6 +49,7 @@ import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -109,13 +115,12 @@ public class TestJaxrsBase {
     public static final String CONTENT_TYPE = "application/json";
 
     private static TestKillbillGuiceListener listener;
-    
-    
+
     private MysqlTestingHelper helper;
     private HttpServer server;
 
     protected CoreConfig config;
-    protected AsyncHttpClient httpClient;	
+    protected AsyncHttpClient httpClient;
     protected ObjectMapper mapper;
     protected ClockMock clock;
     protected TestApiListener busHandler;
@@ -123,8 +128,8 @@ public class TestJaxrsBase {
     // Context information to be passed around
     private static final String createdBy = "Toto";
     private static final String reason = "i am god";
-    private static final String comment = "no comment";    
-    
+    private static final String comment = "no comment";
+
     public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestJaxrsBase.class.getResource(resource);
         assertNotNull(url);
@@ -145,10 +150,10 @@ public class TestJaxrsBase {
     }
 
     public static class TestKillbillGuiceListener extends KillbillGuiceListener {
-        
+
         private final MysqlTestingHelper helper;
         private final Clock clock;
-        
+
         public TestKillbillGuiceListener(final MysqlTestingHelper helper, final Clock clock) {
             super();
             this.helper = helper;
@@ -158,7 +163,7 @@ public class TestJaxrsBase {
         protected Module getModule() {
             return new TestKillbillServerModule(helper, clock);
         }
-        
+
         //
         // Listener is created once before Suite and keeps pointer to helper and clock so they can get
         // reset for each test Class-- needed in order to ONLY start Jetty once across all the test classes
@@ -167,23 +172,30 @@ public class TestJaxrsBase {
         public MysqlTestingHelper getMysqlTestingHelper() {
             return helper;
         }
-        
+
         public Clock getClock() {
             return clock;
         }
     }
 
+    public static class InvoiceModuleWithMockSender extends DefaultInvoiceModule {
+        @Override
+        protected void installInvoiceNotifier() {
+            bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
+        }
+    }
+
     public static class TestKillbillServerModule extends KillbillServerModule {
 
         private final MysqlTestingHelper helper;
         private final Clock clock;
-        
+
         public TestKillbillServerModule(final MysqlTestingHelper helper, final Clock clock) {
             super();
             this.helper = helper;
             this.clock = clock;
         }
-        
+
         @Override
         protected void installClock() {
             bind(Clock.class).toInstance(clock);
@@ -214,7 +226,7 @@ public class TestJaxrsBase {
             install(new NotificationQueueModule());
             install(new CallContextModule());
             install(new AccountModule());
-            install(new DefaultInvoiceModule());
+            install(new InvoiceModuleWithMockSender());
             install(new TemplateModule());
             install(new DefaultEntitlementModule());
             install(new AnalyticsModule());
@@ -238,12 +250,23 @@ public class TestJaxrsBase {
         }
     }
 
-    @BeforeMethod(groups="slow")
-    public void cleanupBeforeMethod() {
+    @BeforeMethod(groups = "slow")
+    public void cleanupBeforeMethod(final Method method) {
+        log.info("***************************************************************************************************");
+        log.info("*** Starting test {}:{}", method.getDeclaringClass().getName(), method.getName());
+        log.info("***************************************************************************************************");
+
         busHandler.reset();
         helper.cleanupAllTables();
     }
 
+    @AfterMethod(groups = "slow")
+    public void endTest(final Method method) throws Exception {
+        log.info("***************************************************************************************************");
+        log.info("***   Ending test {}:{}", method.getDeclaringClass().getName(), method.getName());
+        log.info("***************************************************************************************************");
+    }
+
     @BeforeClass(groups="slow")
     public void setupClass() throws IOException {
         loadConfig();
@@ -456,7 +479,7 @@ public class TestJaxrsBase {
     //
     // HTTP CLIENT HELPERS
     //
-    protected Response doPost(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
+    protected Response doPost(final String uri, @Nullable final String body, final Map<String, String> queryParams, final int timeoutSec) {
         BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("POST", getUrlFromUri(uri), queryParams);
         if (body != null) {
             builder.setBody(body);
diff --git a/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
index 63f09d8..f399dae 100644
--- a/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
+++ b/util/src/main/java/com/ning/billing/util/email/DefaultEmailSender.java
@@ -41,9 +41,12 @@ public class DefaultEmailSender implements EmailSender {
             email = new HtmlEmail();
 
             email.setSmtpPort(config.getSmtpPort());
-            email.setAuthentication(config.getSmtpUserName(), config.getSmtpPassword());
+            if (config.useSmtpAuth()) {
+                email.setAuthentication(config.getSmtpUserName(), config.getSmtpPassword());
+            }
             email.setHostName(config.getSmtpServerName());
-            email.setFrom(config.getSmtpUserName());
+            email.setFrom(config.getDefaultFrom());
+
             email.setSubject(subject);
             email.setHtmlMsg(htmlBody);
 
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailConfig.java b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
index bcbf5e5..e972382 100644
--- a/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
+++ b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
@@ -16,26 +16,34 @@
 
 package com.ning.billing.util.email;
 
-import com.ning.billing.config.KillbillConfig;
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.DefaultNull;
 
-import java.util.Locale;
+import com.ning.billing.config.KillbillConfig;
 
 public interface EmailConfig extends KillbillConfig {
-    @Config("mail.smtp.host")
-    @Default("smtp.gmail.com")
+    @Config("killbill.mail.smtp.host")
+    @DefaultNull
     public String getSmtpServerName();
 
-    @Config("mail.smtp.port")
-    @Default("465")
+    @Config("killbill.mail.smtp.port")
+    @DefaultNull
     public int getSmtpPort();
 
-    @Config("mail.smtp.user")
-    @Default("killbill.ning@gmail.com")
+    @Config("killbill.mail.smtp.auth")
+    @Default("false")
+    public boolean useSmtpAuth();
+
+    @Config("killbill.mail.smtp.user")
+    @DefaultNull
     public String getSmtpUserName();
 
-    @Config("mail.smtp.password")
-    @Default("killbill@ning!")
+    @Config("killbill.mail.smtp.password")
+    @DefaultNull
     public String getSmtpPassword();
+
+    @Config("killbill.mail.from")
+    @Default("support@example.com")
+    String getDefaultFrom();
 }
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailModule.java b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
index 5bb252a..e8574cd 100644
--- a/util/src/main/java/com/ning/billing/util/email/EmailModule.java
+++ b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
@@ -16,12 +16,13 @@
 
 package com.ning.billing.util.email;
 
-import com.google.inject.AbstractModule;
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.google.inject.AbstractModule;
+
 public class EmailModule extends AbstractModule {
     protected void installEmailConfig() {
-        EmailConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
+        final EmailConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
         bind(EmailConfig.class).toInstance(config);
     }