diff --git a/stripe.admin.inc b/stripe.admin.inc
index c9b7aae..187cc8f 100644
--- a/stripe.admin.inc
+++ b/stripe.admin.inc
@@ -70,7 +70,7 @@ function stripe_admin_config_submit($form, &$form_state) {
 
 }
 
-function stripe_admin_test() {
+function stripe_admin_test($form, &$form_State) {
 /* From example on https://stripe.com/api
 
 <form action="" method="POST" id="payment-form">
@@ -93,7 +93,7 @@ function stripe_admin_test() {
 
 */
   $form['card_number'] = array(
-    '#type' => 'textfield',
+    '#type' => 'stripe_textfield',
     '#title' => t('Card Number:'),
     '#size' => 20,
     '#default_value' => '4242424242424242',
@@ -104,7 +104,7 @@ function stripe_admin_test() {
   );
 
   $form['card_cvc'] = array(
-    '#type' => 'textfield',
+    '#type' => 'stripe_textfield',
     '#title' => t('CVC:'),
     '#size' => 4,
     '#default_value' => '123',
@@ -115,23 +115,20 @@ function stripe_admin_test() {
   );
 
   $form['card_expiry_month'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Expiration (MM/YYYY):'),
-    '#size' => 2,
-    '#default_value' => '10',
+    '#type' => 'stripe_select',
+    '#title' => t('Card Expiration:'),
     '#attributes' => array(
       'class' => array('card-expiry-month'),
     ),
+    '#options' => drupal_map_assoc(array('01','02','03','04','05','06','07','08','09','10','11','12')),
   );
 
   $form['card_expiry_year'] = array(
-    '#type' => 'textfield',
-    '#title' => t('/'),
-    '#size' => 2,
-    '#default_value' => '2020',
+    '#type' => 'stripe_select',
     '#attributes' => array(
       'class' => array('card-expiry-year'),
     ),
+    '#options' => drupal_map_assoc(range(date('Y'), date('Y')+15)),
   );
 
   $form['amount'] = array(
@@ -143,8 +140,10 @@ function stripe_admin_test() {
     '#size' => 6,
     '#attributes' => array(
       'class' => array('amount'),
-    ),
+    )
   );
+  
+  $form['stripe_token'] = array('#type' => 'hidden', '#default_value' => '');
 
   // Attach a little custom css.
   $form['#attached']['css'] = array(
@@ -178,4 +177,19 @@ function stripe_admin_test() {
   }
 
   return $form;
+}
+
+
+function stripe_admin_test_submit($form, &$form_state) {
+  dpm($form_state);
+  
+  require_once(drupal_get_path('module', 'stripe') . '/stripe/lib/Stripe.php');
+  
+  Stripe::setApiKey("key goes here");
+  dpm(Stripe_Charge::create(array(
+    "amount" => $form_state['values']['amount'],
+    "currency" => "usd",
+    "card" => $form_state['values']['stripe_token'], // obtained with stripe.js
+    "description" => "Charge for matt.kleve@gmail.com")
+  ));
 }
\ No newline at end of file
diff --git a/stripe.js b/stripe.js
index 54b3822..e2150c6 100644
--- a/stripe.js
+++ b/stripe.js
@@ -4,18 +4,17 @@ Drupal.behaviors.stripeConfig = {
   attach: function (context) {
     // For now we'll just do the test form.
     $("#stripe-admin-test").submit(function(event) {
-        console.log('test');
         // disable the submit button to prevent repeated clicks
         $('.submit-button').attr("disabled", "disabled");
-
+        
         var amount = $('.amount').val();
         Stripe.createToken({
-            number: $('.card-number').val(),
-            cvc: $('.card-cvc').val(),
-            exp_month: $('.card-expiry-month').val(),
-            exp_year: $('.card-expiry-year').val()
+          number: $('.card-number').val(),
+          cvc: $('.card-cvc').val(),
+          exp_month: $('.card-expiry-month').val(),
+          exp_year: $('.card-expiry-year').val()
         }, amount, stripeResponseHandler);
-
+        
         // prevent the form from submitting with the default action
         return false;
     });
@@ -25,6 +24,9 @@ Drupal.behaviors.stripeConfig = {
 
 
   stripeResponseHandler = function(status, response) {
+    console.log('status:');
+    console.log(status);
+    console.log('response:');
     console.log(response);
     if (response.error) {
       //show the errors on the form
@@ -35,10 +37,10 @@ Drupal.behaviors.stripeConfig = {
       // token contains id, last4, and card type
       var token = response['id'];
       // insert the token into the form so it gets submitted to the server
-      $form.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
+      $('input[name=stripe_token]').val(response['id']);
+
       // and submit
-      //$form.get(0).submit();
-      alert('done');
+      $form.get(0).submit();
     }
   }
 
diff --git a/stripe.module b/stripe.module
index c8b2300..d8bd645 100644
--- a/stripe.module
+++ b/stripe.module
@@ -35,13 +35,6 @@ function stripe_permission() {
 }
 
 /**
- * Implements hook_theme().
-
-function stripe_theme() {
-
-}
-
-/**
  * Implements hook_menu().
  */
 function stripe_menu() {
@@ -74,4 +67,71 @@ function stripe_menu() {
   );
 
   return $items;
-}
\ No newline at end of file
+}
+
+
+/**
+ * Implements hook_element_info().
+ */
+function stripe_element_info() {
+  $types = array();
+  $types['stripe_textfield'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    '#maxlength' => 128,
+    '#autocomplete_path' => FALSE,
+    '#process' => array('ajax_process_form'),
+    '#theme' => 'stripe_textfield',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['stripe_select'] = array(
+    '#input' => TRUE,
+    '#multiple' => FALSE,
+    '#process' => array('form_process_select', 'ajax_process_form'),
+    '#theme' => 'stripe_select',
+    '#theme_wrappers' => array('form_element'),
+  );
+  return $types;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function stripe_theme($existing, $type, $theme, $path) {
+  return array(
+    'stripe_textfield' => array(
+      'render element' => 'element',
+    ),
+    'stripe_select' => array(
+      'render element' => 'element',
+    ),
+  );
+}
+
+/**
+ * Theme function for the 'stripe textfield' form element.
+ * Mimics the textfield theme function except for the name attribute.
+ */
+function theme_stripe_textfield($variables) {
+  $element = $variables['element'];
+  $element['#attributes']['type'] = 'text';
+  element_set_attributes($element, array('id', 'value', 'size', 'maxlength'));
+  _form_set_class($element, array('form-text'));
+  
+  $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
+  
+  return $output;
+}
+
+/**
+ * Theme function for the 'stripe_select' form element.
+ * Mimics the select theme function except for the name attribute.
+ */
+function theme_stripe_select($variables) {
+  $element = $variables['element'];
+  element_set_attributes($element, array('id', 'size'));
+  _form_set_class($element, array('form-select'));
+
+  return '<select' . drupal_attributes($element['#attributes']) . '>' . form_select_options($element) . '</select>';
+}
+
