diff --git a/css/recurly-subscription.css b/css/recurly-subscription.css
new file mode 100644
index 0000000..3c7f974
--- /dev/null
+++ b/css/recurly-subscription.css
@@ -0,0 +1,85 @@
+/**
+ * @file
+ * CSS for subscription information. Typically under user/x/subscription.
+ */
+.subscription {
+  border: 1px solid #CCC;
+  border-radius: 10px;
+  padding: 10px;
+}
+.subscription h2 {
+  padding: 0;
+  margin: 0;
+}
+.subscription table {
+  width: auto;
+  float: left;
+}
+.subscription table td,
+.subscription table th {
+  color: inherit;
+  background: transparent;
+}
+.subscription table tr {
+  background: transparent;
+}
+
+.subscription .line-items {
+  float: right;
+  width: 260px;
+  padding: 0px;
+}
+.subscription .line-items.change {
+  background: #fffbcc;
+  border: 1px solid #E6DB55;
+  clear: right;
+}
+.subscription .line-items.change .label {
+  font-weight: bold;
+  padding: 10px;
+}
+.subscription .line-items.change .total {
+  border-color: #E6DB55;
+}
+.subscription .line-items ul {
+  margin: 0;
+  padding: 0;
+}
+.subscription .line-items li {
+  padding: 5px 10px 5px 10px;
+  overflow: hidden;
+  vertical-align: baseline;
+  font-size: 13px;
+}
+.subscription .line-items li > .name {
+  display: inline;
+}
+.subscription .line-items li > .qty {
+  font-weight: bold;
+  font-size: 12px;
+  display: inline;
+}
+.subscription .line-items li > .qty:after {
+  font-weight: normal;
+  content: '\00D7';
+  padding-right: 5px;
+}
+.subscription .line-items li > .cost {
+  display: block;
+  float: right;
+  padding-left: 10px;
+  padding-bottom: 10px;
+  height: 100%;
+}
+.subscription .line-items .total {
+  clear: both;
+  float: right;
+  border-top: 1px solid gray;
+  padding: 10px;
+}
+.subscription .changing_to {
+  display: inline;
+  top: 100%;
+  left: 0;
+  background: red;
+}
\ No newline at end of file
diff --git a/includes/recurly.admin.inc b/includes/recurly.admin.inc
index 459e6d6..9f69ca0 100644
--- a/includes/recurly.admin.inc
+++ b/includes/recurly.admin.inc
@@ -56,7 +56,84 @@
     '#default_value' => variable_get('recurly_push_logging', FALSE),
   );
 
-  return system_settings_form($form);
+  $entity_types = entity_get_info();
+  $entity_options = array();
+  foreach ($entity_types as $entity_name => $entity_info) {
+    $entity_options[$entity_name] = $entity_info['label'];
+    $first_bundle_name = key($entity_info['bundles']);
+    // Don't generate a list of bundles if this entity does not have types.
+    if ($entity_info['bundles'] > 1 && $first_bundle_name !== $entity_name) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        $entity_type_options[$entity_name][$bundle_name] = $bundle_info['label'];
+      }
+    }
+  }
+
+  // If any of the below options change we need to rebuild the menu system.
+  // Keep a record of their current values.
+  $recurly_entity_type = variable_get('recurly_entity_type', 'user');
+  $form_state['pages_previous_values'] = array(
+    'recurly_entity_type' => $recurly_entity_type,
+    'recurly_bundle_' . $recurly_entity_type => variable_get('recurly_bundle_' . $recurly_entity_type),
+    'recurly_entity_display_title' => variable_get('recurly_entity_display_title', 'Subscription'),
+  );
+
+  $form['pages'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Built-in subscription/invoice pages'),
+    '#collapsible' => TRUE,
+    '#description' => t('The Recurly module provides built-in pages for letting users view their own recent invoices on the site instead of needing to go to the Recurly site. If enabled, these pages will show up under a particular object type (User, Node, Group, etc.) on that object\'s asset page. If a companion module is enabled such as the Recurly Hosted Pages or RecurlyJS module (both included with this project), appropriate links to update billing information or subscribe will also be displayed on these pages.'),
+  );
+  $form['pages']['recurly_entity_type'] = array(
+    '#title' => t('Enable built-in subscription/invoice pages on each'),
+    '#type' => 'select',
+    '#options' => array('' => 'Nothing (disabled)') + $entity_options,
+    '#default_value' => $recurly_entity_type,
+  );
+  foreach ($entity_type_options as $entity_name => $entity_options) {
+    $form['pages']['recurly_bundles']['recurly_bundle_' . $entity_name] = array(
+      '#title' => t('Specifically the following @entity type', array('@entity' => $entity_types[$entity_name]['label'])),
+      '#type' => 'select',
+      '#options' => $entity_options,
+      '#default_value' => variable_get('recurly_bundle_' . $entity_name),
+      '#states' => array(
+        'visible' => array(
+          'select[name="recurly_entity_type"]' => array('value' => $entity_name),
+        ),
+      ),
+    );
+  }
+  $form['pages']['recurly_entity_display'] = array(
+    '#title' => t('Display subscription/invoice pages as'),
+    '#type' => 'radios',
+    '#options' => array(
+      'tabs' => t('A local task on the object page (similar to the "View" and "Edit" tabs)'),
+      'hidden' => t('Hidden menu callback only (i.e. user/[uid]/subscription)'),
+    ),
+    '#default_value' => variable_get('recurly_entity_display', 'tabs'),
+    '#states' => array(
+      'visible' => array(
+        'select[name="recurly_entity_type"]' => array('!value' => ''),
+      ),
+    ),
+  );
+  $form['pages']['recurly_entity_display_title'] = array(
+    '#title' => t('Subscription tab/page title'),
+    '#type' => 'textfield',
+    '#required' => TRUE,
+    '#size' => 20,
+    '#default_value' => variable_get('recurly_entity_display_title', 'Subscriptions'),
+    '#description' => t('The text used as the label of the tab (if displayed as a tab) and page title for subscription/invoice pages.'),
+    '#states' => array(
+      'visible' => array(
+        'select[name="recurly_entity_type"]' => array('!value' => ''),
+      ),
+    ),
+  );
+
+  $form = system_settings_form($form);
+  $form['#submit'][] = 'recurly_settings_form_submit';
+  return $form;
 }
 
 /**
@@ -68,9 +145,22 @@
     'recurly_private_key',
     'recurly_subdomain',
     'recurly_listener_key',
+    'recurly_entity_display_title',
   );
   foreach ($keys as $key) {
     $form_state['values'][$key] = trim($form_state['values'][$key]);
+  }
+}
+
+/**
+ * Submit handler for recurly_settings_form().
+ */
+function recurly_settings_form_submit($form, &$form_state) {
+  // Rebuild the menu system if any of the built-in page options change.
+  foreach ($form_state['pages_previous_values'] as $variable_name => $previous_value) {
+    if (isset($form_state['values'][$variable_name]) && $form_state['values'][$variable_name] !== $previous_value) {
+      menu_rebuild();
+    }
   }
 }
 
@@ -117,21 +207,21 @@
 
     // TODO: Remove reset() calls once Recurly_CurrencyList implements Iterator.
     // See https://github.com/recurly/recurly-client-php/issues/37
-    $unit_amounts = in_array('Iterator', class_implements($plan->unit_amount_in_cents)) ? $plan->unit_amount_in_cents : reset($plan->unit_amount_in_cents);
-    $setup_fees = in_array('Iterator', class_implements($plan->setup_fee_in_cents)) ? $plan->setup_fee_in_cents : reset($plan->setup_fee_in_cents);
+    $unit_amounts = in_array('IteratorAggregate', class_implements($plan->unit_amount_in_cents)) ? $plan->unit_amount_in_cents : reset($plan->unit_amount_in_cents);
+    $setup_fees = in_array('IteratorAggregate', class_implements($plan->setup_fee_in_cents)) ? $plan->setup_fee_in_cents : reset($plan->setup_fee_in_cents);
 
     $row = array();
     $row[] = check_plain($plan->name) . ' <small>(' . t('Plan code: @code', array('@code' => $plan->plan_code)) . ')</small>' . $description;
 
     $amount_strings = array();
     foreach ($unit_amounts as $unit_amount) {
-      $amount_strings[] = t('@unit_price per @interval_length @interval_unit', array('@unit_price' => number_format($unit_amount->amount(), 2) . ' ' . $unit_amount->currencyCode, '@interval_length' => $plan->plan_interval_length, '@interval_unit' => $plan->plan_interval_unit));
+      $amount_strings[] = t('@unit_price every @interval_length @interval_unit', array('@unit_price' => recurly_format_currency($unit_amount->amount_in_cents, $unit_amount->currencyCode), '@interval_length' => $plan->plan_interval_length, '@interval_unit' => $plan->plan_interval_unit));
     }
     $row[] = implode('<br />', $amount_strings);
 
     $setup_strings = array();
     foreach ($setup_fees as $setup_fee) {
-      $setup_strings[] = check_plain(number_format($unit_amount->amount(), 2) . ' ' . $unit_amount->currencyCode);
+      $setup_strings[] = recurly_format_currency($unit_amount->amount_in_cents, $unit_amount->currencyCode);
     }
     $row[] = implode('<br />', $setup_strings);
 
diff --git a/includes/recurly.pages.inc b/includes/recurly.pages.inc
new file mode 100644
index 0000000..b37f548
--- /dev/null
+++ b/includes/recurly.pages.inc
@@ -0,0 +1,259 @@
+<?php
+
+/**
+ * @file
+ * Recurly front-end page callbacks.
+ */
+
+/**
+ * Menu callback; Display a summary of a Recurly account subscriptions.
+ */
+function recurly_subscription_page($entity_type, $entity) {
+  $subscriptions = array();
+
+  // Initialize the Recurly client with the site-wide settings.
+  if (!recurly_client_initialize()) {
+    return t('Could not initialize the Recurly client.');
+  }
+
+  // See if we have a local mapping of entity ID to Recurly account code.
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $account = recurly_account_load(array('entity_type' => $entity_type, 'entity_id' => $id));
+
+  // Unlikely that we'd have more than 50 subscriptions, but you never know.
+  $per_page = 50;
+  $subscription_list = Recurly_SubscriptionList::getForAccount($account->account_code, array('per_page' => $per_page));
+  $page_subscriptions = recurly_pager_results($subscription_list, $per_page);
+
+  $subscriptions['subscriptions']['#attached']['css'] = array(
+    drupal_get_path('module', 'recurly') . '/css/recurly-subscription.css',
+  );
+  foreach ($page_subscriptions as $subscription) {
+    // Generate the list of links for this subscription.
+    $subscription_links = array();
+    $subscription_links['update'] = array(
+      'href' => $entity_type . '/' . $id . '/subscription/id/' . $subscription->uuid,
+      'title' => t('Change plan'),
+    );
+    if ($subscription->state === 'active') {
+      $subscription_links['cancel'] = array(
+        'href' => $entity_type . '/' . $id . '/subscription/id/' . $subscription->uuid . '/cancel',
+        'title' => t('Cancel'),
+      );
+    }
+    elseif ($subscription->state === 'canceled') {
+      $subscription_links['reactivate'] = array(
+        'href' => $entity_type . '/' . $id . '/subscription/id/' . $subscription->uuid . '/reactivate',
+        'title' => t('Reactivate'),
+      );
+    }
+    // Allow other modules to provide links, perhaps "suspend" for example.
+    drupal_alter('recurly_subscription_links', $subscription_links);
+
+    $plan = $subscription->plan;
+    $add_ons = array();
+    $total = 0;
+    foreach ($subscription->subscription_add_ons as $add_on) {
+      // Fully load the add on to get the name attribute.
+      $full_add_on = Recurly_Addon::get($plan->plan_code, $add_on->add_on_code);
+      $add_ons[$add_on->add_on_code] = array(
+        'add_on_code' => $add_on->add_on_code,
+        'name' => check_plain($full_add_on->name),
+        'quantity' => check_plain($add_on->quantity),
+        'cost' => recurly_format_currency($add_on->unit_amount_in_cents, $subscription->currency),
+      );
+      $total += $add_on->unit_amount_in_cents * $add_on->quantity;
+    }
+    $total += $subscription->unit_amount_in_cents * $subscription->quantity;
+    $subscriptions['subscriptions'][$plan->plan_code] = array(
+      '#theme' => 'recurly_subscription_summary',
+      '#plan_code' => $plan->plan_code,
+      '#plan_name' => check_plain($plan->name),
+      '#state' => $subscription->state,
+      '#cost' => recurly_format_currency($subscription->unit_amount_in_cents, $subscription->currency),
+      '#quantity' => $subscription->quantity,
+      '#add_ons' => $add_ons,
+      '#start_date' => recurly_format_date($subscription->activated_at),
+      '#current_period_start' => recurly_format_date($subscription->current_period_started_at),
+      '#current_period_ends_at' => recurly_format_date($subscription->current_period_ends_at),
+      '#total' => recurly_format_currency($total, $subscription->currency),
+      '#subscription_links' => theme('links', array('links' => $subscription_links)),
+      '#message' => recurly_subscription_state_message($subscription->state),
+    );
+  }
+
+  $subscriptions['pager'] = array(
+    '#markup' => theme('pager'),
+    '#access' => $subscription_list->count() > $per_page,
+  );
+
+  drupal_alter('recurly_subscription_page', $subscriptions);
+  return $subscriptions;
+}
+
+/**
+ * Returns a message for a subscription if the subscription state is not active.
+ */
+function recurly_subscription_state_message($state) {
+  switch ($state) {
+    case 'active':
+      return '';
+    case 'closed':
+      return t('Your previous account is now closed. Please repurchase a subscription.');
+    case 'in_trial':
+      return t('You are currently in a trial period.');
+    case 'past_due':
+      return t('Your account is past due. Please update your billing information');
+    case 'canceled':
+      return t('Your previous subscription was canceled. You can reactivate your previous subscription to by clicking Reactivate');
+    case 'expired':
+      return t('Your previous subscription has expired. Please purchase a new subscription.');
+    case 'future':
+      return t('Your subscription has not started yet. Please contact support if you have any questions.');
+    default:
+      return '';
+  }
+}
+
+/**
+ * Menu callback; Display a list of all invoices for a user.
+ */
+function recurly_invoices_page($entity_type, $entity) {
+  // Initialize the Recurly client with the site-wide settings.
+  if (!recurly_client_initialize()) {
+    return t('Could not initialize the Recurly client.');
+  }
+
+  // See if we have a local mapping of entity ID to Recurly account code.
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $account = recurly_account_load(array('entity_type' => $entity_type, 'entity_id' => $id));
+
+  $per_page = 5;
+  $invoice_list = Recurly_InvoiceList::getForAccount($account->account_code, array('per_page' => $per_page));
+  $page_invoices = recurly_pager_results($invoice_list, $per_page);
+
+  $header = array(t('Number'), t('Date'), t('Total'));
+  $rows = array();
+  foreach ($page_invoices as $invoice) {
+    $row = array();
+    $row[] = l($invoice->invoice_number, $entity_type . '/' . $id . '/subscription/invoices/' . $invoice->invoice_number);
+    $row[] = recurly_format_date($invoice->created_at);
+    $row[] = recurly_format_currency($invoice->total_in_cents, $invoice->currency);
+    $rows[] = $row;
+  }
+
+  $invoices['table']['#attached']['css'] = array(
+    drupal_get_path('module', 'recurly') . '/css/recurly-subscription.css',
+  );
+  $invoices['table'] = array(
+    '#markup' => theme('table', array('header' => $header, 'rows' => $rows)),
+  );
+  $invoices['pager'] = array(
+    '#markup' => theme('pager'),
+    '#access' => $invoice_list->count() > $per_page,
+  );
+
+  return $invoices;
+}
+
+/**
+ * Menu callback; Display an individual invoice.
+ */
+function recurly_invoice_page($entity_type, $entity, $invoice_number) {
+  // Initialize the Recurly client with the site-wide settings.
+  if (!recurly_client_initialize()) {
+    return t('Could not initialize the Recurly client.');
+  }
+
+  // See if we have a local mapping of entity ID to Recurly account code.
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $account = recurly_account_load(array('entity_type' => $entity_type, 'entity_id' => $id));
+
+  // Load the invoice. For some reason the RecurlySDK returns this as a list,
+  // so a little clean up is necessary.
+  $invoice = Recurly_Invoice::get($invoice_number);
+
+  // Load the invoice account.
+  $invoice_account = $invoice->account->get();
+
+  // Ensure that the user account is the same as the invoice account.
+  if ($invoice_account->account_code !== $account->account_code) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  return 'test';//drupal_render_children($invoice);
+}
+
+/**
+ * Deliver an invoice PDF file from Recurly.com.
+ */
+function recurly_invoice_pdf($entity_type, $entity, $invoice_number) {
+  if (empty($invoice_number) || !is_numeric($uid) || !is_numeric($invoice_number)) {
+    return drupal_set_message(t('An invoice was not found or their was an error retrieving it.'), 'error');
+  }
+
+  // Initialize the Recurly client with the site-wide settings.
+  if (!recurly_client_initialize()) {
+    return t('Could not initialize the Recurly client.');
+  }
+
+  try {
+    $pdf = Recurly_Invoice::getInvoicePdf($invoice_number, 'en-US');
+    if (!empty($pdf)) {
+      if (headers_sent()) {
+        die("Unable to stream pdf: headers already sent");
+      }
+
+      drupal_add_http_header('Content-Type', 'application/pdf', TRUE);
+      drupal_add_http_header('Content-Disposition', 'inline; filename="' . $invoice_number . '.pdf"', TRUE);
+      // I guess below is not necessary plus filesize was not working anyway?!
+      // drupal_add_http_header('Content-Transfer-Encoding', 'binary', TRUE);
+      // drupal_add_http_header('Content-Length', filesize($pdf), TRUE);
+      echo $pdf;
+    }
+  }
+  catch (Recurly_NotFoundError $e) {
+    drupal_set_message(t('Invoice not found'));
+    return MENU_NOT_FOUND;
+  }
+}
+
+/**
+ * Utility function to retrieve a specific page of results from a Recurly_Pager.
+ *
+ * @param $pager_object
+ *   Any object that extends a Recurly_Pager object, such as a
+ *   Recurly_InvoiceList, Recurly_SubscriptionList, or Recurly_TransactionList.
+ * @param $element
+ *   A unique identifier for this pager.
+ * @param $per_page
+ *   The number of items to display per page.
+ * @param $page_num
+ *   The desired page number to display. Usually automatically determined from
+ *   the URL.
+ */
+function recurly_pager_results($pager_object, $per_page, $page_num = NULL) {
+  if (!isset($page_num)) {
+    $page_num = isset($_GET['page']) ? (int) $_GET['page'] : 0;
+  }
+
+  // Fast forward the list to the current page.
+  $start = $page_num * $per_page;
+  for ($n = 0; $n < $start; $n++) {
+    $pager_object->next();
+  }
+
+  // Populate $page_results with the current page.
+  $total = $pager_object->count();
+  $page_end = min($start + $per_page, $total);
+  $page_results = array();
+  for ($n = $start; $n < $page_end; $n++) {
+    $invoice = $pager_object->current();
+    $page_results[$invoice->invoice_number] = $invoice;
+    $pager_object->next();
+  }
+
+  pager_default_initialize($total, $per_page);
+
+  return $page_results;
+}
diff --git a/recurly.install b/recurly.install
index 750775f..8a36870 100644
--- a/recurly.install
+++ b/recurly.install
@@ -7,38 +7,10 @@
   $schema = array();
 
   $schema['recurly_account'] = array(
-    'description' => 'Recurly account information mapped to Drupal users.',
+    'description' => 'Recurly account information mapped to Drupal entities.',
     'fields' => array(
       'account_code' => array(
         'description' => 'The unique identifier of the account in Recurly.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'email' => array(
-        'description' => 'The e-mail address associated with the account in Recurly.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'first_name' => array(
-        'description' => 'The first name of the account holder.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'last_name' => array(
-        'description' => 'The last name of the account holder.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'company_name' => array(
-        'description' => 'The company name associated with the account.',
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
@@ -51,8 +23,15 @@
         'not null' => TRUE,
         'default' => '',
       ),
-      'uid' => array(
-        'description' => 'The {users}.uid that maps to this account.',
+      'entity_type' => array(
+        'description' => 'The Drupal entity type this account is associated with, typical "user" or "node".',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'entity_id' => array(
+        'description' => 'The Drupal entity ID that maps to this account.',
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
@@ -63,23 +42,10 @@
         'not null' => TRUE,
         'default' => 0,
       ),
-      'data' => array(
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big',
-        'serialize' => TRUE,
-        'description' => 'A serialized array of additional data.',
-      ),
     ),
     'primary key' => array('account_code'),
     'indexes' => array(
-      'uid' => array('uid'),
-    ),
-    'foreign keys' => array(
-      'user' => array(
-        'table' => 'users',
-        'columns' => array('uid' => 'uid'),
-      ),
+      'entity_type_entity_id' => array('entity_type', 'entity_id'),
     ),
   );
 
@@ -106,7 +72,4 @@
   // Remove the push notification settings.
   variable_del('recurly_listener_key');
   variable_del('recurly_push_logging');
-
-  // Remove the integration options settings.
-  variable_del('recurly_account_integration');
 }
diff --git a/recurly.module b/recurly.module
index 1dfe78b..336af0a 100644
--- a/recurly.module
+++ b/recurly.module
@@ -6,7 +6,6 @@
  * with Drupal user accounts.
  */
 
-
 /**
  * Implements hook_menu().
  */
@@ -51,6 +50,55 @@
     'weight' => -5,
     'file' => 'includes/recurly.admin.inc',
   );
+
+  // Configure built-in pages if enabled.
+  $entity_type = variable_get('recurly_entity_type', 'user');
+  if ($entity_type) {
+    $entity_info = entity_get_info();
+    $bundle = variable_get('recurly_bundle_' . $entity_type, NULL);
+    $bundle = empty($bundle) ? $entity_type : $bundle;
+
+    $entity_path = $entity_type . '/%' . $entity_type;
+    $items[$entity_path . '/subscription'] = array(
+      'title' => variable_get('recurly_entity_display_title', 'Billing'),
+      'page callback' => 'recurly_subscription_page',
+      'page arguments' => array($entity_type, 1),
+      'access callback' => $entity_info[$entity_type]['access callback'],
+      'access arguments' => array('update', 1, NULL, $entity_type),
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 10,
+      'file' => 'includes/recurly.pages.inc',
+    );
+    $items[$entity_path . '/subscription/summary'] = array(
+      'title' => 'Summary',
+      'page callback' => 'recurly_subscription_page',
+      'page arguments' => array($entity_type, 1),
+      'access callback' => $entity_info[$entity_type]['access callback'],
+      'access arguments' => array('update', 1, NULL, $entity_type),
+      'type' => MENU_DEFAULT_LOCAL_TASK,
+      'weight' => 0,
+      'file' => 'includes/recurly.pages.inc',
+    );
+    $items[$entity_path . '/subscription/invoices'] = array(
+      'title' => 'Invoices',
+      'page callback' => 'recurly_invoices_page',
+      'page arguments' => array($entity_type, 1),
+      'access callback' => $entity_info[$entity_type]['access callback'],
+      'access arguments' => array('update', 1, NULL, $entity_type),
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 1,
+      'file' => 'includes/recurly.pages.inc',
+    );
+    $items[$entity_path . '/subscription/invoices/%'] = array(
+      'title' => 'Invoices',
+      'page callback' => 'recurly_invoice_page',
+      'page arguments' => array($entity_type, 1, 4),
+      'access callback' => $entity_info[$entity_type]['access callback'],
+      'access arguments' => array('update', 1, NULL, $entity_type),
+      'type' => MENU_CALLBACK,
+      'file' => 'includes/recurly.pages.inc',
+    );
+  }
 
   return $items;
 }
@@ -105,6 +153,43 @@
     case 'admin/config/services/recurly/subscription-plans':
       return '<p>' . t('The subscription plans below are defined for the Recurly account configured in your default account settings. Plans should be defined and updated at Recurly and are cached here for informational purposes only. Edit links and purchase links that may appear in the table redirect to Recurly.') . '</p>';
   }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function recurly_theme() {
+  $items['recurly_subscription_summary'] = array(
+    'variables' => array(
+      'plan_code' => NULL,
+      'plan_name' => NULL,
+      'state' => NULL,
+      'cost' => NULL,
+      'quantity' => NULL,
+      'add_ons' => array(),
+      'start_date' => NULL,
+      'current_period_start' => NULL,
+      'current_period_ends_at'=> NULL,
+      'total' => NULL,
+      'subscription_links' => array(),
+      'message' => NULL,
+    ),
+    'template' => 'templates/recurly-subscription-summary',
+  );
+  $items['recurly_credit_card_information'] = array(
+    'variables' => array(
+      'first_name' => NULL,
+      'last_name' => NULL,
+      'card_type' => NULL,
+      'year' => NULL,
+      'month' => NULL,
+      'mask_length' => NULL,
+      'last_four' => NULL,
+    ),
+    'template' => 'templates/recurly-credit-card-information',
+  );
+
+  return $items;
 }
 
 /**
@@ -183,20 +268,26 @@
 
       // If no local record exists and we've specified to create it...
       if (empty($local_account)) {
-        $uid = 0;
-
-        // Attempt to find a matching user account.
-        if ($user = user_load_by_mail($recurly_account->email)) {
-          $uid = $user->uid;
+        // First try to match based on the account code.
+        // i.e. "user-1" would match the user with UID 1.
+        $parts = explode('-', $recurly_account->account_code);
+        $entity_type = variable_get('recurly_entity_type', 'user');
+        if ($parts[0] === $entity_type) {
+          if (isset($parts[1]) && is_numeric($parts[1]) && ($entity = entity_load($parts[0], array($parts[1])))) {
+            recurly_account_save($recurly_account, $entity_type, $parts[1]);
+          }
         }
 
-        // Save the local record now.
-        recurly_account_save($recurly_account, $uid);
+        // Attempt to find a matching user account by e-mail address if the
+        // enabled entity type is user.
+        if ($entity_type === 'user' && ($user = user_load_by_mail($recurly_account->email))) {
+          recurly_account_save($recurly_account, 'user', $user->uid);
+        }
       }
       elseif (!empty($local_account)) {
         // Otherwise if a local record was found and we want to keep it
         // synchronized, save it afresh now, preserving any existing data array.
-        recurly_account_save($recurly_account, $local_account->uid, $local_account->data);
+        recurly_account_save($recurly_account, $local_account->entity_type, $local_account->entity_id, $local_account->data);
       }
     }
   }
@@ -327,9 +418,6 @@
     return FALSE;
   }
 
-  // Unserialize the data array.
-  $data->data = unserialize($data->data);
-
   // If we only want local data, return it now.
   if ($local) {
     return $data;
@@ -346,12 +434,8 @@
     }
 
     // If any data has changed remotely, update it locally now.
-    if ($recurly_account->first_name != $data->first_name ||
-      $recurly_account->last_name != $data->last_name ||
-      $recurly_account->company_name != $data->company_name ||
-      $recurly_account->email != $data->email ||
-      $recurly_account->state != $data->status) {
-      recurly_account_save($recurly_account, $data->uid);
+    if ($recurly_account->state != $data->status) {
+      recurly_account_save($recurly_account, $data->entity_type, $data->entity_id);
     }
   }
   catch (Exception $e) {
@@ -381,7 +465,7 @@
  *   STATUS_INSERT or STATUS_UPDATE indicating the type of query performed to
  *   save the account information locally.
  */
-function recurly_account_save($recurly_account, $uid, $data = array(), $export = FALSE) {
+function recurly_account_save($recurly_account, $entity_type, $entity_id, $data = array(), $export = FALSE) {
   // First attempt to save the data at Recurly if specified. Failing an export
   // will prevent local data from being saved so you don't end up with a local
   // record that does not match a record at Recurly.
@@ -413,11 +497,8 @@
 
   // Generate an array of data to save.
   $fields = array(
-    'email' => (string) $recurly_account->email,
-    'first_name' => (string) $recurly_account->first_name,
-    'last_name' => (string) $recurly_account->last_name,
-    'company_name' => (string) $recurly_account->company_name,
-    'uid' => $uid,
+    'entity_type' => $entity_type,
+    'entity_id' => $entity_id,
     'updated' => REQUEST_TIME,
   );
 
@@ -475,6 +556,19 @@
 }
 
 /**
+ * Returns an edit URL for a subscription plan.
+ *
+ * @param $plan
+ *   The subscription plan object returned by the Recurly client.
+ *
+ * @return
+ *   The URL for the plan's edit page at Recurly.
+ */
+function recurly_subscription_plan_edit_url($plan) {
+  return recurly_url('company/plans/' . $plan->plan_code);
+}
+
+/**
  * Returns the base Recurly URL for the current account with an optional path
  * appended to it.
  */
@@ -484,3 +578,106 @@
 
   return url('https://' . $subdomain . '.recurly.com/' . $path);
 }
+
+/**
+ * Format a date for use in invoices.
+ */
+function recurly_format_date($date) {
+  $format = variable_get('recurly_date_format', 'long');
+  if (is_object($date)) {
+    $date->setTimezone(new DateTimeZone('UTC'));
+    $timestamp = $date->format('U');
+  }
+  else {
+    $timestamp = strtotime($date);
+  }
+
+  return is_numeric($timestamp) ? format_date($timestamp, $format) : NULL;
+}
+
+/**
+ * Format a Recurly subscription state.
+ */
+function recurly_format_state($state) {
+  switch ($state) {
+    case 'active':
+      return t('Active');
+    case 'canceled':
+      return t('Canceled');
+    case 'expired':
+      return t('Expired');
+    case 'future':
+      return t('Future Activation');
+    case 'in_trial':
+      return t('Trial');
+    case 'live':
+      return t('Live');
+    case 'past_due':
+      return t('Past Due');
+  }
+}
+
+/**
+ * Format a price for display.
+ */
+function recurly_format_currency($price_in_cents, $currency) {
+  // Commerce module provides a more flexible and complete currency formatter.
+  if (module_exists('commerce')) {
+    return commerce_currency_format($price_in_cents, $currency, NULL, TRUE);
+  }
+
+  // Provide native support for the same currencies that Recurly supports:
+  // Currency code => array(
+  //   0 => 'symbol before',
+  //   1 => 'symbol after',
+  //   2 => 'thousand separator',
+  //   3 => 'decimal_separator',
+  //   4 => 'decimals',
+  //   5 => 'rounding_step',
+  // );
+  $currencies = array(
+    'USD' => array('$', ' USD'),
+    'AUD' => array('$', ' AUD'),
+    'CAD' => array('$', ' CAD'),
+    'EUR' => array('', ' €', ' ', ','),
+    'GBP' => array('£', ''),
+    'CZK' => array('', ' Kč', ' ', ','),
+    'DKK' => array('', ' kr.', ' ', ','),
+    'HUF' => array('', ' Ft', NULL, NULL, 0),
+    'JPY' => array('¥', ''),
+    'NOK' => array('', ' Nkr', ' ', ','),
+    'NZD' => array('NZ$', ''),
+    'PLN' => array('', ' zł', ' ', ','),
+    'SGD' => array('S$', ''),
+    'SEK' => array('', ' kr', ' ', ','),
+    'CHF' => array('', ' Fr.', NULL, NULL, NULL, '0.05'),
+    'ZAR' => array('R', ''),
+  );
+
+  if (isset($currencies[$currency])) {
+    $currency_info = $currencies[$currency];
+    $prefix = $currency_info[0] ? $currency_info[0] : '';
+    $suffix = $currency_info[1] ? $currency_info[1] : '';
+    $thousands_separator = isset($currency_info[2]) ? $currency_info[2] : ',';
+    $decimal_separator = isset($currency_info[3]) ? $currency_info[3] : '.';
+    $decimals = isset($currency_info[4]) ? $currency_info[4] : 2;
+    $rounding_step = isset($currency_info[5]) ? $currency_info[5] : NULL;
+
+    // Convert to a decimal amount.
+    $float = $price_in_cents/100;
+
+    // Round the amount if necessary i.e. Francs round up to the nearest 0.05.
+    if ($rounding_step) {
+      $modifier = 1 / $rounding_step;
+      $float = round($float * $modifier) / $modifier;
+    }
+
+    // Format the number.
+    $formatted = $prefix . number_format($float, $decimals, $decimal_separator, $thousands_separator) . $suffix;
+  }
+  else {
+    $formatted = number_format($price_in_cents/100, 2, '.', ',') . ' ' . $currency;
+  }
+
+  return $formatted;
+}
diff --git a/templates/recurly-credit-card-information.tpl.php b/templates/recurly-credit-card-information.tpl.php
new file mode 100644
index 0000000..c24f1aa
--- /dev/null
+++ b/templates/recurly-credit-card-information.tpl.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * @file
+ * Prints out a summary of an existing credit card with masked digits.
+ */
+?>
+<div class="credit-card-information">
+  <div class="credit-card-name"><?php print $first_name . ' ' . $last_name; ?></div>
+  <div class="credit-card-date"><?php print check_plain($card_type) . ' ' . t('Exp: @date', array('@date' => sprintf('%1$02d', $month) . '/' . $year)); ?></div>
+  <div class="credit-card-number"><?php print str_repeat ('x', $mask_length) . $last_four; ?></div>
+</div>
\ No newline at end of file
diff --git a/templates/recurly-subscription-summary.tpl.php b/templates/recurly-subscription-summary.tpl.php
new file mode 100644
index 0000000..108ad8d
--- /dev/null
+++ b/templates/recurly-subscription-summary.tpl.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @file
+ * Output a summary of a subscription with links to manage it.
+ */
+?>
+<div class="subscription mini clearfix">
+  <div class="subscription-summary clearfix">
+    <h2><?php print $plan_name; ?></h2>
+    <?php if (!empty($message)) : ?>
+      <div class="messages warning"><h2 class="element-invisible"><?php print('Warning message'); ?></h2><?php print $message; ?></div>
+    <?php endif; ?>
+    <table class="properties">
+      <tr class="status">
+        <th><?php print t('Status'); ?></th>
+        <td><?php print recurly_format_state($state); ?></td>
+      </tr>
+      <tr>
+        <th>Start Date</th>
+        <td><?php print $start_date; ?></td>
+      </tr>
+      <tr>
+        <th>Next Invoice</th>
+        <td><?php print $current_period_ends_at; ?></td>
+      </tr>
+    </table>
+    <div class="line-items">
+      <ul>
+        <li>
+          <div class="qty"><?php print $quantity; ?></div>
+          <div class="cost"><?php print $cost; ?></div>
+          <div class="name"><?php print $plan_name; ?></div>
+        </li>
+        <?php foreach ($add_ons as $add_on): ?>
+        <li>
+          <div class="qty"><?php print $add_on['quantity']; ?></div>
+          <div class="cost"><?php print $add_on['cost']; ?></div>
+          <div class="name"><?php print $add_on['name']; ?></div>
+        </li>
+        <?php endforeach; ?>
+      </ul>
+      <div class="total"><?php print $total; ?></div>
+    </div>
+  </div>
+  <div class="subscription-links clearfix">
+    <?php print $subscription_links; ?>
+  </div>
+</div>