2
votes

I'm trying to submit a stripeToken to charge.php without ajax but I'm getting following error:

Fatal error: Uncaught exception 'Stripe\Error\InvalidRequest' with message 'Invalid source object: must be a dictionary or a non-empty string.

I believe it has something to do with the token not being appended to input. I have tried several different ways without success.

minimum markup

<form action="charge.php" method="POST" id="payment-form">

//rest of the form

<input id="stripeToken" type="hidden" name="stripeToken" value="">
<input type="submit" class="submit" value="Submit Payment">
</form>

javascript

Stripe.setPublishableKey('pk_test_PUBLISHABLE'); //publishable key
var $form = $('#payment-form'); // declare form variable
$(function() {

  $form.submit(function(event) {
    // Disable the submit button to prevent repeated clicks:
    $form.find('.submit').prop('disabled', true);

    // Request a token from Stripe:
    Stripe.card.createToken($form, stripeResponseHandler);

    // Prevent the form from being submitted:
    return false;
  });
});

function stripeResponseHandler(status, response) {
  if (response.error) {

    // display errors in form

 } else {

    // Get the token
    var token = response.id;

    // Insert the token in the form:
    document.getElementById('stripeToken').value=token;
   // $form.append($('<input type="hidden" name="stripeToken">').val(token));

    // Submit the form:
    $form.get(0).submit();
  }
};

charge.php

require_once('config.php');

\Stripe\Stripe::setApiKey($mysk_key);

$stripeToken=$_POST['stripeToken'];//line of the error

  $customer = \Stripe\Customer::create(array(
    'email' => $email,
    'source'  =>$stripeToken, //line of the error
    'plan' => $myplan
  ));

UPDATE config.php

<?php

    require_once('vendor/init.php');

    $test_sk="sk_test_xxx";
    $live_sk="sk_live_xxx";
    $mykey_sk=$test_sk;

    $test_pk="pk_test_xxx";
    $live_pk="pk_live_xxx";
    $mykey_pk=$test_pk;

    $stripe = array(
      "secret_key"      =>$mykey_sk,
      "publishable_key" =>$mykey_pk
    );

    \Stripe\Stripe::setApiKey($stripe['secret_key']);
    ?>
3
Check your HTML to make sure you don't have duplicate id="stripeToken" elements. Your code looks identical to mine, which works fine. - Barmar
Check the Network tab in DevTools to see what parameters you're sending in the submit request. - Barmar

3 Answers

0
votes

Your using jQuery already so you might as well extract the token and assign it to the input using jQuery.

Make sure response.id is not null, than edit the value using jQuery.

$('#stripeToken').val(token);

The stripe api will complain if you're passing an empty string so it's important that you make sure your response.id is not an empty string.

0
votes

You should review the docs on how to get a token from Stripe:

https://stripe.com/docs/stripe.js/v2#card-createToken

You can pass a form element to createToken, but all your form fields would need a data-stripe attribute, and the value of the data-stripe attribute one of the keys they expect. Otherwise, you need to pass in the card parameters, like their example:

{
  number: $('.card-number').val(),
  cvc: $('.card-cvc').val(),
  exp_month: $('.card-expiry-month').val(),
  exp_year: $('.card-expiry-year').val(),
  address_zip: $('.address_zip').val() 
}

Since you didn't show us the rest of the form, we must assume you have data-stripe attributes, like:

<input type="text" data-stripe="number" id="number" /> 
<input type="text" data-stripe="cvc" id="cvc" /> 
<input type="text" data-stripe="exp_month" id="exp_month" /> 
<!-- and so on ... ->

Then createToken will attempt to get a token, and use the response handler you provided. You should be checking for response.error in that handler:

stripeResponseHandler: function( status, response ){
    if( response.error ){
        alert('ERROR');
    }else{
        // Go ahead and post to the server ...
        // Or just output to the console to verify the response ...
        console.log( response );
    }
}

I'm assuming that response.id is acceptable to use, but in my particular code (that is working), I used response['id'] to get the stripe token value.

I use AJAX to POST to my server payment processing, but I suppose the way you are doing it would work. Just make sure to verify the contents of $_POST.

0
votes

Make sure that you have included Stripejs like described here

Here is the test file created. You can modify it according to your need. I used ajax for process the payment.

index.php

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>Stripe Test</title>
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<style>
* {
  font-family: "Helvetica Neue", Helvetica;
  font-size: 15px;
  font-variant: normal;
  padding: 0;
  margin: 0;
}

html {
  height: 100%;
}

body {
  background: #E6EBF1;
  align-items: center;
  min-height: 100%;
  display: flex;
  width: 100%;
}

form {
  width: 480px;
  margin: 20px auto;
}

.group {
  background: white;
  box-shadow: 0 7px 14px 0 rgba(49,49,93,0.10),
              0 3px 6px 0 rgba(0,0,0,0.08);
  border-radius: 4px;
  margin-bottom: 20px;
}

label {
  position: relative;
  color: #8898AA;
  font-weight: 300;
  height: 40px;
  line-height: 40px;
  margin-left: 20px;
  display: block;
}

.group label:not(:last-child) {
  border-bottom: 1px solid #F0F5FA;
}

label > span {
  width: 20%;
  text-align: right;
  float: left;
}

.field {
  background: transparent;
  font-weight: 300;
  border: 0;
  color: #31325F;
  outline: none;
  padding-right: 10px;
  padding-left: 10px;
  cursor: text;
  width: 70%;
  height: 40px;
  float: right;
}

.field::-webkit-input-placeholder { color: #CFD7E0; }
.field::-moz-placeholder { color: #CFD7E0; }
.field:-ms-input-placeholder { color: #CFD7E0; }

button {
  float: left;
  display: block;
  background: #666EE8;
  color: white;
  box-shadow: 0 7px 14px 0 rgba(49,49,93,0.10),
              0 3px 6px 0 rgba(0,0,0,0.08);
  border-radius: 4px;
  border: 0;
  margin-top: 20px;
  font-size: 15px;
  font-weight: 400;
  width: 100%;
  height: 40px;
  line-height: 38px;
  outline: none;
}

button:focus {
  background: #555ABF;
}

button:active {
  background: #43458B;
}

.outcome {
  float: left;
  width: 100%;
  padding-top: 8px;
  min-height: 24px;
  text-align: center;
}

.success, .error {
  display: none;
  font-size: 13px;
}

.success.visible, .error.visible {
  display: inline;
}

.error {
  color: #E4584C;
}

.success {
  color: #666EE8;
}

.success .token {
  font-weight: 500;
  font-size: 13px;
}
</style>
<script src="https://js.stripe.com/v3/"></script>
<form>
  <div class="group">
    <label>
      <span>Name</span>
      <input id="name" name="name" class="field" placeholder="" />
    </label>
     <label>
      <span>Email</span>
      <input name="email" id="email" class="field" placeholder="" />
    </label>
    <label>
      <span>Phone</span>
      <input class="field" placeholder="(123) 456-7890" id="phone" type="phone" />
    </label>
  </div>
  <div class="group">
    <label>
      <span>Card</span>
      <div id="card-element" class="field"></div>
    </label>
  </div>
  <button type="submit">Pay $25</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('YOUR-STRIPE-TOKEN');
var elements = stripe.elements();

var card = elements.create('card', {
  hidePostalCode:true, 
  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
    var stripe_token = result.token.id;
    successElement.querySelector('.token').textContent = stripe_token;
    successElement.classList.add('visible');

    // AJAX
    $.post( "do_payment.php", { stripeToken: stripe_token, name: $('#name').val(), "email":$('#email').val(), "phone": $('#phone').val() })
    .done(function( data ) {
      console.log(data);
    });



  } 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=name]').value,
  };
  stripe.createToken(card, extraDetails).then(setOutcome);
});
</script>
</body>
</html>

For ajax process create a php file do_payment.php

<?php
    require_once('vendor/init.php');

    $stripe = array(
        "secret_key" => "YOUR STRIPE SECRET KEY",
        "publishable_key" => "YOUR STRIPE PUBLISHABLE KEY"
    );

    \Stripe\Stripe::setApiKey($stripe['secret_key']);

    $token = $_POST['stripeToken'];

    $customer = \Stripe\Customer::create(array(
        'email' => '[email protected]',
        'source' => $token
    ));


    try {

        $charge = \Stripe\Charge::create(array(
            'customer' => $customer->id,
            'amount' => 2500, //Charge $25 See https://stripe.com/docs/currencies#zero-decimal
            'currency' => 'usd'
        ));
    } catch (RateLimit $e) {
        $body = $e->getJsonBody();
        return ['status' => 'failed', 'res' => $body['error']];
    } catch (InvalidRequest $e) {
        $body = $e->getJsonBody();
        return ['status' => 'failed', 'res' => $body['error']];
    } catch (Authentication $e) {
        $body = $e->getJsonBody();
        return ['status' => 'failed', 'res' => $body['error']];
    } catch (ApiConnection $e) {
        $body = $e->getJsonBody();
        return ['status' => 'failed', 'res' => $body['error']];
    } catch (Base $e) {
        $body = $e->getJsonBody();
        return ['status' => 'failed', 'res' => $body['error']];
    } catch (Exception $e) {
        return ['status' => 'failed', 'res' => json_encode($e->getMessage())];
    }
    echo '<h1>Successfully charged $50.00!</h1>';

    ?>

For getting test credentials see this document