Choosing a Plugin
Integration with multiple payment gateways
Secure checkout forms
Saved order information
Products (or membership levels) with pricing
WooCommerce
A products custom post type
The ability to browse products
The ability to search through products
The ability to purchase multiple products at once
Support for shipping address and shipping price calculations
Support for custom tax rules
The WooCommerce plugin and extensions
Customizing WooCommerce through hooks
- Setting a sitewide sale
There are plugins you can use to set a sitewide sale for your WooCommerce store, but since you’re an awesome developer, you might like to do it yourself through custom code. The following code applies a 10% sale to any product that doesn’t already have a sale price:
// Set sale price to 10% of regular price if not already on sale
function
my_get_sale_price
(
$sale_price
,
$product
)
{
if
(
empty
(
$sale_price
))
{
$sale_price
=
$product
->
get_regular_price
()
*
.
9
;
$product
->
set_price
(
$sale_price
);
}
return
$sale_price
;
}
add_filter
(
'woocommerce_product_get_sale_price'
,
'my_get_sale_price'
,
10
,
2
);
add_filter
(
'woocommerce_product_variation_get_sale_price'
,
'my_get_sale_price'
,
10
,
2
);
WooCommerce products have both a “regular” price and a “sale” price. The final calculated price is just called “price.” The hook you can use to set the sale price is
woocommerce_product_get_sale_price
; however, you will have to use thewoocommerce_product_get_variation_sale_price
hook as well to handle products with variations (e.g., Small, Medium, Large T-Shirts).Note also the line where we call
$product->set_price($sale_price)
. The calculated price isn’t updated automatically after the sale price is returned from this callback, so we have to do it manually. The$product
parameter is passed into the callback by reference; updating the product object here updates it outside of the filter as well.- Autocompleting orders
By default, when a new order is created at checkout, the order is put into “pending” status. A site administrator then needs to process the order. For typically shipped goods, process means putting the order items into a box and shipping it out. Once that is done, the order is placed into “completed” status.
For virtual goods, you might want to automatically move the items into “completed” status. Besides saving you a click, many WooCommerce plugins fire when an order goes into completed status. The sooner an order goes into completed status, the sooner those plugins can send their emails, hit their APIs, or whatever else they are doing. The following code will loop through the cart items after checkout and automatically mark the order as completed if all the items in the cart are virtual:
function
autocomplete_virtual_orders
(
$order_id
)
{
//get the existing order
$order
=
new
WC_Order
(
$order_id
);
//assume we will autocomplete
$autocomplete
=
true
;
//get line items
if
(
count
(
$order
->
get_items
()
)
>
0
)
{
foreach
(
$order
->
get_items
()
as
$item
)
{
if
(
$item
[
'type'
]
==
'line_item'
)
{
$_product
=
$order
->
get_product_from_item
(
$item
);
if
(
!
$_product
->
is_virtual
())
{
//found a non-virtual product in the cart
$autocomplete
=
false
;
break
;
}
}
}
}
//change status if needed
if
(
!
empty
(
$autocomplete
))
{
$order
->
update_status
(
'completed'
,
'Autocompleted.'
);
}
}
add_filter
(
'woocommerce_thankyou'
,
'autocomplete_virtual_orders'
);
Paid Memberships Pro
Recurring pricing for subscriptions
Tools for locking down content based on membership level
Easy Digital Downloads
File downloads restricted to authorized customers only
The ability to purchase multiple downloads at once
Easy Digital Downloads code examples
//Restrict access to a page if a user hasn't purchased a specific download yet
function
my_template_redirect_check_edd
()
{
global
$current_user
;
//Set to slug of page to protect.
$protected_page_slug
=
'customers-only'
;
//Set to ID of download to check for.
$required_download_id
=
184
;
//Only protecting one specific page.
if
(
!
is_page
(
$protected_page_slug
))
return
;
//Redirect if no user or missing purchase.
if
(
!
is_user_logged_in
()
||
!
edd_has_user_purchased
(
$current_user
->
ID
,
$required_download_id
))
{
wp_redirect
(
get_permalink
(
$required_download_id
));
exit
;
}
}
add_action
(
'template_redirect'
,
'my_template_redirect_check_edd'
);
//Helper function to check if a user is a plugin customer
function
is_plugin_customer
(
$user_id
=
null
)
{
$plugin_download_id
=
184
;
//update this
//default to current user
if
(
empty
(
$user_id
))
{
global
$current_user
;
$user_id
=
$current_user
->
ID
;
}
return
edd_has_user_purchased
(
$user_id
,
$plugin_download_id
);
}
//Add a support link to primary menu for users who purchased
function
add_support_link_to_menu
(
$items
,
$args
)
{
if
(
$args
->
theme_location
==
'primary'
&&
is_plugin_customer
())
{
$items
.=
'<li class="menu-item menu-item-type-post_type
menu-item-object-page menu-item-support">'
;
$items
.=
'<a href="/support/">Support</a>'
;
$items
.=
'</li>'
;
}
return
$items
;
}
add_filter
(
'wp_nav_menu_items'
,
'add_support_link_to_menu'
,
10
,
2
);
Payment Gateways
Does the gateway support the country and currency in which you do business?
Does the gateway integrate with the plugin you are using for ecommerce?
Does the gateway work with the type of business you are in? Some gateways will not work with adult sites, gambling sites, or other “high-risk merchants.”
Does the gateway offer the features you need, like recurring billing or stored credit cards?
How does the gateway handle Payment Card Industry (PCI) compliance?4
Will the gateway work compatibly with your merchant account (discussed in the next section)?
Finally, what are the fees? One percent of $10 million is a lot of money, and it is worth it for you to fight for lower fees. However, in general, the fees are fairly standard across gateways, and you should start by looking for a gateway that will work with your business setup. As your business grows in revenue and volume, it becomes easier to negotiate lowering your fees to the standard minimums in your industry.
Merchant Accounts
Will my gateway work with this merchant account?
Will this merchant account underwrite my type of business? Some merchant accounts will not work with adult or gambling sites, or other types of “high-risk merchants.”
Will this merchant account underwrite my size of business? Some merchant accounts won’t approve new businesses that sell high-priced (thousands of dollars) goods.
Finally, what are the fees? Sometimes these fees are bundled into the payment gateway fees, and sometimes they are separate.
Setting Up SaaS with Paid Memberships Pro
The SaaS Model
Step 0: Establishing How You Want to Charge for Your App
Step 1: Installing and Activating Paid Memberships Pro
From your WordPress dashboard, go to Plugins → Add New.
Find Paid Memberships Pro, and then click the Install link.
Optionally, enter your FTP information here (some hosting setups won’t require this).
When the plugin installs successfully, click the Activate link.
Step 2: Setting Up the Level
From your WordPress dashboard, go to the newly created Memberships page.
Click the “Add new level” link or button.
Enter the membership information in the boxes, as shown in Figure 15-2. For our level, that will be:
Name: <School Name>
Description: School administrators should sign up here to create and gain access to your SchoolPress site.
Confirmation Message: (can leave it blank)
Initial Payment: 1000
Recurring Subscription: Checked
Billing Amount: 1000
Per: 1
Days/Weeks/Years: Years
Billing Cycle Limit: 0
Custom Trial: Unchecked
Disable New Signups: Unchecked
Membership Expiration: Unchecked
Categories: All Unchecked
Click Save Level.
Step 3: Setting Up Pages
Step 4: Choosing Payment Settings
Step 5: Choosing Email Settings
Note
Step 6: Choosing Advanced Settings
Note
Step 7: Locking Down Pages
Lock down a specific page
Lock down a page by URL
//lock down our members group
function
my_buddy_press_members_group
()
{
$uri
=
$_SERVER
[
'REQUEST_URI'
];
if
(
strtolower
(
substr
(
$uri
,
0
,
16
))
==
"/groups/members/"
)
{
//make sure they are a member
if
(
!
pmpro_hasMembershipLevel
())
{
wp_redirect
(
pmpro_url
(
"levels"
));
exit
;
}
}
}
add_action
(
"init"
,
"my_buddy_press_members_group"
);
Note
if
(
pmpro_hasMembershipLevel
(
array
(
1
,
2
)))
{
//do something for level 1 and 2 members here
}
Lock down a portion of a page by shortcode
Welcome to SchoolPress! [membership level="1"]Thanks for your continuing membership.[/membership] [membership level="-1"]Sign up your school now![/membership]
Lock down a portion of a page by PHP code using the pmpro_hasMembershipLevel() function
<?
php
if
(
is_user_logged_in
())
{
?>
<div class="user-welcome">
Welcome
<?php
if
(
function_exists
(
"pmpro_hasMembershipLevel"
)
&&
pmpro_hasMembershipLevel
())
{
?>
<a href="/
<?php
echo
pmpro_url
(
"account"
);
?>
">
<?php
echo
$current_user
->
display_name
;
?>
</a>
<?php
}
else
{
?>
<a href="/
<?php
echo
home_url
(
"/wp-admin/profile.php"
);
?>
">
<?php
echo
$current_user
->
display_name
;
?>
</a>
<?php
}
?>
</div> <!-- end user-welcome -->
<?php
}
?>
Step 8: Customizing Paid Memberships Pro
Figure out what you want to change.
Find out where the default behavior for your change is coded.
Locate or add a hook to support the customization you want.
Write an action or filter to use the hook.
Restricting nonmembers to the home page
function
my_template_redirect
()
{
$okay_pages
=
array
(
pmpro_getOption
(
'billing_page_id'
),
pmpro_getOption
(
'account_page_id'
),
pmpro_getOption
(
'levels_page_id'
),
pmpro_getOption
(
'checkout_page_id'
),
pmpro_getOption
(
'confirmation_page_id'
)
);
//if the user doesn't have a membership, send them home
if
(
!
is_user_logged_in
()
&&
!
is_home
()
&&
!
is_page
(
$okay_pages
)
&&
!
strpos
(
$_SERVER
[
'REQUEST_URI'
],
"login"
))
{
wp_redirect
(
home_url
(
'wp-login.php?redirect_to='
.
urlencode
(
$_SERVER
[
'REQUEST_URI'
])));
}
elseif
(
is_page
()
&&
!
is_home
()
&&
!
is_page
(
$okay_pages
)
&&
!
pmpro_hasMembershipLevel
()
{
wp_redirect
(
home_url
());
}
}
add_action
(
'template_redirect'
,
'my_template_redirect'
);
Locking down files
RewriteEngine On RewriteBase / RewriteRule ^wp-content/uploads/(.*)$ \ /wp-content/plugins/paid-memberships-pro/services/getfile.php [L]
Change user roles based on membership levels
function
my_pmpro_after_change_membership_level
(
$level_id
,
$user_id
)
{
if
(
$level_id
==
1
)
{
//New member of level #1.
//If they are a subscriber, make them an author.
$wp_user_object
=
new
WP_User
(
$user_id
);
if
(
in_array
(
'subscriber'
,
$wp_user_object
->
roles
))
$wp_user_object
->
set_role
(
'author'
);
}
else
{
//Not a member of level #1.
//If they are an author, make them a subscriber.
$wp_user_object
=
new
WP_User
(
$user_id
);
if
(
in_array
(
'author'
,
$wp_user_object
->
roles
))
$wp_user_object
->
set_role
(
'subscriber'
);
}
}
add_action
(
'pmpro_after_change_membership_level'
,
'my_pmpro_after_change_membership_level'
,
10
,
2
);
International and long-form addresses
/*
Change the Default Country from US to GB (Great Britain)
*/
function
my_pmpro_default_country
(
$default
)
{
return
'GB'
;
}
add_filter
(
'pmpro_default_country'
,
'my_pmpro_default_country'
);
/*
Add/remove some countries from the default list.
*/
function
my_pmpro_countries
(
$countries
)
{
//remove the US
unset
(
$countries
[
'US'
]);
//add The Moon (LN short for Lunar?)
$countries
[
'LN'
]
=
'The Moon'
;
//You could also rebuild the array from scratch.
//$countries = array('CA' => 'Canada', 'US' => 'United States',
// 'GB' => 'United Kingdom');
return
$countries
;
}
add_filter
(
'pmpro_countries'
,
'my_pmpro_countries'
);
/*
(optional) You may want to add/remove certain countries from the list.
The pmpro_countries filter allows you to do this.
The array is formatted like
array('US'=>'United States', 'GB'=>'United Kingdom');
with the acronym as the key and the full
country name as the value.
*/
function
my_pmpro_countries
(
$countries
)
{
//remove the US
unset
(
$countries
[
'US'
]);
//add The Moon (LN short for Lunar?)
$countries
[
'LN'
]
=
'The Moon'
;
//You could also rebuild the array from scratch.
//$countries = array('CA' => 'Canada', 'US' => 'United States',
// 'GB' => 'United Kingdom');
return
$countries
;
}
add_filter
(
'pmpro_countries'
,
'my_pmpro_countries'
);
/*
Change some of the billing fields to be not required.
Default fields are: bfirstname, blastname, baddress1, bcity, bstate,
bzipcode, bphone, bemail, bcountry, CardType, AccountNumber,
ExpirationMonth, ExpirationYear, CVV
*/
function
my_pmpro_required_billing_fields
(
$fields
)
{
//remove state and zip
unset
(
$fields
[
'bstate'
]);
unset
(
$fields
[
'bzipcode'
]);
return
$fields
;
}
add_filter
(
'pmpro_required_billing_fields'
,
'my_pmpro_required_billing_fields'
);
1 What counts as an “ecommerce platform”? Should market share be computed based on the number of sites or the number of sales? How do you account for hosted ecommerce solutions like Shopify or Amazon?
2 As of this writing, the BuiltWith site estimates that 21% of ecommerce sites in the top one million of all sites use WooCommerce.
3 Minus any fees.
4 At the high end, PCI compliance requires more expensive server setups and full-time resources to maintain and document them properly. Some gateways have technology and processes to help you avoid those costs while still keeping your customer data secure.