0

I am a beginner, implementing stripe to get payments handled on my django application.

I am using dj-stripe and can create customers and add a plan through this set-up.

I want to use stripe.js to create the token so I can use the nicely formatted card-submission forms from stripe itself (https://stripe.com/docs/elements/examples) . I don't want to use the django forms to collect card details, as these are hard to change on layout and I would rather not handle the credit card details with my django application myself from security perspective.

I managed to get the stripe javascript form working, and it delivers an output in the form of a "token". I should pass the javascript output to my django application and attach this to my customer.

I figured out this should happen with some sort of jQuery / Ajax set-up but as I am a beginner, I don't know how to get started. I found this but don't know how to adapt it to my needs.

My current set-up prints the output token where in payment.html Success! Your Stripe token is <span class="token"></span> is coded. I tried referring to token inside a jQuery script like this but the token got the error could not be read inside my browser:

My jQuery/AJAX script attempt, earlier added to payment.html but not shown in payment.html below:

<script type="text/javascript">
$(document).ready(function() {
    $.ajax({
        method: 'POST',
        url: '/payments/settings/',
        data: {'yourJavaScriptArrayKey': yourJavaScriptArray},
        success: function (data) {
             //this gets called when server returns an OK response
             alert("it worked!");
        },
        error: function (data) {
             alert("it didnt work");
        }
    });
});
</script>

payment.html:

<style> some styles regarding layout of the js-form</style>

<script src="https://js.stripe.com/v3/"></script>

<form>
  <div class="group">
    <label>
      <span>Name</span>
      <input name="cardholder-name" class="field" placeholder="Jane Doe" />
    </label>
    <label>
      <span>Phone</span>
      <input class="field" placeholder="(123) 456-7890" type="tel" />
    </label>
  </div>
  <div class="group">
    <label>
      <span>Card</span>
      <div id="card-element" class="field"></div>
    </label>
  </div>
  <button type="submit">Add Card</button>
  <div class="outcome">
    <div class="error" role="alert"></div>
    <div class="success">
      Success! Your Stripe token is <span class="token"></span>
    </div>
  </div>
</form>

<script type="text/javascript">

var stripe = Stripe('pk_test_xxxxxxxxxxxxxxxxxxxxxxxx');
var elements = stripe.elements();

var card = elements.create('card', {
  style: {
    base: {
      iconColor: '#666EE8',
      color: '#31325F',
      lineHeight: '40px',
      fontWeight: 300,
      fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
      fontSize: '15px',

      '::placeholder': {
        color: '#CFD7E0',
      },
    },
  }
});
card.mount('#card-element');

function setOutcome(result) {
  var successElement = document.querySelector('.success');
  var errorElement = document.querySelector('.error');
  successElement.classList.remove('visible');
  errorElement.classList.remove('visible');

  if (result.token) {
    // Use the token to create a charge or a customer
    // https://stripe.com/docs/charges
    successElement.querySelector('.token').textContent = result.token.id;
    successElement.classList.add('visible');
  } else if (result.error) {
    errorElement.textContent = result.error.message;
    errorElement.classList.add('visible');
  }
}

card.on('change', function(event) {
  setOutcome(event);
});

document.querySelector('form').addEventListener('submit', function(e) {
  e.preventDefault();
  var form = document.querySelector('form');
  var extraDetails = {
    name: form.querySelector('input[name=cardholder-name]').value,
  };
  stripe.createToken(card, extraDetails).then(setOutcome);
});

</script>
<script>
// I am missing an AJAX/jQUERY script to pass the outcome of the javascript generated token
</script>

views.py

def payment(request):
    template = get_template('payment.html')
    context = {

    }
    # HERE THERE SHOULD BE SOME AJAX CALL
    return HttpResponse(template.render(context,request))

How should my AJAX/jQuery script look inside my payment.html so it accesses the outputted token and how can I call this token inside my views.py?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
radzia2
  • 339
  • 4
  • 19

1 Answers1

1

First, I don't like how you are using setOutcome twice, once for the change event for the card, and then again when you attempt to get the token from stripe. Split those things up:

stripe.createToken(card, extraDetails).then(function(result) {
    if(result.error && result.error.message){
        // Do something with the errors
    }else{
        // Charge the card
        doCardCharge(result.token.id);
    }
});

Make your server response a JSON type object. Your doCardCharge function then looks like this:

function doCardCharge( stripeToken ){
    $.ajax({
        type: 'post',
        data: {
            // Add any other fields you need to here
            'name': $('input[name="cardholder-name"]').val(),
            'stripeToken':  stripeToken
        },
        cache: false,
        url: '/payments/settings/',
        dataType: 'json',
        success: function(data){
            if( data.server_errors ){
                // There could be errors on the server
            }else if( data.status == 'success'){
                // The charge was successful
            }else if( data.errors ){
                // There were validation errors
            }else{
                // There was some unknown error
            }
        },
        error: function(){
            // There was some sort of network error, or error on the server
        }
    });
}

And that assumes that you have some sort of output buffering to catch errors if you want to display them as data.server_errors. If the charge was successful, you're passing back "success" as the value of data.status. If the server caught a validation error, such as a person's address = "2oi3n5o2i3n5o2i", then you are dealing with those errors because you set them as the value of data.errors.

Notice also that I declare a dataType for the ajax response, and that's because jQuery tries to intelligently guess the response type, but I found in many cases that it's not so intelligent.

In Django, when you need to get posted data in your view, check this for how to get it: Django - taking values from POST request

In Django, you can set the JSON type response quite easily. Take a look at this Stack Overflow page for an example: Creating a JSON response using Django and Python

Finally, if you can't figure out what's going on, sometimes it's helpful to look at the network tab of your developer's console (in your web browser). It will a lot of times offer the raw response.

Brian Gottier
  • 4,522
  • 3
  • 21
  • 37
  • Got the form loaded and can see the tokens being created in the Network environment so definitely took steps in the good direction. Now struggling with the CSRF token not being accepted by Django due to using from not created with forms.py but with the JS / HTML code. Was looking into `csrf_exempt` but i don't know if this is the good approach – radzia2 Sep 11 '17 at 13:44
  • [link](https://stackoverflow.com/questions/16653962/passing-csrf-token-to-stripe) points out that exempting the csrf token is the right way to go. set-up works now with your feedback. happy man. – radzia2 Sep 11 '17 at 14:14
  • I would start here and decide if CSRF is something that is a concern in this situation: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) – Brian Gottier Sep 11 '17 at 14:15