I was building a WordPress site where people would buy the one item using a simple PayPal button. There was no need for an eCommerce shopping cart or anything fancy.
But the client wanted the option for their customers to use different coupon codes to get different discounts.
PayPal doesn't support discount codes directly. The code they give also exposes any coupon codes inside the javascript on the page. We figured there had to be a better way. We did it by programmatically switching buttons.
Here's how to create a PayPal coupon code by switching buttons:
- Create the default button at PayPal
- Create PayPal buttons for each discount amount you offer
- Use jQuery, Ajax and PHP to return the correct button for each code
- Use text and Font Awesome to let people know they have the right code
Worth noting is I build this on a WordPress site, so I had access to some useful helper functionality. If you're doing this on another type of site, just change the code accordingly. 95% of the code will be the same.
Something else worth mentioning is that most of the code that goes into a PayPal button is the same. Only a few elements change based on whether you're running it in the sandbox or live.
It's a good idea to have sandbox code, so you can test functionality without spending actual money. But if you don't need that, the only thing that changes between buttons is the "value" of "hosted_button_id".
This code currently works as of early October 2018 in Australia and is essentially the same (I've changed PayPal codes, discount codes and prices to anonymize it) as code that is currently live and in use on a site. I've tested it against our situation. Your PayPal settings or country may be different. By the time you read this, PayPal may have changed their forms. Use this as a guide and test your own implementation before rolling to a live site.
The PayPal Button Switching Code
The code to switch PayPal buttons based on the coupon code entered is in two parts. The first part goes on the page you want the button to display. The second part goes into your functions.php file.
Put This On The Page You Want The Button To Display
In the actual part of the page you want the button to be displayed, put this:
<p id="aa-paypal"><input id="aa-paypal-coupon" /></p>
<div id="aa-paypal-button"><?php echo aa_paypal_button_base(); ?></div>
You could also do the echo aa_paypal_button_base() as a shortcode. It will connect with our button rendering code.
In the first line, the input is for the user to enter the coupon code. Obviously style this as you wish. If you change id="aa-paypal-coupon", make sure to also change it in the jQuery we'll put on that page.
Speaking of which, put this jQuery in the footer of the page that has the button. You don't need it to be sitewide, so either insert it through your theme or with something like the SOGO plugin. Another way I've used in the past is with a shortcode. Just get it in there somehow.
This is the script you need:
<script type="text/javascript">
jQuery(function($){
function debounce(fn, duration)
{
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(fn, duration)
}
}
$('#aa-paypal-coupon').keyup(debounce(function()
{
$('#aa-paypal-coupon').after(' <i class="fa fa-spinner fa-spin"></i>');
var coupon = $('#aa-paypal-coupon').val();
var post_url = ajaxurl,
application_data = {
action: 'aa_paypal_button',
coupon_code: coupon
};
$.post(post_url, application_data, function(response)
{
$(".fa-spinner").remove();
$(".fa-check-circle").remove();
$(".fa-times").remove();
if (response['success'] == true)
{
var pp_code = response['data']['pp_code'];
$('#aa-paypal-coupon').after(' <i class="fa fa-check-circle"></i>');
$('#aa-paypal-button').html(pp_code);
}
else
{
$('#aa-paypal-coupon').after(' <i class="fa fa-times"></i>');
}
});
},1000));
});
</script>
This script monitors what the user types into the coupon code box as they're typing it. So that the system doesn't get overwhelmed and for a better user experience, we use the debounce function to slow the checking down to every second the user is typing.
There's no need for the user to hit enter and there's not limit on how many checks they can do, so it would be possible to brute force the coupon code. In this instance, we were talking about a $20 discount tops on a page that was only accessible after you'd signed up to a website. However, if you're using this on a higher ticket item or is public facing, you might want to consider some further rate limiting (look for 1000 at the bottom of the script and increase that) or force a submission.
While the system is checking the code, a Font Awesome spinner is displayed, which turns to a cross on fail and a check on success. We also use a bit of simple CSS to change the cross to red and the check to green, for extra visual effect.
The last point I need to make is if you change any of the ids of the html, be sure to change the corresponding connections in the jQuery.
This goes into your functions.php file
/* Display initial, full price buy button */
function aa_paypal_button_base() {
$form = aa_paypal_button_form();
$button = "<p>1 Year's Membership: $30</p>" . $form;
return $button;
}
/* Create the PayPal form */
function aa_paypal_button_form($discount = false, $test = false)
{
if ($test) {
$url_base = 'https://www.sandbox.paypal.com/';
$url_img = 'https://www.sandbox.paypal.com/en_AU/i/';
$unique_form_id = 'PAYPALTESTCODE';
} else {
$url_base = 'https://www.paypal.com/';
$url_img = 'https://www.paypalobjects.com/en_AU/i/';
$unique_form_id = 'PAYPALLIVECODE';
}
# Change Unique ID Depending On Discounts
if ($discount == 25) {
$unique_form_id = "PP25DISCOUNTCODE";
}
elseif ($discount == 20) {
$unique_form_id = "PP20DISCOUNTCODE";
}
$paypal_form = '<form action="' . $url_base . 'webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="' . $unique_form_id . '">
<input type="image" src="' . $url_img . 'btn/btn_paynowCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online!">
<img alt="" border="0" src="' . $url_img . 'scr/pixel.gif" width="1" height="1">
</form>';
return $paypal_form;
}
/* Respond to the coupon code entry */
if(!function_exists('aa_paypal_button'))
{
add_action( 'wp_ajax_nopriv_aa_paypal_button', 'aa_paypal_button' );
add_action( 'wp_ajax_aa_paypal_button', 'aa_paypal_button' );
function aa_paypal_button()
{
$button = '';
$twentyfiveoff = array('somecode','othercode', 'bigsale');
$twentyoff = array('something','somethingelse','christmas');
$coupon_given = strtolower(sanitize_text_field($_POST['coupon_code']));
if (in_array($coupon_given,$twentyfiveoff))
{
$form = aa_paypal_button_form(25);
$button = "<p>1 Year's Membership: $22.50 (25% discount)</p>" . $form;
}
elseif (in_array($coupon_given,$twentyoff))
{
$form = aa_paypal_button_form(20);
$button = "<p>1 Year's Membership: $24.00 (20% discount)</p>" . $form;
}
if (!empty($button)) {
wp_send_json_success(array('pp_code'=>$button));
} else {
wp_send_json_error(array('bad'=>$coupon_code));
}
}
}
There's a lot to unpack there, but if you're just going to grab and go, make sure you change the $unique_form_id values, because what's in there is obviously dummy code, as I wouldn't expose the actual codes here in this tutorial.
You'll also want to change the coupon codes inside the array for $twentyfiveoff and $twentyoff to whatever coupon codes you want for whatever discounts you're offering. Obviously the codes I put in there are fake too, for the purpose of teaching.
Let's break down the PHP code
The two add_action lines at the top allow our code to use Ajax. This lets us check the coupon codes without needing to refresh the page.
Display Default Button And Standard Price
The first function, aa_paypal_button_base() connects this PHP to the HTML in the previous section to display the initial, standard message and the full price PayPal buy button to the user. You could easily replace this section with shortcode processing to achieve the same result.
This connects to aa_paypal_button_form(), which is the main function of this whole code.
Create the PayPal form
If we call aa_paypal_button_form() without any parameters, it will display the default PayPal buy button for the full price product.
We have optional parameters that let us change to discount buttons and/or use the sandbox.
You'll need to create a new button at PayPal for each price point. Inside that form, which PayPal will give you at the end of creating that button, you'll have a unique form ID, which will be a value of the input named "hosted_button_id".
If you look closely at the HTML in $paypal_form in this function, you'll see the common elements of every PayPal buy button. Make sure that this matches the buttons you create.
Depending on when you're reading this, PayPal might have changed its button or the URL base and image URL base. Just be sure all these match up to the form you get back from PayPal. If they are different to what I have here, use this as a template to roll your own function.
Your default URLs and form IDs go into the first conditional.
The second conditional is where you test for different discounts. If you want to test discounts in the sandbox, just expand the conditionals. So you might do this:
# Change Unique ID Depending On Discounts
if ($test)
{
if ($discount == 25) {
$unique_form_id = "PP25testDISCOUNTCODE";
}
elseif ($discount == 20) {
$unique_form_id = "PP20testDISCOUNTCODE";
}
}
else
{
if ($discount == 25) {
$unique_form_id = "PP25DISCOUNTCODE";
}
elseif ($discount == 20) {
$unique_form_id = "PP20DISCOUNTCODE";
}
}
If you have a lot of discounts, I'd probably change it to a switch statement.
We used integers 25 and 20 for this implementation, but because this implementation is not strongly typed, you could use strings to select the discount instead.
Respond to coupon code entry
In the final section of the php code, we use Ajax to hook into the button and process the coupon code entry.
The first line just makes sure there are no conflicts. Next we connect WordPress's Ajax system to our button.
Notice the add_action hooks into 'wp_ajax_nopriv_aa_paypal_button' and 'wp_ajax_aa_paypal_button'? Those bolded parts have to be the same as the div id that we used around the form in the HTML from the first section. Read more on how that works here.
Also notice the use of underscores in the add_action hook, as opposed to the hyphens we used in the HTML.
You'll see we used arrays with $twentyfiveoff and $twentyoff. That's because we wanted multiple coupon codes that produced the same discount. If you just have one code for each discount button (perhaps for sales or inventory tracking), you don't have to use an array at this point. A single string would suffice. Just make sure to change the following conditional check accordingly.
In $coupon_given, we collect the coupon code that the user typed. Don't let the $_POST confuse you - this is just how our Ajax implementation passes the information across to our PHP.
We sanitize the text using WordPress standard sanitizing. This just makes extra sure no one can enter anything dodgy. This would be overkill, given the code will never interact with the database, but is a good practice to get into. More importantly for our user experience, it strips linebreaks, tabs and whitespace. That way, if a user adds a space at the start or end of the coupon code, it won't return a "can't find the discount code".
Next we use conditionals to check the coupon the user entered against our list of valid codes to determine if they get a discount and by how much.
If it finds a valid code, we then go and fetch the correct PayPal button from the form builder. Notice the use of integers in the parameters. This connects with the conditionals in the builder.
I broke the $form and $button into two lines for ease of code reading, but you can certainly put them together in a single $button variable.
The change in message is important from a user experience point of view, because it lets the user know how much their discount was before they hit the buy button.
Finally, if we found a valid coupon code, the new PayPal button and message is sent back to the jQuery on the page the user is on, which then gets rendered to them as if by magic.
You'll notice that on success, we have 'pp_code' in the array with the button. This connects with the line in jQuery that reads:
var pp_code = response['data']['pp_code'];
Just make sure that if you change 'pp_code' in the PHP you also change it in the response data.
The 'bad' response in the JSON error doesn't matter. We just have to send something back to the jQuery, so it stops waiting for a response.
So there you have it. We created a PayPal discount code by programmatically switching buttons, based on the user's input. Please be sure to test this before you run it on a live site.
Let me know in the comments if you found this useful.