How to capture shipping address with Stripe Checkout

WARNING: THIS USES DEPRECATED FEATURES. IT MAY STOP WORKING AT ANY TIME

At KoalaSafe, we use Stripe Checkout to accept payments. It's a well tested, mobile optimized payments workflow. For some reason, the Stripe team deprecated the feature to capture shipping addresses. The good news is, it still works, it's just not documented.

The Stripe Docs have the following:

$('#customButton').on('click', function(e) {
    // Open Checkout with further options
    handler.open({
      name: 'KoalaSafe',
      description: '2 widgets',
      currency: "aud",
      amount: 2000
    });
    e.preventDefault();
  });

Which pops up the basic payment window:

Checkout without billing

To capture the shipping address, you need to add the following two lines:

 billingAddress: true,
 shippingAddress: true

giving you:

      $("[data-buy]").on('click', function(e) {
        // Open Checkout with further options
        handler.open({
          name: 'KoalaSafe',
          description: '2 widgets',
          currency: "aud",
          amount: 2000,
          //Capture addresses
          billingAddress: true,
          shippingAddress: true
        });
        e.preventDefault();
      });

That will configure the popup to capture the addresses.

Checkout with billing

The problem is, when you make a charge using the supplied token, it won't actually include the shipping address on the server. It's a bug in Stripe (in as much as a deprecated feature not working is actually a bug...)

You can still access it however. Change the handler code from the stripe docs from:

    var handler = StripeCheckout.configure({
    key: 'pk_test_WWK97fKMtgUPwTSbU77xxxx',
    image: 'https://s3.amazonaws.com/stripe-uploads/icon100x100.png',
    token: function(token) {
      // Use the token to create the charge with a server-side script.
      // You can access the token ID with token.id
    }
  });

to

    var handler = StripeCheckout.configure({
    key: 'pk_test_WWK97fKMtgUPwTSbU77xxxx',
    image: 'https://s3.amazonaws.com/stripe-uploads/icon100x100.png',
    token: function(token, addresses) {
      // the billing and shipping address are present in the addresses object.
    }
  });

the addresses param looks like:

{
    "billing_name":"Steve",
    "billing_address_line1":"Suite 145, National Innovation Centre",
    "billing_address_zip":"2015",
    "billing_address_state":"NSW",
    "billing_address_city":"Eveleigh",
    "billing_address_country":"Australia",
    "billing_address_country_code":"AU",
    "shipping_name":"Steve",
    "shipping_address_line1":"1 Station St",
    "shipping_address_zip":"2000",
    "shipping_address_state":"NSW",
    "shipping_address_city":"Sydney",
    "shipping_address_country":"Australia",
    "shipping_address_country_code":"AU"
    }

meaning you can submit the shipping address to your server-side page where actually create the charge. For example:

    var handler = StripeCheckout.configure({
    key: 'pk_test_WWK97fKMtgUPwTSbU77xxxx',
    image: 'https://s3.amazonaws.com/stripe-uploads/icon100x100.png',
    token: function(token, addresses) {
    // the billing and shipping address are present in the addresses object.
    var form = $('#stripeForm');
    form.append($('<input type="hidden" name="stripeToken" />').val(token.id));
    form.append($('<input type="hidden" name="stripeEmail" />').val(token.email));
    form.append($('<input type="hidden" name="stripeShippingName" />').val(addresses.shipping_name));
    form.append($('<input type="hidden" name="stripeShippingAddressLine1" />').val(addresses.shipping_address_line1));
    form.append($('<input type="hidden" name="stripeShippingAddressZip" />').val(addresses.shipping_address_zip));
    form.append($('<input type="hidden" name="stripeShippingAddressState" />').val(addresses.shipping_address_state));
    form.append($('<input type="hidden" name="stripeShippingAddressCity" />').val(addresses.shipping_address_city));
    form.append($('<input type="hidden" name="stripeShippingAddressCountry" />').val(addresses.shipping_address_country));
    form.get(0).submit();
    }
  });

You'll now have access to the shipping address at the time you create the charge. As I said, simply creating the charge using the token won't include the shipping address, so you have to do it as a separate step. Here's how:

First, create the charge:

 $charge = \Stripe\Charge::create(array(
  "amount" => 9000, // amount in cents, again
  "currency" => "usd",
  "source" => $token,
  "description" => "KoalaSafe Router ($90.00 USD)",
  "receipt_email" => $email
  )
);

Second, update it with the shipping address you submitted to the form:

    $name = htmlspecialchars($_POST['stripeShippingName']);
    $line1 = htmlspecialchars($_POST['stripeShippingAddressLine1']);
    $zip = htmlspecialchars($_POST['stripeShippingAddressZip']);
    $state = htmlspecialchars($_POST['stripeShippingAddressState']);
    $city = htmlspecialchars($_POST['stripeShippingAddressCity']);
    $country = htmlspecialchars($_POST['stripeShippingAddressCountry']);

    $address = array(
      "line1" => $line1,
      "line2" => $line2,
      "city" => $city,
      "state" => $state,
      "postal_code" => $zip,
      "country" => $country
    );

    $shippingDetails = array(
      "name" => $name,
      "address" => $address
    );

    $charge->shipping = $shippingDetails;
    $charge->save();

Voila, you now have a shipping address to go with your order. The shipping address will show up as an amendment in your logs and the charge.shipping property will now be filled out

Stripe log showing shipping amendment