Pippins Plugins
  • Email
  • Facebook
  • Feedburner
  • Github
  • Google
  • Twitter
  • Vimeo
  • Youtube
  • Rss
  • About
  • News
  • Join the Site
    • Member Benefits
    • Member Plugins
    • Email Notifications
  • Plugin Store
    • Affiliate Area
    • Checkout
  • Plugins
    • Plugin Portfolio
      • Plugin Portfolio – List View
    • Free
    • Premium
    • Member Plugins
    • Coding Standards
    • Get Plugin Support
  • Tutorials
    • Series
      • Plugin Development 101
      • Creating a User Follow System Plugin
      • Customizing Restrict Content Pro
      • Displaying Content with Easy Content Types
      • Writing Your First WordPress Plugins, Basic to Advanced
      • Working with Widgets
      • User Submitted Image Galleries
      • Plugin Thoughts
      • Integrating Stripe.com with WordPress
      • WordPress Rewrite API
    • Member Exclusive
      • Free Members
      • Subscriber Only
    • Difficulty
      • Beginner
      • Intermediate
      • Advanced
    • Action and Filter Hooks
    • Ajax
    • Custom Post Types
    • External APIs
    • Short Codes
    • Taxonomies
    • Video Tutorials
    • Widget Tutorials
    • WordPress Admin / Dashboard
    • Working with jQuery
    • WordPress Database
    • Writing Plugins
    • Tag Index
  • Reviews
  • Support Forum
  • Contact
    • Support the Site
    • Request Code Review
    • Plugin Support

Stripe Integration Part 5 – Accepting Discount Codes

Posted on May 30, 2012 by Pippin in Advanced, External APIs, Tutorials, Writing Plugins 13 Comments
Home» Tutorials » Advanced » Stripe Integration Part 5 – Accepting Discount Codes
blueprints
Tweet
Love It - 4
This entry is part 5 of 9 in the Integrating Stripe.com with WordPress Series
← Stripe Integration Part 4 – Multiple Recurring Payment OptionsStripe Integration Part 6 – Payment Receipts →
  • Stripe Integration Part 1 – Building the Settings and a Simple Payment Form
  • Stripe Integration Part 2 – Recurring Payments
  • Stripe Integration Part 3 – Variable Prices and Enhanced Plan Handling
  • Stripe Integration Part 4 – Multiple Recurring Payment Options
  • Stripe Integration Part 5 – Accepting Discount Codes
  • Stripe Integration Part 6 – Payment Receipts
  • Stripe Integration Part 7 – Creating and Storing Customers
  • Stripe Integration Part 8 – Working with Invoices
  • Stripe Integration Part 9 – The Stripe Button

In this part of our WordPress + Stripe Integration tutorial series, we will be expanding our payment form to accept discount codes. The coupon system in Stripe is pretty robust, and I’m going to show you how to take advantage of it by walking you through the process of adding it to our plugin.

There will be several required updates to our plugin that will have to happen in order for us to accept discount codes. The changes that must be made are:

  • Add a discount code field to the payment form
  • Check the code’s validity upon submit
  • Add the coupon to the customer object for recurring payments
  • Add the coupon to the charge object, and calculate the discounted price, for one time payments

We will start by adding the discount field to the payment form.

Updating the Payment Form

Our payment form is defined in includes/shortcodes.php, so open that file. We are going to place the new field at the bottom, just above the submit button. If you prefer to place it else where, you can. The placement only matters for aesthetic reasons.

Our field code looks like this:

1
2
3
4
<div class="form-row">
	<label><?php _e('Discount Code', 'pippin_stripe'); ?></label>
	<input type="text" size="20" class="discount" name="discount"/>
</div>

When combined with the rest of our payment form, the complete short code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
 
function pippin_stripe_payment_form($atts, $content = null) {
 
	extract( shortcode_atts( array(
		'amount' => ''
	), $atts ) );
 
	global $stripe_options;
 
	if(isset($_GET['payment']) && $_GET['payment'] == 'paid') {
		echo '<p class="success">' . __('Thank you for your payment.', 'pippin_stripe') . '</p>';
	} else { ?>
		<form action="" method="POST" id="stripe-payment-form">
			<div class="form-row">
				<label><?php _e('Email', 'pippin_stripe'); ?></label>
				<input type="text" size="20" class="email" name="email"/>
			</div>
			<div class="form-row">
				<label><?php _e('Card Number', 'pippin_stripe'); ?></label>
				<input type="text" size="20" autocomplete="off" class="card-number"/>
			</div>
			<div class="form-row">
				<label><?php _e('CVC', 'pippin_stripe'); ?></label>
				<input type="text" size="4" autocomplete="off" class="card-cvc"/>
			</div>
			<div class="form-row">
				<label><?php _e('Expiration (MM/YYYY)', 'pippin_stripe'); ?></label>
				<input type="text" size="2" class="card-expiry-month"/>
				<span> / </span>
				<input type="text" size="4" class="card-expiry-year"/>
			</div>
			<?php if(isset($stripe_options['recurring'])) { ?>
			<div class="form-row">
				<label><?php _e('Payment Type:', 'pippin_stripe'); ?></label>
				<input type="radio" name="recurring" class="stripe-recurring" value="no" checked="checked"/><span><?php _e('One time payment', 'pippin_stripe'); ?></span>
				<input type="radio" name="recurring" class="stripe-recurring" value="yes"/><span><?php _e('Recurring monthly payment', 'pippin_stripe'); ?></span>
			</div>
			<div class="form-row" id="stripe-plans" style="display:none;">
				<label><?php _e('Choose Your Plan', 'pippin_stripe'); ?></label>
				<select name="plan_id" id="stripe_plan_id">
					<?php 
						$plans = pippin_get_stripe_plans();
						if($plans) {
							foreach($plans as $id => $plan) {
								echo '<option value="' . $id . '">' . $plan . '</option>';
							}
						}
					?>
				</select>
			</div>
			<div class="form-row">
				<label><?php _e('Discount Code', 'pippin_stripe'); ?></label>
				<input type="text" size="20" class="discount" name="discount"/>
			</div>
			<?php } ?>
			<input type="hidden" name="action" value="stripe"/>
			<input type="hidden" name="redirect" value="<?php echo get_permalink(); ?>"/>
			<input type="hidden" name="amount" value="<?php echo base64_encode($amount); ?>"/>
			<input type="hidden" name="stripe_nonce" value="<?php echo wp_create_nonce('stripe-nonce'); ?>"/>
			<button type="submit" id="stripe-submit"><?php _e('Submit Payment', 'pippin_stripe'); ?></button>
		</form>
		<div class="payment-errors"></div>
		<?php
	}
}
add_shortcode('payment_form', 'pippin_stripe_payment_form');

Now we need to process our discount code form.

Verifying Discount Codes

For the discount verification, we must first check to see if our discount field contains a code (is not empty). We will do this in the includes/process-payment.php file.

Just after we setup the Stripe::setApiKey($secret_key); function, add this:

1
2
3
4
5
if(isset($_POST['discount']) && strlen(trim($_POST['discount'])) > 0) {
 
	$using_discount = true;
 
}

This checks to see if the discount field was posted, and that it wasn’t blank. If the field contains a code, we set the $using_discount variable to true.

All we’ve done is check to see if a code was used, now we need to check and see if the code is valid. To do that, we will use the Stripe_Coupon::retrieve() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// check for a discount code and make sure it is valid if present
if(isset($_POST['discount']) && strlen(trim($_POST['discount'])) > 0) {
 
	$using_discount = true;
 
	// we have a discount code, now check that it is valid			
 
	try {
 
		$coupon = Stripe_Coupon::retrieve( trim( $_POST['discount'] ) );
		// if we got here, the coupon is valid
 
	} catch (Exception $e) {
 
		// an exception was caught, so the code is invalid
		wp_die(__('The coupon code you entered is invalid. Please click back and enter a valid code, or leave it blank for no discount.', 'pippin'), 'Error');
 
	}
 
}

The Stripe_Coupon::retrieve() will return an object containing the information about the coupon (which we will use later) if it is valid. If the code is invalid, an exception will be caught. If the code isn’t valid, then we kill WordPress with wp_die() and show an error message. This method of showing errors isn’t super fancy, but it works well for this example, since we’re focusing on how to integrate with Stripe, not how to show pretty error messages.

Our discount code checking is complete, so we can now add the coupon to the functions that create recurring payment profiles and single charges.

Adding the Discount to Recurring Payments

Applying a discount to a recurring payment is almost stupidly simple. All we have to do is pass a “coupon” variable that contains the coupon code. Remember, the recurring payments are setup with the Stripe_Customer::create() function, and when this is updated to include the coupon code, it looks like this:

1
2
3
4
5
6
7
$customer = Stripe_Customer::create(array(
		'card' => $token,
		'plan' => $plan_id,
		'email' => strip_tags(trim($_POST['email'])),
		'coupon' => trim($_POST['discount'])
	)
);

Pretty simple right? Well almost. This will work perfectly fine anytime a discount code is entered, but if the discount field is left blank, having the “coupon” parameter present will actually cause the payment profile creation to fail. To get past this, we setup a simple conditional:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if($using_discount !== false) {
 
	$customer = Stripe_Customer::create(array(
			'card' => $token,
			'plan' => $plan_id,
			'email' => strip_tags(trim($_POST['email'])),
			'coupon' => trim($_POST['discount'])
		)
	);
 
} else {
 
	$customer = Stripe_Customer::create(array(
			'card' => $token,
			'plan' => $plan_id,
			'email' => strip_tags(trim($_POST['email']))
		)
	);
 
}

The $using_discount variable was defined when we checked for the presence of a coupon code earlier.

Adding the Coupon to One Time Payments

The one time payments work slightly different than the recurring profiles, primarily in that there is no “coupon” parameter that we can pass to Stripe_Charge::create(). This is a limitation of the Stripe API that I really don’t quite understand, but after clarifying with Stripe support, you definitely cannot add coupons to one time charges. In order to get past this, we have to calculate the charge amount before we pass it to Stripe. Luckily, this is pretty easy.

When we checked the validity of the discount code, we stored the code returned from Stripe in a variable called $coupon. This is an object and contains all of the details of the coupon code. All we have to do is take the discount amount (it’s a percentage), do a little math, and then pass the updated $amount to our Stripe_Charge:create() function.

Here’s how we calculate the discounted amount:

1
2
3
4
if($using_discount !== false) {				
	// calculate the discounted price
	$amount = $amount - ( $amount * ( $coupon->percent_off / 100 ) );
}

Combined with our Stripe_Charge::create() function, we have this:

1
2
3
4
5
6
7
8
9
10
11
if($using_discount !== false) {				
	// calculate the discounted price
	$amount = $amount - ( $amount * ( $coupon->percent_off / 100 ) );
}	
 
$charge = Stripe_Charge::create(array(
		'amount' => $amount, // amount in cents
		'currency' => 'usd',
		'card' => $token
	)
);

You can see that if no discount is provided, we create a charge for the amount specified by our short code. If a discount is present (and is valid), we overwrite the amount from the short code with a discounted amount.

That’s it! Our payment form now accepts discount codes.

All Together

The complete process-payment.php file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?php
 
function pippin_stripe_process_payment() {
	if(isset($_POST['action']) && $_POST['action'] == 'stripe' && wp_verify_nonce($_POST['stripe_nonce'], 'stripe-nonce')) {
 
		global $stripe_options;
 
		// load the stripe libraries
		require_once(STRIPE_BASE_DIR . '/lib/Stripe.php');
 
		$amount = base64_decode($_POST['amount']) * 100;
 
		// retrieve the token generated by stripe.js
		$token = $_POST['stripeToken'];
 
		// check if we are using test mode
		if(isset($stripe_options['test_mode']) && $stripe_options['test_mode']) {
			$secret_key = $stripe_options['test_secret_key'];
		} else {
			$secret_key = $stripe_options['live_secret_key'];
		}
 
		Stripe::setApiKey($secret_key);		
 
		// check for a discount code and make sure it is valid if present
		if(isset($_POST['discount']) && strlen(trim($_POST['discount'])) > 0) {
 
			$using_discount = true;
 
			// we have a discount code, now check that it is valid			
 
			try {
 
				$coupon = Stripe_Coupon::retrieve( trim( $_POST['discount'] ) );
				// if we got here, the coupon is valid
 
			} catch (Exception $e) {
 
				// an exception was caught, so the code is invalid
				wp_die(__('The coupon code you entered is invalid. Please click back and enter a valid code, or leave it blank for no discount.', 'pippin'), 'Error');
 
			}
 
		}
 
		if(isset($_POST['recurring']) && $_POST['recurring'] == 'yes') { // process a recurring payment
 
			$plan_id = strip_tags(trim($_POST['plan_id']));		
 
			try {		
 
				if($using_discount !== false) {
 
					$customer = Stripe_Customer::create(array(
							'card' => $token,
							'plan' => $plan_id,
							'email' => strip_tags(trim($_POST['email'])),
							'coupon' => trim($_POST['discount'])
						)
					);
 
				} else {
 
					$customer = Stripe_Customer::create(array(
							'card' => $token,
							'plan' => $plan_id,
							'email' => strip_tags(trim($_POST['email']))
						)
					);
 
				}
 
				// redirect on successful recurring payment setup
				$redirect = add_query_arg('payment', 'paid', $_POST['redirect']);
 
			} catch (Exception $e) {
				// redirect on failure
				$redirect = add_query_arg('payment', 'failed', $_POST['redirect']);
			}
 
		} else { // process a one-tiome payment
 
			// attempt to charge the customer's card
			try {
 
				if($using_discount !== false) {				
					// calculate the discounted price
					$amount = $amount - ( $amount * ( $coupon->percent_off / 100 ) );
				}	
 
				$charge = Stripe_Charge::create(array(
						'amount' => $amount, // amount in cents
						'currency' => 'usd',
						'card' => $token
					)
				);
 
				// redirect on successful payment
				$redirect = add_query_arg('payment', 'paid', $_POST['redirect']);
 
			} catch (Exception $e) {
				wp_die($e);
				// redirect on failed payment
				$redirect = add_query_arg('payment', 'failed', $_POST['redirect']);
			}
		}
 
		// redirect back to our previous page with the added query variable
		wp_redirect($redirect); exit;
	}
}
add_action('init', 'pippin_stripe_process_payment');

Note! You must create the discount codes in your Stripe Dashboard under the “Coupons” section.

You can download the complete (work in progress) plugin below.

Download Plugin
Tweet Follow @pippinsplugins
Stripe

13 comments on “Stripe Integration Part 5 – Accepting Discount Codes”

  1. itsjohn says:
    June 22, 2012 at 1:14 pm

    this is awesome, thanks so much.

    i’m curious how do you come about creating a receipt for the client after the payment?

    Reply
    • Pippin says:
      June 22, 2012 at 2:47 pm

      Since Stripe does not automatically send a payment receipt (except to the recipient) as a lot of merchants do, you have to manually create the system for that. You have to use Stripe’s Webhook system. It’s pretty simple to use and the documentation on it is pretty thorough.

      I’m planning to write another part (sometime next week) on creating the payment receipts.

    • Steve Dimmick says:
      June 24, 2012 at 8:36 pm

      That would be rockin!
      Steve ;)

  2. Steven says:
    September 3, 2012 at 12:37 pm

    Adding an else statement to line 45 and adding $using_discount = false; was the only way I was able to make recurring work without a coupon code entered

    Reply
  3. Nour Akalay says:
    December 11, 2012 at 2:11 am

    Hello,

    Maybe you could take into account coupons expressed in fixed amount instead of percentage?


    if (isset($coupon->percent_off)) {
    $amount = $amount - ( $amount * ( $coupon->percent_off / 100 ) );
    } else if (isset($coupon->amount_off)){
    $amount = $amount - $coupon->amount_off;
    }

    percent_off and amount_off will never be set at the same time (Stripe doesn’t allow it) so this code should work.

    Reply
    • Pippin says:
      December 11, 2012 at 2:24 pm

      Good idea.

    • John Smith says:
      April 11, 2013 at 11:48 pm

      That worked??

  4. Helms says:
    March 7, 2013 at 2:45 pm

    Hey,

    When I try to enter an expired coupon code, I get a blank error page with a bunch of text? Do you get the same when you enter an expired code? Thanks in advance.

    Reply
    • Pippin says:
      March 10, 2013 at 8:07 pm

      Can you show me a screenshot?

  5. John Smith says:
    April 12, 2013 at 1:07 am

    This error comes when i try to purchase anything with float price(e.g. $10999.98) with discount code, which is in percentage(e.g 12%).

    exception ‘Stripe_InvalidRequestError’ with message ‘Invalid integer: 967998.24

    Reply
    • Pippin says:
      April 12, 2013 at 4:27 pm

      Does your final amount have 3 or more decimals by chance?

    • John Smith says:
      April 12, 2013 at 11:31 pm

      Yes,4 decimals
      i edited the code
      if ( $using_discount !== false ) {
      // calculate the discounted price
      if (isset($coupon->percent_off)) {
      $price = $amount – ( $amount * ( $coupon->percent_off / 100 ) );
      $amount = number_format($price, 0, ‘.’, ”);
      } else if (isset($coupon->amount_off)){
      $amount = $amount – $coupon->amount_off;
      }
      }

    • Pippin says:
      April 15, 2013 at 4:43 pm

      That edit should work fine.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • Login

Lost your password?

Please enter your username or e-mail address. You will receive a new password via e-mail.

  • Facebook Become a Fan Like

  • Twitter Subscribe on Twitter Follow

  • YouTube Follow my Videos Subscribe

  • RSS Feed Subscribe with RSS Subscribe

Easy Digital Downloads

Most Loved

  • Love It Pro for WordPress
  • Write a “Love It” Plugin with Ajax to Let Users Love Their Favorite Posts / Pages
  • Simple Notices Pro Plugin for WordPress
  • User Bookmarks for WordPress
  • Front End Registration and Login Forms Plugin

Similar Plugins and Posts

  • Stripe Integration Part 9 – The Stripe Button
  • Stripe Integration Part 8 – Working with Invoices
  • Stripe Integration Part 7 – Creating and Storing Customers
  • Stripe Integration Part 6 – Payment Receipts
  • Stripe Integration Part 4 – Multiple Recurring Payment Options

Latest Premium Content

  • Plugin Development 101 – Introduction to Adding Dashboard Menus
  • Plugin Development 101 – Intro to Loading Scripts and Styles
  • User Follow System – Part 5
  • Plugin Development 101 – Intro to Short Codes
  • Plugin Development 101 – Registering a Custom Post Type
  • Plugin Development 101 – Intro to Actions

Latest Tutorials

  • Submitting Your First Pull Request to a WordPress Plugin on Github (0)

    Github is an extremely popular tool for managing WordPress plugins, and one...

  • Plugin Development 101 – Introduction to Adding Dashboard Menus (1)

    Adding new menus, both top level and sub level, to the WordPress Dashboard is a really common task for plugins...

  • Plugin Development 101 – Intro to Loading Scripts and Styles (16)

    In this part of Plugin...

Enter your email to receive automated updates when new posts are published

Latest Tweets

  • @sunnyratilal awesome!
    May 18, 2013
  • @sfakuyi @chriscct7 it&#039;s really, really close
    May 18, 2013
  • @sfakuyi @chriscct7 yes it will
    May 18, 2013

Topics

shortcodes get_user_meta register_setting the_content attachments featured hook add_shortcode Sugar Event Calendar contextual help Tom McFarlin wp_enqueue_script add_options_page short codes Related posts login plugin authors do_action attachment mail chimp image forms post types bbpress apply_filters recent posts comments short code taxonomies custom post type images gallery Ajax Stripe taxonomy jquery users widgets add_filter easy content types add_action widget restrict content pro easy digital downloads

Weekly Newsletter

Useful Links

  • Join the Site
  • Plugin Store
  • Affiliate Area
  • Tag Index
  • Support the Site
  • Suggest a Tutorial
  • Random Post
  • Contact

Monthly Archives

(c) 2011 Pippin's Plugins