diff --git a/app/index.html b/app/index.html
index 4218741..72771c3 100644
--- a/app/index.html
+++ b/app/index.html
@@ -33,13 +33,8 @@
     
 
     
-    
-    
-    
-    
-    
-    
-    
+    
+    
 
   
   
@@ -342,14 +337,8 @@
       
 
 
-        
-        
-
-        
-        
-        
-        
-        
+        
+        
 
         
         
diff --git a/app/js/bluecrypt-acme.js b/app/js/bluecrypt-acme.js
index 8984883..8d58e6e 100644
--- a/app/js/bluecrypt-acme.js
+++ b/app/js/bluecrypt-acme.js
@@ -2,8 +2,7 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-;
-(function (exports) {
+;(function (exports) {
 
 var Enc = exports.Enc = {};
 
@@ -1962,7 +1961,12 @@ ACME._getChallenges = function (me, options, authUrl) {
   , payload: ''
   , url: authUrl
   }).then(function (resp) {
-    return resp.body;
+    // Pre-emptive rather than lazy for interfaces that need to show the challenges to the user first
+    return ACME._challengesToAuth(me, options, resp.body, false).then(function (auths) {
+      resp.body._rawChallenges = resp.body.challenges;
+      resp.body.challenges = auths;
+      return resp.body;
+    });
   });
 };
 ACME._wait = function wait(ms) {
@@ -1987,12 +1991,6 @@ ACME._testChallengeOptions = function () {
       "token": "test-" + chToken + "-1",
       "_wildcard": true
     }
-  , {
-      "type": "tls-sni-01",
-      "status": "pending",
-      "url": "https://acme-staging-v02.example.com/2",
-      "token": "test-" + chToken + "-2"
-    }
   , {
       "type": "tls-alpn-01",
       "status": "pending",
@@ -2010,47 +2008,49 @@ ACME._testChallenges = function (me, options) {
       challenges = challenges.filter(function (ch) { return ch._wildcard; });
     }
 
-    var challenge = ACME._chooseChallenge(options, { challenges: challenges });
-    if (!challenge) {
-      // For example, wildcards require dns-01 and, if we don't have that, we have to bail
-      var enabled = Object.keys(options.challenges).join(', ') || 'none';
-      var suitable = challenges.map(function (r) { return r.type; }).join(', ') || 'none';
-      return Promise.reject(new Error(
-        "None of the challenge types that you've enabled ( " + enabled + " )"
-          + " are suitable for validating the domain you've selected (" + identifierValue + ")."
-          + " You must enable one of ( " + suitable + " )."
-      ));
-    }
-
-    // TODO remove skipChallengeTest
-    if (me.skipDryRun || me.skipChallengeTest) {
-      return null;
-    }
-
-    if ('dns-01' === challenge.type) {
-      // Give the nameservers a moment to propagate
-      CHECK_DELAY = 1.5 * 1000;
-    }
-
-    return Promise.resolve().then(function () {
-      var results = {
+    // The dry-run comes first in the spirit of "fail fast"
+    // (and protecting against challenge failure rate limits)
+    var dryrun = true;
+    var resp = {
+      body: {
         identifier: {
           type: "dns"
         , value: identifierValue.replace(/^\*\./, '')
         }
-      , challenges: [ challenge ]
+      , challenges: challenges
       , expires: new Date(Date.now() + (60 * 1000)).toISOString()
       , wildcard: identifierValue.includes('*.') || undefined
-      };
+      }
+    };
+    return ACME._challengesToAuth(me, options, resp.body, dryrun).then(function (auths) {
+      resp.body._rawChallenges = resp.body.challenges;
+      resp.body.challenges = auths;
 
-      // The dry-run comes first in the spirit of "fail fast"
-      // (and protecting against challenge failure rate limits)
-      var dryrun = true;
-      return ACME._challengeToAuth(me, options, results, challenge, dryrun).then(function (auth) {
-        if (!me._canUse[auth.type]) { return; }
-        return ACME._setChallenge(me, options, auth).then(function () {
-          return auth;
-        });
+      var auth = ACME._chooseAuth(options, resp.body.challenges);
+      if (!auth) {
+        // For example, wildcards require dns-01 and, if we don't have that, we have to bail
+        var enabled = Object.keys(options.challenges).join(', ') || 'none';
+        var suitable = resp.body.challenges.map(function (r) { return r.type; }).join(', ') || 'none';
+        return Promise.reject(new Error(
+          "None of the challenge types that you've enabled ( " + enabled + " )"
+            + " are suitable for validating the domain you've selected (" + identifierValue + ")."
+            + " You must enable one of ( " + suitable + " )."
+        ));
+      }
+
+      // TODO remove skipChallengeTest
+      if (me.skipDryRun || me.skipChallengeTest) {
+        return null;
+      }
+
+      if ('dns-01' === auth.type) {
+        // Give the nameservers a moment to propagate
+        CHECK_DELAY = 1.5 * 1000;
+      }
+
+      if (!me._canUse[auth.type]) { return; }
+      return ACME._setChallenge(me, options, auth).then(function () {
+        return auth;
       });
     });
   })).then(function (auths) {
@@ -2067,93 +2067,88 @@ ACME._testChallenges = function (me, options) {
     });
   });
 };
-ACME._chooseChallenge = function(options, results) {
+ACME._chooseAuth = function(options, auths) {
   // For each of the challenge types that we support
-  var challenge;
+  var auth;
   var challengeTypes = Object.keys(options.challenges);
   // ordered from most to least preferred
-  challengeTypes = [ 'tls-alpn-01', 'http-01', 'dns-01' ].filter(function (chType) {
+  challengeTypes = (options.challengePriority||[ 'tls-alpn-01', 'http-01', 'dns-01' ]).filter(function (chType) {
     return challengeTypes.includes(chType);
   });
 
-  /*
-  // Lot's of error checking to inform the user of mistakes
-  if (!(options.challengeTypes||[]).length) {
-    options.challengeTypes = Object.keys(options.challenges||{});
-  }
-  if (!options.challengeTypes.length) {
-    options.challengeTypes = [ options.challengeType ].filter(Boolean);
-  }
-  if (options.challengeType) {
-    options.challengeTypes.sort(function (a, b) {
-      if (a === options.challengeType) { return -1; }
-      if (b === options.challengeType) { return 1; }
-      return 0;
-    });
-    if (options.challengeType !== options.challengeTypes[0]) {
-      return Promise.reject(new Error("options.challengeType is '" + options.challengeType + "',"
-        + " which does not exist in the supplied types '" + options.challengeTypes.join(',') + "'"));
-    }
-  }
-  // TODO check that all challengeTypes are represented in challenges
-  if (!options.challengeTypes.length) {
-    return Promise.reject(new Error("options.challengeTypes (string array) must be specified"
-      + " (and in order of preferential priority)."));
-  }
-  */
   challengeTypes.some(function (chType) {
     // And for each of the challenge types that are allowed
-    return results.challenges.some(function (ch) {
+    return auths.some(function (ch) {
       // Check to see if there are any matches
       if (ch.type === chType) {
-        challenge = ch;
+        auth = ch;
         return true;
       }
     });
   });
 
-  return challenge;
+  return auth;
 };
-ACME._challengeToAuth = function (me, options, request, challenge, dryrun) {
+ACME._challengesToAuth = function (me, options, request, dryrun) {
   // we don't poison the dns cache with our dummy request
   var dnsPrefix = ACME.challengePrefixes['dns-01'];
   if (dryrun) {
     dnsPrefix = dnsPrefix.replace('acme-challenge', 'greenlock-dryrun-' + ACME._prnd(4));
   }
+  var challengeTypes = Object.keys(options.challenges);
 
-  var auth = {};
-
-  // straight copy from the new order response
-  // { identifier, status, expires, challenges, wildcard }
-  Object.keys(request).forEach(function (key) {
-    auth[key] = request[key];
-  });
-
-  // copy from the challenge we've chosen
-  // { type, status, url, token }
-  // (note the duplicate status overwrites the one above, but they should be the same)
-  Object.keys(challenge).forEach(function (key) {
-    // don't confused devs with the id url
-    auth[key] = challenge[key];
-  });
-
-  // batteries-included helpers
-  auth.hostname = auth.identifier.value;
-  // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
-  auth.altname = ACME._untame(auth.identifier.value, auth.wildcard);
   return ACME._importKeypair(me, options.accountKeypair).then(function (pair) {
     return me.Keypairs.thumbprint({ jwk: pair.public }).then(function (thumb) {
-      auth.thumbprint = thumb;
-      //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
-      auth.keyAuthorization = challenge.token + '.' + auth.thumbprint;
-      // conflicts with ACME challenge id url is already in use, so we call this challengeUrl instead
-      // TODO auth.http01Url ?
-      auth.challengeUrl = 'http://' + auth.identifier.value + ACME.challengePrefixes['http-01'] + '/' + auth.token;
-      auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', '');
+      return Promise.all(request.challenges.map(function (challenge) {
+        // Don't do extra work for challenges that we can't satisfy
+        if (!challengeTypes.includes(challenge.type)) {
+          return null;
+        }
 
-      return Crypto._sha('sha256', auth.keyAuthorization).then(function (hash) {
-        auth.dnsAuthorization = hash;
-        return auth;
+        var auth = {};
+
+        // straight copy from the new order response
+        // { identifier, status, expires, challenges, wildcard }
+        Object.keys(request).forEach(function (key) {
+          auth[key] = request[key];
+        });
+
+        // copy from the challenge we've chosen
+        // { type, status, url, token }
+        // (note the duplicate status overwrites the one above, but they should be the same)
+        Object.keys(challenge).forEach(function (key) {
+          // don't confused devs with the id url
+          auth[key] = challenge[key];
+        });
+
+        // batteries-included helpers
+        auth.hostname = auth.identifier.value;
+        // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
+        auth.altname = ACME._untame(auth.identifier.value, auth.wildcard);
+
+        auth.thumbprint = thumb;
+        //   keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
+        auth.keyAuthorization = challenge.token + '.' + auth.thumbprint;
+
+        if ('http-01' === auth.type) {
+          // conflicts with ACME challenge id url is already in use, so we call this challengeUrl instead
+          // TODO auth.http01Url ?
+          auth.challengeUrl = 'http://' + auth.identifier.value + ACME.challengePrefixes['http-01'] + '/' + auth.token;
+          return auth;
+        }
+
+        if ('dns-01' !== auth.type) {
+          return auth;
+        }
+
+        return Crypto._sha('sha256', auth.keyAuthorization).then(function (hash) {
+          auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', '');
+          auth.dnsAuthorization = hash;
+          auth.keyAuthorizationDigest = hash;
+          return auth;
+        });
+      })).then(function (auths) {
+        return auths.filter(Boolean);
       });
     });
   });
@@ -2241,18 +2236,21 @@ ACME._postChallenge = function (me, options, auth) {
         return resp.body;
       }
 
-      var errmsg;
-      if (!resp.body.status) {
-        errmsg = "[acme-v2] (E_STATE_EMPTY) empty challenge state for '" + altname + "':";
-      }
-      else if ('invalid' === resp.body.status) {
-        errmsg = "[acme-v2] (E_STATE_INVALID) challenge state for '" + altname + "': '" + resp.body.status + "'";
-      }
-      else {
-        errmsg = "[acme-v2] (E_STATE_UKN) challenge state for '" + altname + "': '" + resp.body.status + "'";
+      var err;
+      if (resp.body.error && resp.body.error.detail) {
+        err = new Error("[acme-v2] " + auth.altname + " state:" + resp.body.status + " " + resp.body.error.detail);
+        err.auth = auth;
+        err.altname = auth.altname;
+        err.type = auth.type;
+        err.urn = resp.body.error.type;
+        err.code = ('invalid' === resp.body.status) ? 'E_CHALLENGE_INVALID' : 'E_CHALLENGE_UNKNOWN';
+        err.uri = resp.body.url;
+      } else {
+        err = new Error("[acme-v2] " + auth.altname + " (E_STATE_UKN): " + JSON.stringify(resp.body, null, 2));
+        err.code = 'E_CHALLENGE_UNKNOWN';
       }
 
-      return Promise.reject(new Error(errmsg));
+      return Promise.reject(err);
     });
   }
 
@@ -2312,34 +2310,32 @@ ACME._setChallenge = function (me, options, auth) {
 ACME._setChallengesAll = function (me, options) {
   var order = options.order;
   var setAuths = order.authorizations.slice(0);
-  var challenges = order.challenges;
+  var claims = order.claims.slice(0);
   var validAuths = [];
   var auths = [];
 
   function setNext() {
     var authUrl = setAuths.shift();
-    var results = challenges.shift();
+    var claim = claims.shift();
     if (!authUrl) { return; }
 
-    // var domain = options.domains[i]; // results.identifier.value
+    // var domain = options.domains[i]; // claim.identifier.value
 
     // If it's already valid, we're golden it regardless
-    if (results.challenges.some(function (ch) { return 'valid' === ch.status; })) {
+    if (claim.challenges.some(function (ch) { return 'valid' === ch.status; })) {
       return setNext();
     }
 
-    var challenge = ACME._chooseChallenge(options, results);
-    if (!challenge) {
+    var auth = ACME._chooseAuth(options, claim.challenges);
+    if (!auth) {
       // For example, wildcards require dns-01 and, if we don't have that, we have to bail
       return Promise.reject(new Error(
         "Server didn't offer any challenge we can handle for '" + options.domains.join() + "'."
       ));
     }
 
-    return ACME._challengeToAuth(me, options, results, challenge, false).then(function (auth) {
-      auths.push(auth);
-      return ACME._setChallenge(me, options, auth).then(setNext);
-    });
+    auths.push(auth);
+    return ACME._setChallenge(me, options, auth).then(setNext);
   }
 
   function checkNext() {
@@ -2358,6 +2354,7 @@ ACME._setChallengesAll = function (me, options) {
     }).then(checkNext);
   }
 
+  // Actually sets the challenge via ACME
   function challengeNext() {
     var auth = validAuths.shift();
     if (!auth) { return; }
@@ -2519,7 +2516,7 @@ ACME._createOrder = function (me, options) {
       if (me.debug) { console.debug('[ordered]', location); } // the account id url
       if (me.debug) { console.debug(resp); }
 
-      if (!options.authorizations) {
+      if (!order.authorizations) {
         return Promise.reject(new Error(
           "[acme-v2.js] authorizations were not fetched for '" + options.domains.join() + "':\n"
           + JSON.stringify(resp.body)
@@ -2528,25 +2525,24 @@ ACME._createOrder = function (me, options) {
 
       return order;
     }).then(function (order) {
-      var challenges = [];
+      var claims = [];
       if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); }
       var challengeAuths = order.authorizations.slice(0);
 
       function getNext() {
         var authUrl = challengeAuths.shift();
-        if (!authUrl) { return challenges; }
+        if (!authUrl) { return claims; }
 
-        return ACME._getChallenges(me, options, authUrl).then(function (results) {
-          // var domain = options.domains[i]; // results.identifier.value
-          challenges.push(results);
+        return ACME._getChallenges(me, options, authUrl).then(function (claim) {
+          // var domain = options.domains[i]; // claim.identifier.value
+          claims.push(claim);
           return getNext();
         });
       }
 
       return getNext().then(function () {
-        order.challenges = challenges;
+        order.claims = claims;
         options.order = order;
-        console.log('DEBUG 2 order (too much info for challenges?):', order);
         return order;
       });
     });
@@ -2684,10 +2680,12 @@ ACME.create = function create(me) {
     }
   };
   me.orders = {
-    create: function (options) {
+    // create + get challlenges
+    request: function (options) {
       return ACME._createOrder(me, options);
     }
-  , finalize: function (options) {
+    // set challenges, check challenges, finalize order, return order
+  , complete: function (options) {
       return ACME._finalizeOrder(me, options);
     }
   };
diff --git a/app/js/greenlock.js b/app/js/greenlock.js
index a80ab03..944b1cd 100644
--- a/app/js/greenlock.js
+++ b/app/js/greenlock.js
@@ -2,6 +2,7 @@
 'use strict';
 
   /*global URLSearchParams,Headers*/
+  var PromiseA = window.Promise;
   var VERSION = '2';
 	// ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
 	// ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
@@ -15,11 +16,32 @@
   var $qs = function (s) { return window.document.querySelector(s); };
   var $qsa = function (s) { return window.document.querySelectorAll(s); };
 	var acme;
-	var accountStuff;
   var info = {};
   var steps = {};
   var i = 1;
   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory';
+  var challenges = {
+    'http-01': {
+      set: function (auth) {
+        console.log('Chose http-01 for', auth.altname, auth);
+        return Promise.resolve();
+      }
+    , remove: function (auth) {
+        console.log('Can remove http-01 for', auth.altname, auth);
+        return Promise.resolve();
+      }
+    }
+  , 'dns-01': {
+      set: function (auth) {
+        console.log('Chose dns-01 for', auth.altname, auth);
+        return Promise.resolve();
+      }
+    , remove: function (auth) {
+        console.log('Can remove dns-01 for', auth.altname, auth);
+        return Promise.resolve();
+      }
+    }
+  };
 
   function updateApiType() {
     console.log("type updated");
@@ -59,8 +81,29 @@
     i += 1;
 
     return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) {
+      var ourfault = true;
       console.error(err);
-      window.alert("Something went wrong. It's our fault not yours. Please email aj@rootprojects.org and let him know that 'step " + j + "' failed.");
+      console.error(Object.keys(err));
+      if ('E_CHALLENGE_INVALID' === err.code) {
+        if ('dns-01' === err.type) {
+          ourfault = false;
+          window.alert("It looks like the DNS record you set for "
+            + err.altname + " was incorrect or did not propagate. "
+            + "The error message was '" + err.message + "'");
+        } else if ('http-01' === err.type) {
+          ourfault = false;
+          window.alert("It looks like the file you uploaded for "
+            + err.altname + " was incorrect or could not be downloaded. "
+            + "The error message was '" + err.message + "'");
+        }
+      }
+
+      if (ourfault) {
+        err.auth = undefined;
+        window.alert("Something went wrong. It's probably our fault, not yours."
+          + " Please email aj@rootprojects.org to let him know. The error message is: \n"
+          + JSON.stringify(err, null, 2));
+      }
     });
   }
 
@@ -178,7 +221,8 @@
     $qs('.js-acme-form-domains').hidden = false;
   };
   steps[1].submit = function () {
-    info.identifiers = $qs('.js-acme-domains').value.split(/\s*,\s*/g).map(function (hostname) {
+    info.domains = $qs('.js-acme-domains').value.replace(/https?:\/\//g, ' ').replace(/,/g, ' ').trim().split(/\s+/g);
+    info.identifiers = info.domains.map(function (hostname) {
       return { type: 'dns', value: hostname.toLowerCase().trim() };
     }).slice(0,1); //Disable multiple values for now.  We'll just take the first and work with it.
     info.identifiers.sort(function (a, b) {
@@ -204,12 +248,14 @@
   steps[2].submit = function () {
     var email = $qs('.js-acme-account-email').value.toLowerCase().trim();
 
+    info.email = email;
     info.contact = [ 'mailto:' + email ];
     info.agree = $qs('.js-acme-account-tos').checked;
     //info.greenlockAgree = $qs('.js-gl-tos').checked;
+    info.domains = info.identifiers.map(function (ident) { return ident.value; });
 
     // TODO ping with version and account creation
-    setTimeout(saveContact, 100, email, info.identifiers.map(function (ident) { return ident.value; }));
+    setTimeout(saveContact, 100, email, info.domains);
 
 		function checkTos(tos) {
 			if (info.agree) {
@@ -227,10 +273,10 @@
 			, accountKeypair: { privateKeyJwk: jwk }
 			}).then(function (account) {
 				console.log("account created result:", account);
-				accountStuff.account = account;
-				accountStuff.privateJwk = jwk;
-				accountStuff.email = email;
-				accountStuff.acme = acme; // TODO XXX remove
+				info.account = account;
+				info.privateJwk = jwk;
+				info.email = email;
+				info.acme = acme; // TODO XXX remove
 			}).catch(function (err) {
 				console.error("A bad thing happened:");
 				console.error(err);
@@ -241,116 +287,104 @@
 				});
 			});
 		}).then(function () {
-      var jwk = accountStuff.privateJwk;
-      var account = accountStuff.account;
+      var jwk = info.privateJwk;
+      var account = info.account;
 
-			return acme.orders.create({
+			return acme.orders.request({
 			  account: account
 			, accountKeypair: { privateKeyJwk: jwk }
-			, identifiers: info.identifiers
+			, domains: info.domains
+      , challenges: challenges
 			}).then(function (order) {
-				return acme.orders.create({
-					signedOrder: signedOrder
-				}).then(function (order) {
-					accountStuff.order = order;
-          var claims = order.challenges;
-          console.log('claims:');
-          console.log(claims);
+        info.order = order;
 
-          var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] };
-          info.challenges = obj;
-          var map = {
-            'http-01': '.js-acme-verification-http-01'
-          , 'dns-01': '.js-acme-verification-dns-01'
-          , 'wildcard': '.js-acme-verification-wildcard'
-          };
-          options.challengePriority = [ 'http-01', 'dns-01' ];
+        var claims = order.claims;
+        console.log('claims:');
+        console.log(claims);
 
-          // TODO make Promise-friendly
-          return PromiseA.all(claims.map(function (claim) {
-            var hostname = claim.identifier.value;
-            return PromiseA.all(claim.challenges.map(function (c) {
-              var keyAuth = BACME.challenges['http-01']({
-                token: c.token
-              , thumbprint: thumbprint
-              , challengeDomain: hostname
-              });
-              return BACME.challenges['dns-01']({
-                keyAuth: keyAuth.value
-              , challengeDomain: hostname
-              }).then(function (dnsAuth) {
-                var data = {
-                  type: c.type
-                , hostname: hostname
-                , url: c.url
-                , token: c.token
-                , keyAuthorization: keyAuth
-                , httpPath: keyAuth.path
-                , httpAuth: keyAuth.value
-                , dnsType: dnsAuth.type
-                , dnsHost: dnsAuth.host
-                , dnsAnswer: dnsAuth.answer
-                };
+        var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] };
+        info.challenges = obj;
+        /*
+        var map = {
+          'http-01': '.js-acme-verification-http-01'
+        , 'dns-01': '.js-acme-verification-dns-01'
+        , 'wildcard': '.js-acme-verification-wildcard'
+        };
+        */
 
-                console.log('');
-                console.log('CHALLENGE');
-                console.log(claim);
-                console.log(c);
-                console.log(data);
-                console.log('');
+        claims.forEach(function (claim) {
+          console.log("Challenge (claim):");
+          console.log(claim);
+          var hostname = claim.identifier.value;
+          claim.challenges.forEach(function (c) {
+            var auth = c;
+            var data = {
+              type: c.type
+            , hostname: hostname
+            , url: c.url
+            , token: c.token
+            , httpPath: auth.challengeUrl
+            , httpAuth: auth.keyAuthorization
+            , dnsType: 'TXT'
+            , dnsHost: auth.dnsHost
+            , dnsAnswer: auth.keyAuthorizationDigest
+            };
 
-                if (claim.wildcard) {
-                  obj.wildcard.push(data);
-                  let verification = $qs(".js-acme-verification-wildcard");
-                  verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname;
-                  verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost;
-                  verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer;
+            console.log('');
+            console.log('CHALLENGE');
+            console.log(claim);
+            console.log(c);
+            console.log(data);
+            console.log('');
 
-                } else if(obj[data.type]) {
+            var verification;
+            if (claim.wildcard) {
+              obj.wildcard.push(data);
+              verification = $qs(".js-acme-verification-wildcard");
+              verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname;
+              verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost;
+              verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer;
 
-                  obj[data.type].push(data);
+            } else if(obj[data.type]) {
 
-                  if ('dns-01' === data.type) {
-                    let verification = $qs(".js-acme-verification-dns-01");
-                    verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname;
-                    verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost;
-                    verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer;
-                  } else if ('http-01' === data.type) {
-                    $qs(".js-acme-ver-file-location").innerHTML = data.httpPath.split("/").slice(-1);
-                    $qs(".js-acme-ver-content").innerHTML = data.httpAuth;
-                    $qs(".js-acme-ver-uri").innerHTML = data.httpPath;
-                    $qs(".js-download-verify-link").href =
-                      "data:text/octet-stream;base64," + window.btoa(data.httpAuth);
-                    $qs(".js-download-verify-link").download = data.httpPath.split("/").slice(-1);
-                  }
-                }
+              obj[data.type].push(data);
 
-              });
-
-            }));
-          })).then(function () {
-
-            // hide wildcard if no wildcard
-            // hide http-01 and dns-01 if only wildcard
-            if (!obj.wildcard.length) {
-              $qs('.js-acme-wildcard-challenges').hidden = true;
+              if ('dns-01' === data.type) {
+                verification = $qs(".js-acme-verification-dns-01");
+                verification.querySelector(".js-acme-ver-hostname").innerHTML = data.hostname;
+                verification.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost;
+                verification.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer;
+              } else if ('http-01' === data.type) {
+                $qs(".js-acme-ver-file-location").innerHTML = data.httpPath.split("/").slice(-1);
+                $qs(".js-acme-ver-content").innerHTML = data.httpAuth;
+                $qs(".js-acme-ver-uri").innerHTML = data.httpPath;
+                $qs(".js-download-verify-link").href =
+                  "data:text/octet-stream;base64," + window.btoa(data.httpAuth);
+                $qs(".js-download-verify-link").download = data.httpPath.split("/").slice(-1);
+              }
             }
-            if (!obj['http-01'].length) {
-              $qs('.js-acme-challenges').hidden = true;
-            }
-
-            updateChallengeType();
-
-            console.log("MAGIC STEP NUMBER in 2 is:", i);
-            steps[i]();
           });
-
         });
+
+        // hide wildcard if no wildcard
+        // hide http-01 and dns-01 if only wildcard
+        if (!obj.wildcard.length) {
+          $qs('.js-acme-wildcard-challenges').hidden = true;
+        }
+        if (!obj['http-01'].length) {
+          $qs('.js-acme-challenges').hidden = true;
+        }
+
+        updateChallengeType();
+
+        console.log("MAGIC STEP NUMBER in 2 is:", i);
+        steps[i]();
       });
     }).catch(function (err) {
       console.error('Step \'\' Error:');
       console.error(err, err.stack);
-      window.alert("An error happened at Step " + i + ", but it's not your fault. Email aj@rootprojects.org and let him know.");
+      window.alert("An error happened (but it's not your fault)."
+        + " Email aj@rootprojects.org to let him know that 'order and get challenges' failed.");
     });
   };
 
@@ -360,58 +394,44 @@
     $qs('.js-acme-form-challenges').hidden = false;
   };
   steps[3].submit = function () {
-    options.challengeTypes = [ 'dns-01' ];
+    var challengePriority = [ 'dns-01' ];
     if ('http-01' === $qs('.js-acme-challenge-type:checked').value) {
-      options.challengeTypes.unshift('http-01');
+      challengePriority.unshift('http-01');
     }
-    console.log('primary challenge type is:', options.challengeTypes[0]);
+    console.log('primary challenge type is:', challengePriority[0]);
 
-    return getAccountKeypair(email).then(function (jwk) {
+    steps[i]();
+    return getAccountKeypair(info.email).then(function (jwk) {
       // for now just show the next page immediately (its a spinner)
       // TODO put a test challenge in the list
       // TODO warn about wait-time if DNS
-      steps[i]();
-		  return getServerKeypair().then(function () {
-        return acme.orders.finalize({
-          account: accountStuff.account
+		  return getServerKeypair().then(function (serverJwk) {
+        return acme.orders.complete({
+          account: info.account
         , accountKeypair: { privateKeyJwk: jwk }
-        , order: accountStuff.order
-        , domainKeypair: 'TODO'
-        });
-      }).then(function (certs) {
-        console.log('WINNING!');
-        console.log(certs);
-        $qs('#js-fullchain').innerHTML = certs;
-        $qs("#js-download-fullchain-link").href =
-          "data:text/octet-stream;base64," + window.btoa(certs);
+        , order: info.order
+        , domains: info.domains
+        , domainKeypair: { privateKeyJwk: serverJwk }
+        , challengePriority: challengePriority
+        , challenges: challenges
+        }).then(function (certs) {
+          return Keypairs.export({ jwk: serverJwk }).then(function (keyPem) {
+            console.log('WINNING!');
+            console.log(certs);
+            $qs('#js-fullchain').innerHTML = [
+              certs.cert.trim() + "\n"
+            , certs.chain + "\n"
+            ].join("\n");
+            $qs("#js-download-fullchain-link").href =
+              "data:text/octet-stream;base64," + window.btoa(certs);
 
-        var wcOpts;
-        var pemName;
-        if (/^R/.test(info.serverJwk.kty)) {
-          pemName = 'RSA';
-          wcOpts = { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } };
-        } else {
-          pemName = 'EC';
-          wcOpts = { name: "ECDSA", namedCurve: "P-256" };
-        }
-        return crypto.subtle.importKey(
-          "jwk"
-        , info.serverJwk
-        , wcOpts
-        , true
-        , ["sign"]
-        ).then(function (privateKey) {
-          return window.crypto.subtle.exportKey("pkcs8", privateKey);
-        }).then (function (keydata) {
-          var pem = spkiToPEM(keydata, pemName);
-          $qs('#js-privkey').innerHTML = pem;
-          $qs("#js-download-privkey-link").href =
-            "data:text/octet-stream;base64," + window.btoa(pem);
-          steps[i]();
+            $qs('#js-privkey').innerHTML = keyPem;
+            $qs("#js-download-privkey-link").href =
+              "data:text/octet-stream;base64," + window.btoa(keyPem);
+            submitForm();
+          });
         });
       });
-    }).then(function () {
-      return submitForm();
     });
   };
 
@@ -424,11 +444,7 @@
   steps[4].submit = function () {
     console.log('Congrats! Auto advancing...');
 
-
-    }).catch(function (err) {
-      console.error(err.toString());
-      window.alert("An error happened in the final step, but it's not your fault. Email aj@rootprojects.org and let him know.");
-    });
+    window.alert("An error happened in the final step, but it's not your fault. Email aj@rootprojects.org and let him know.");
   };
 
   steps[5] = function () {
@@ -436,6 +452,8 @@
     hideForms();
     $qs('.js-acme-form-download').hidden = false;
   };
+
+  // The kickoff
   steps[1]();
 
   var params = new URLSearchParams(window.location.search);
@@ -481,8 +499,8 @@
       return true;
     }
 
-    return testRsaSupport().then(function () {
-      console.info('[crypto] RSA is supported');
+    return testEcdsaSupport().then(function () {
+      console.info('[crypto] ECDSA is supported');
     }).catch(function (err) {
       console.error('[crypto] could not use either RSA nor EC.');
       console.error(err);
diff --git a/index.html b/index.html
index 18b177e..6874cc6 100644
--- a/index.html
+++ b/index.html
@@ -23,15 +23,10 @@
     
     
     
-
     
-    
-    
-    
-    
-    
-    
-    
+    
+    
+