[Stripe] Checkout custom form for 3D Secure (sources API)

by VeryGoodFiddle

HTML

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.6/featherlight.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.6/featherlight.min.js"></script>
<script src="https://js.stripe.com/v2/"></script>
<script src="https://checkout.stripe.com/checkout.js"></script>
<div class="container">

  <div class="page-header">
    <h1>Stripe 3D Secure demo</h1>
    <p class="lead">
      This page demonstrates Stripe's sources API for 3D Secure payments.
    </p>
  </div>

  <p id="result" class="bg-info"></p>

  <form class="form-horizontal" id="charge-form">
    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <button id="customButton" class="btn btn-default">Purchase with 3D Secure</button>
      </div>
    </div>
  </form>

  <div id="processing">
    <p class="text-center">Processing...</p>
  </div>

  <div id="modal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-body" id="iframe-container">
        </div>
      </div>
    </div>
  </div>

  <h2>Test Cards</h2>
  <table id="test-card-numbers" class="table">
    <tr>
      <td>
        4242424242424242
      </td>
      <td>
        3D Secure authentication not supported, but will charge successfully
      </td>
    </tr>
    <tr>
      <td>
        4000000000003055
      </td>
      <td>
        3D Secure authentication supported, will go through the full 3D Secure flow
      </td>
    </tr>
    <tr>
      <td>
        4000000000003063
      </td>
      <td>
        3D Secure authentication required, charge will be declined without it
      </td>
    </tr>
  </table>
</div>

CSS

#processing {
  display: none;
}

#result {
  padding: 15px;
  display: none;
}

#iframe-container iframe {
  width: 100%;
  height: 400px;
}

JavaScript

var stripePublishableKey = 'pk_test_P5VzMOKf8Mg3vVLlonXAFkEM';
var amount = 100;
var currency = 'eur';

Stripe.setPublishableKey(stripePublishableKey);

// Create Checkout's handler
var handler = StripeCheckout.configure({
  key: stripePublishableKey,
  image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
  locale: 'auto',
  allowRememberMe: false,
  token: function(token) {
    // use Checkout's card token to create a card source
    Stripe.source.create({
      type: 'card',
      token: token.id
    }, stripeCardResponseHandler);

    displayProcessing();
  }
});

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

// Close Checkout on page navigation:
$(window).on('popstate', function() {
  handler.close();
});

function displayProcessing() {
  document.getElementById("processing").style.display = 'block';

  document.getElementById("charge-form").style.display = 'none';
  document.getElementById("result").style.display = 'none';
}

function displayResult(resultText) {
  document.getElementById("processing").style.display = 'none';

  document.getElementById("charge-form").style.display = 'block';
  document.getElementById("result").style.display = 'block';
  document.getElementById("result").innerText = resultText;
}

function stripeCardResponseHandler(status, response) {
  if (response.error) {
    var message = response.error.message;
    displayResult("Unexpected card source creation response status: " + status + ". Error: " + message);
    return;
  }

  // check if the card supports 3DS
  if (response.card.three_d_secure == 'not_supported') {
    displayResult("This card does not support 3D Secure.");
    return;
  }
  
  // since we're going to use an iframe in this example, the
  // return URL will only be displayed briefly before the iframe
  // is...