Samuel Elh Blog

WordPress, PHP, Python and JavaScript tutorials and snippets

Integrate credit card payments with Stripe

In this quick tip tutorial we will learn about using Stripe API to receive payments from credit cards and charging users and verifying the payments.

Getting Started

We’ll create a folder for our project and use it. For the sake of this tutorial, I’m naming it to stripe-payments.

Right after that, we’ll need to require some dependencies. We’ll need the latest PHP SDK for Stripe from their Github repository https://github.com/stripe/stripe-php and load it.

  • If you are using composer:

To autoload this library with composer just run the following throw the CLI (considering you’re in stripe-payments directory):

composer require stripe/stripe-php
  • If you don’t use composer:

You know you’re missing out if you don’t use composer yet, but you can download a release from Github for Stripe PHP SDK and load it in your project:

Go to Stripe PHP releases, click the latest tag (at this moment v4.9.1), and download code. Unpack it to the project folder which in my case is stripe-payments.

Loading Stripe library and using it

We’ll keep things simple enough and use 2 static PHP files to do the job:

  • index.php where we will place the payment button from Stripe checkout
  • charge.php which will be used as the form processor to verify the front-end submitted data and make actual charges.

So, go ahead and create these 2 files. Next we’ll load Stripe library into index.php file:

  • If you use composer:

index.php

<?php

require __DIR__ . '/vendor/autoload.php';
  • If you don’t use composer:

index.php

<?php

require __DIR__ . '/stripe-php-4.9.1/init.php';

Just make sure you got the directory name right, you might be having a different release than “stripe-php-4.9.1”.

Great, now if we navigate to your project in the browser http://127.0.0.1/stripe-payments/ you should probably see a blank screen. If so, then you’re at it, otherwise if you get a 500 error page or something then make sure you have the correct files required. Debugging is always necessary so let’s add some code to the top of our index.php and charge.php files ( this is totally optional and you should not leave debugging mode on when in production):

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

# ... reset of code

Now if you have any errors they’ll be printed wide and clear in the browser, in order for you to debug them properly.

Basic configuration

Let’s now place some constants into the top of our index.php in order to configure the process properly.

  • STRIPE_PUBLIC_KEY: Your API public key which can be obtained from the dashboard dashboard.stripe.com
  • STRIPE_SECRET_KEY: Your API secret, can be obtained from the same dashboard
  • STRIPE_PRICE: The price we are charging here. It should be an integer and in cents (pennies) so multiply it by 100 ($50 becomes 5000 and £0.5 becomes 50)
  • CURRENCY_CODE: The ISO code for the currency you are charging for. USD for the US dollar $, EUR for euro €, GBP for £ the British pound, you can find a list of currency codes and their symbols online.
  • USER_EMAIL: Optional, if you already have a user signed in or you only allow logged in users to make payments, then place here the current user’s email address otherwise the user will be free to choose an email address in order to make a payment.

So we’ll add them as constants into our index.php file (from now on, index.php will handle every request, even calling charge.php when form data has been passed):

index.php

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require __DIR__ . '/vendor/autoload.php';

// API public key
define ( 'STRIPE_PUBLIC_KEY', 'pk_test_6pRNASCoBOKtIshFeQd4XMUh' );

// API secret key
define ( 'STRIPE_SECRET_KEY', 'sk_test_BQokikJOvBiI2HlWgH4olfQ2' );

// stripe amount
define ( 'STRIPE_PRICE', 100 ); // that's $1

// amount currency
define ( 'CURRENCY_CODE', 'USD' );

// current user email
define ( 'USER_EMAIL', '[email protected]' );

Using Stripe Checkout tool

What’s amazing about Stripe is it saves you all the trouble of coding the payment forms, validating credit card numbers and form fields, and everything! Stripe Checkout gives you the opportunity to place a simple button which the user can click to get a neat popup where they fill in their card information to make the payment. Once the information have been submitted successfully, Stripe Checkout will submit then the form to your server (charge.php in this tutorial) and from there you’ll use Stripe API to make actual payments (because no payment has been made yet).

We can then with no further due add a form with their checkout JS to our index page and actually start displaying content!

index.php

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require __DIR__ . '/vendor/autoload.php';

// API public key
define ( 'STRIPE_PUBLIC_KEY', 'pk_test_6pRNASCoBOKtIshFeQd4XMUh' );

// API secret key
define ( 'STRIPE_SECRET_KEY', 'sk_test_BQokikJOvBiI2HlWgH4olfQ2' );

// stripe amount
define ( 'STRIPE_PRICE', 100 ); // that's $1

// amount currency
define ( 'CURRENCY_CODE', 'USD' );

// current user email
define ( 'USER_EMAIL', '[email protected]' );
// start output
?>

<!DOCTYPE html>
<html>
<head>
 <title>My Amazing Membership Site</title>
</head>
<body style="background: #ececec; display: table; margin: 0 auto; padding-top: 5vw">

<form action="index.php?charge=1" method="POST">
 <h3>Pay membership with credit/debit card</h3>

 <p>This is a one-time payment. You will be prompted to enter your card details securly.</p>

 <script
 src="https://checkout.stripe.com/checkout.js" class="stripe-button"
 data-key="<?php echo STRIPE_PUBLIC_KEY; ?>"
 data-email="<?php echo USER_EMAIL; ?>"
 data-amount="<?php echo STRIPE_PRICE; ?>"
 data-name="My Amazing Site"
 data-description="Premium Membership / For Life!"
 data-image="http://lorempixel.com/150/100/cats/"
 data-currency="<?php echo CURRENCY_CODE; ?>"
 data-locale="auto">
 </script>
</form>

</body>
</html>

Great so now we have some content in our index!

Go ahead and click the pay button, you’ll see the popup containing the credit card credentials fields, and the icon, title, description and button text along with other generic data can be customized in the script tag within the form, for instance loose the cat and add a real icon image to your card by specifying the image URL through data-image

data-image="http://lorempixel.com/150/100/cats/"

Cool! To do some test payment, Stripe has some sandbox card which number is 4242 4242 4242 4242, the expiration can be any future date in format MM/YY (10/19 for October 2019), and CVC can be a random 3 digits number so 123.

The remember button will basically let Stripe remember the card details for the current site and fill them automatically upon the next request securly, this is not something we’d be worried about for the moment, nor collecting the card details as well, we don’t need them.

We’re not going to submit the card yet, let’s make sure charge.php file is involved when doing so, so as to verify payments:

index.php

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require __DIR__ . '/vendor/autoload.php';

// API public key
define ( 'STRIPE_PUBLIC_KEY', 'pk_test_6pRNASCoBOKtIshFeQd4XMUh' );

// API secret key
define ( 'STRIPE_SECRET_KEY', 'sk_test_BQokikJOvBiI2HlWgH4olfQ2' );

// stripe amount
define ( 'STRIPE_PRICE', 100 ); // that's $1

// amount currency
define ( 'CURRENCY_CODE', 'USD' );

// current user email
define ( 'USER_EMAIL', '[email protected]' );

// if the form was submitted (i.e payment request sent)
if ( isset($_GET['charge']) && $_POST ) {
require __DIR__ . '/charge.php';
}

// start output
?>

<!DOCTYPE html>
<html>
<head>
<title>My Amazing Membership Site</title>
</head>
<body style="background: #ececec; display: table; margin: 0 auto; padding-top: 5vw">

<form action="index.php?charge=1" method="POST">
<h3>Pay membership with credit/debit card</h3>

<p>This is a one-time payment. You will be prompted to enter your card details securly.</p>

<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<?php echo STRIPE_PUBLIC_KEY; ?>"
data-email="<?php echo USER_EMAIL; ?>"
data-amount="<?php echo STRIPE_PRICE; ?>"
data-name="My Amazing Site"
data-description="Premium Membership / For Life!"
data-image="http://lorempixel.com/150/100/cats/"
data-currency="<?php echo CURRENCY_CODE; ?>"
data-locale="auto">
</script>
</form>

</body>
</html>

Now lets get into charge.php file, we actually want to debug the data passed by checkout.js to our server:

charge.php

<?php

// prevent direct access, only index.php can include this file
defined ( 'STRIPE_SECRET_KEY' ) || exit ( 'Direct access not allowed' . PHP_EOL );

use \Stripe\Stripe;
use \Stripe\Customer;
use \Stripe\Charge;

Stripe::setApiKey(STRIPE_SECRET_KEY);

$token = isset($_POST['stripeToken']) ? $_POST['stripeToken'] : null;

if ( !$token ) {
return; // just in case
}

echo var_dump($_POST);
exit;

Registering the customer and charging them

So far so good. Now if you submit the payment for with dummy details:

  • Card number: 4242 4242 4242 4242
  • Expiration: (some future month)/(some future year): 10/19
  • CVC: 123

We’ll now have checkout.js submit the form automatically and we will see an array of data passed, among them stripeToken which will be the token Stripe PHP SDK requires to create the customer and make the payment:

$customer = Customer::create(array(
'email' => USER_EMAIL,
'source' => $token
));

Now usually this is the step before las. It can either be successful creating a customer or throw a warning of some exception, so we’ll wrap it in try/catch block to make sure we handle the request properly.

A one last step is charging this customer using their id returned:

$charge = Charge::create(array(
'customer' => $customer->id,
'amount' => STRIPE_PRICE,
'currency' => CURRENCY_CODE
));

Now usually this should throw errors when something is not well, so we will again use the try/catch bloc and not worry about the different exceptions that Stripe throws because in the end all of them are pointing to the fact that an error occured and no payment has been made.

In the catch bloc you might want to log the request, send you an email as the site admin, and tell the user to hang in there until you inspect the issue. This is rare to happen unless of course we have a mis-configured something  or the user uses the same form data many times etc. (The code comes after)

We can then charge the user and show them custom success notices, but we are also supposed to be doing this with care and make sure their membership is upgraded as paid or some other action is taken, a good thing worth mentioning is sending them a custom notice to their inbox to let them know and keep track of this transaction.

And of course, if you visit your Stripe dashboard you’ll notice you have made some earnings in the transactions graph. I have $2 in the screenshot because I actually forgot to take one until I made already 2 transactions for this tutorial.

Wrapping up

To wrap up, here’s the final code for both files index.php and charge.php

index.php

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

require __DIR__ . '/vendor/autoload.php';

// API public key
define ( 'STRIPE_PUBLIC_KEY', 'pk_test_6pRNASCoBOKtIshFeQd4XMUh' );

// API secret key
define ( 'STRIPE_SECRET_KEY', 'sk_test_BQokikJOvBiI2HlWgH4olfQ2' );

// stripe amount
define ( 'STRIPE_PRICE', 100 ); // that's $1

// amount currency
define ( 'CURRENCY_CODE', 'USD' );

// current user email
define ( 'USER_EMAIL', '[email protected]' );

$success = $error = array();

// if the form was submitted (i.e payment request sent)
if ( isset($_GET['charge']) && $_POST ) {
require __DIR__ . '/charge.php';
}

// start output
?>

<!DOCTYPE html>
<html>
<head>
<title>My Amazing Membership Site</title>
</head>
<body style="background: #ececec; display: table; margin: 0 auto; padding-top: 5vw">

<form action="index.php?charge=1" method="POST">
<?php if ( $success ) : ?>
<ul style="background: #d3f1d3; display: block; padding: 1em; border: 1px solid #ddd; border-radius: 3px;">
<li><?php echo implode ( '</li><li>', $success ); ?></li>
</ul>
<?php endif; ?>

<?php if ( $error ) : ?>
<ul style="background: #fec8b7; display: block; padding: 1em; border: 1px solid #ddd; border-radius: 3px;">
<li><?php echo implode ( '</li><li>', $error ); ?></li>
</ul>
<?php endif; ?>

<h3>Pay membership with credit/debit card</h3>

<p>This is a one-time payment. You will be prompted to enter your card details securly.</p>

<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<?php echo STRIPE_PUBLIC_KEY; ?>"
data-email="<?php echo USER_EMAIL; ?>"
data-amount="<?php echo STRIPE_PRICE; ?>"
data-name="My Amazing Site"
data-description="Premium Membership / For Life!"
data-image="http://lorempixel.com/150/100/cats/"
data-currency="<?php echo CURRENCY_CODE; ?>"
data-locale="auto">
</script>
</form>

</body>
</html>

charge.php

<?php

// prevent direct access, only index.php can include this file
defined ( 'STRIPE_SECRET_KEY' ) || exit ( 'Direct access not allowed' . PHP_EOL );

use \Stripe\Stripe;
use \Stripe\Customer;
use \Stripe\Charge;

Stripe::setApiKey(STRIPE_SECRET_KEY);

$token = isset($_POST['stripeToken']) ? $_POST['stripeToken'] : null;
unset($_POST);

if ( !$token ) {
return; // just in case
}

try {
$customer = Customer::create(array(
'email' => USER_EMAIL,
'source' => $token
));
} catch ( \Exception $e ) {
$error []= "Error registering your payment request. Please try again or later!";

return;
}

try {
$charge = Charge::create(array(
'customer' => $customer->id,
'amount' => STRIPE_PRICE,
'currency' => CURRENCY_CODE
));
} catch ( Exception $e ) {
error_log( sprintf('ERROR: Stripe failed for user %s @%d (ip:%s)', USER_EMAIL, time(), $_SERVER['REMOTE_ADDR']) );
error_log( print_r( $e, true ) );

$error []= "Error verifying your payment request. Please try again or later if you are sure no payment has been made yet";
return;
}

// Now we're dealing with a verified payment! Let's upgrade the user!

// a success notice
$success []= "Your payment has been verified successfully! We're upgrading your membership.";

if ( function_exists('upgrade_user') ) {
$upgraded = upgrade_user(USER_EMAIL);

if ( !$upgraded ) {
$error []= "We could not upgrade your membership. Please sit tight as we do it manually";

error_log( sprintf('Upgrade user %s failed, do it manually ASAP!!', USER_EMAIL) );
}
}

You can download the source code from my gist https://gist.github.com/elhardoum/8cffce0c62f7c554ca0d57247e78fff2

Going Live

Before going live, in addition to disabling debug mode (removing the error_reporting and ini_set to the top of index.php file), you are required to have a valid SSL certificate on your site. It makes sense to enable SSL when you have a user authentication site for security, now it makes even more sense to secure your payments requests.

If you are on shared hosting and cannot afford an SSL, you can always use CloudFlare’s free SSL which is included along with the other free services they have in their free plan. Otherwise if you can afford one, I recommend Godaddy or NameCheap (that’s my referral link).

If you already have a dedicated server or a VPS then problem solved with LetsEncrypt’s powerful and free SSL certificates.

You also switch the API keys from sandbox (test keys) to live keys, as long as you activated your account. Here’s a useful checklist about going live with Stripe.

Digital Ocean

Cheap Cloud SSD Hosting

Get a VPS now starting at $5/m, fast and perfect for WordPress and PHP applications

Sign Up with $10 Credit

1 Comment

  1. This was very helpful. Thanks a lot!

Leave a Reply

Your email address will not be published.

*

© 2017 Samuel Elh - Powered by WordPress, DigitalOcean & NameCheap

Theme by Anders NorenUp ↑

Subscribe to our mailing list

Sign up to receive updates about WordPress, free and premium plugins and themes in general and tips and tricks

* indicates required