2012.sandiego.wordcamp

35
2012 · SAN DIEGO · WORDCAMP @BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL HOW TO SELL PREMIUM PLUGINS WHILE STILL SUPPORTING THE GPL

description

 

Transcript of 2012.sandiego.wordcamp

Page 1: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

HOW TO SELL PREMIUM PLUGINS WHILE STILL SUPPORTING THE GPL

Page 2: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

HTTP://BDOVE.ME/WCSD2012-SLIDES

Page 3: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ WORDPRESS PLUGIN & THEME DEVELOPER FOR PIXEL JAR‣ WORDCAMP ORANGE COUNTY ORGANIZER‣ LEAD DEVELOPER OF THE PREMIUM PLUGIN ADSANITY

INTRO

Page 4: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ ONE DEVELOPER'S SOLUTION TO THIS PROBLEM (MINE)‣ AUTHENTICATION AGAINST A “MOTHERSHIP” USING XML-RPC‣ REAL, ACTUAL CODE – YOU'VE BEEN WARNED

WHAT WE’RE GOING TO COVER

Page 5: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

A PLUGIN THAT REQUIRES YOU TO PAY

FOR ACCESS TO CODE OR THE DEVELOPER

SO, WHAT IS A PREMIUM PLUGIN?

Page 6: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

PREMIUM DOES NOT ALWAYS MEAN BETTER

JUST CAUSE YOU PAY FOR SOMETHING...

Page 7: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

BUT WHAT ABOUT THE GPL?

DON’T ALL WORDPRESS PLUGINS HAVE TO BE

OPEN SOURCE?

Page 8: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

BUT WHAT ABOUT THE GPL?

INSERT PHILOSOPHICAL DEBATE HERE< >

Page 9: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

BUT WHAT ABOUT THE GPL?

YES**UNLESS YOU’RE A DIRTBAG

Page 10: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

BUT WHAT ABOUT THE GPL?

UNDER THE GPL, CODE CAN BE REDISTRIBUTED AND/OR MODIFIED BY

ANYONE. END OF STORY.

Page 11: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ DONATIONWARE‣ FREEMIUM‣ PAY ONCE FOR CODE‣ SUBSCRIBE TO UPDATES & SUPPORT‣ PROBABLY OTHERS...

MORE THAN ONE WAY TO SKIN A KITTEH

HTTP://WHYEVOLUTIONISTRUE.WORDPRESS.COM/2010/05/29/CATURDAY-FELIDS-COOL-CATS/SHAVED-CAT-2/

Page 12: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ ENCODING = BAD‣ YOU RESPECT THE GPL‣ SUPPORTING USERS IS A FULL TIME JOB‣ YOU NEED TO MAKE A LIVING

SOME ASSUMPTIONS ON MY PART

Page 13: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

ENCODING MAKES IT HARD FOR USERS TO

CUSTOMIZE YOUR CODE

PREMIUM PLUGINS SHOULDN’T TAKE AWAY YOUR RIGHTS

YOU ARE USING ACTIONS & FILTERS, RIGHT?

Page 14: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

STOPPING PIRACY IS HARD

JUST ASK LAMAR SMITHHTTP://WWW.MEMBERGUIDE.GPOACCESS.GOV/112/RP/SMITHLAMAR

SOPA & PIPA SUCK

Page 15: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

WITH AUTHENTICATION, WE CAN:‣ CHARGE FOR SUPPORT & ACCESS TO UPDATES‣ CHARGE FOR ADD-ONS

AUTHENTICATION IS EASY

Page 16: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ USER DOWNLOADS THE PLUGIN FROM THE WORDPRESS REPO‣ USER INSTALLS/ACTIVATES THE PLUGIN‣ DEVELOPER RELEASES AN UPDATE‣ USER IS NOTIFIED OF THE UPDATE IN THE WORDPRESS DASHBOARD‣ USER UPDATES THE PLUGIN

STANDARD PLUGIN WORKFLOW

Page 17: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

‣ CUSTOMER PURCHASES THE PLUGIN‣ CUSTOMER DOWNLOADS THE PLUGIN FROM DEVELOPER’S SITE‣ CUSTOMER INSTALLS/ACTIVATES THE PLUGIN‣ DEVELOPER RELEASES AN UPDATE‣ CUSTOMER IS NOTIFIED OF THE UPDATE IN THE WORDPRESS DASHBOARD‣ IF CUSTOMER IS AUTHENTICATED ‣ CUSTOMER IS ALLOWED TO UPDATE THE PLUGIN

THE WORKFLOW

Page 18: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

IN PRACTICAL TERMS, HOW DO WE DO THAT?

Page 19: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

STEP 1: MAKE A SWEET LOOKIN’ CHART

2A

2B

Plugin Authentication

email

password

Submit

Mothership

auth

Oh Noes!Success!

XMLRPC POST

API Key IXR_Error

Page 20: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

COMMUNICATION CHANNEL TO A REMOTE

SERVER USING XML

XML-RPC

Page 21: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

gist.github.com/f3e2a1cbd7a3a7c693f2

// check against remote server if( !class_exists( 'IXR_Client' ) ) require_once( ABSPATH.WPINC.'/class-IXR.php' ); $client = new IXR_Client( 'http://mothership.com/xmlrpc.php' );

$client_request_args = array( 'username' => $_POST['U'], 'password' => $_POST['P'], 'plugin' => 'plugin-slug.php', 'url' => site_url() ); if ( !$client->query( 'pue.is_user_authorized', $client_request_args ) ) : add_action( 'admin_notices', 'oh_noes' ); else :

// store the unique api key, and enable auto-updates

endif;

// handle the error function oh_noes() { global $client; echo '<div class="error"><p>' . esc_html( $client->message->faultString ) . '</p></div>'; }

STEP 2A: SUBMIT CREDENTIALS TO THE MOTHERSHIP

Page 22: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (PLUGIN)

if( !class_exists( 'IXR_Client' ) )

require_once( ABSPATH . WPINC . '/class-IXR.php' );

$client = new IXR_Client( 'http://mothership.com/xmlrpc.php' );

Page 23: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (PLUGIN)

$client_request_args = array(

'username' => $_POST['u'],

'password' => $_POST['p'],

'plugin' => 'plugin-slug.php',

'url' => site_url()

);

Page 24: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (PLUGIN)

$client->query(

'pue.is_user_authorized',

$client_request_args

);

Page 25: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

github.com/brandondove/plugin-update-engine

/* * Adding XMLRPC methods for the client plugin to call /**/ add_filter( 'xmlrpc_methods', 'custom_xmlrpc_methods' ); function custom_xmlrpc_methods( $api_methods ) { $api_methods['pue.is_user_authorized'] = 'custom_auth'; return $api_methods; } function custom_auth( $args = array() ) { extract( $args ), EXTR_OVERWRITE ); if( !$username || !$password || !$plugin || !$url ) : return new IXR_Error( 401, "OH NOES! You're doin' it wrong!" ); endif; if( !user_pass_ok( $username, $password ) ) : return new IXR_Error( 401, 'Sorry, Username and/or Password is Incorrect' ); endif;

require_once( PUENGINE_CORE.'models/pue-authenticated-user.php' ); $user = new pue_authenticated_user( $username );

if( !$user->is_authorized( $plugin ) ) return new IXR_Error( 401, __( "You don't have an active license for this plugin." ) ); if( !$user->has_available_licenses( $plugin, $url ) ) return new IXR_Error( 401, __( 'NOT AUTHORIZED! You have used up all of your licenses.' ) ); return md5( $username.$password.$plugin.$url.PUENGINE_SALT ); }

STEP 2B: HAVE MOTHERSHIP VALIDATE CREDENTIALS

Page 26: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

add_filter( 'xmlrpc_methods', 'custom_xmlrpc_methods' );

function custom_xmlrpc_methods( $api ) {

$api['pue.is_user_authorized'] = 'custom_auth';

return $api;

}

Page 27: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

extract( $args ), EXTR_OVERWRITE );

// ensure the correct data was submitted

if( !$username || !$password || !$plugin || !$url ) :

return new IXR_Error( 401, "OH NOES! You're doin' it wrong!" );

endif;

Page 28: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

// ensure the user/pass combo is real

if( !user_pass_ok( $username, $password ) ) :

return new IXR_Error( 401, 'Username/Password is Incorrect' );

endif;

Page 29: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

require_once( PUENGINE_CORE.'models/pue-authenticated-user.php' );

$user = new pue_authenticated_user( $username );

Page 30: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

if( !$user->is_authorized( $plugin ) )

return new IXR_Error( 401, __( "No license for this plugin." ) );

Page 31: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

if( !$user->has_available_licenses( $plugin, $url ) )

return new IXR_Error( 401, __( 'No more your licenses.' ) );

Page 32: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

XML-RPC (MOTHERSHIP)

return md5(

$username . $password .

$plugin . $url .

PUENGINE_SALT

);

Page 33: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

THE PLUGIN IS AUTHENTICATED.

Page 34: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

NEXT STEPS...

REPO HOSTING“PROFESSIONAL WORDPRESS PLUGIN DEVELOPMENT”

PAGES 263-269

WRITTEN BY THE AWESOMEBRAD WILLIAMS, OZH RICHARD & JUSTIN TADLOCK

HTTP://BDOVE.ME/PWPPD

Page 35: 2012.sandiego.wordcamp

2012 · SAN DIEGO · WORDCAMP@BRANDONDOVE OF @PIXEL_JAR #PREMIUMGPL

[email protected]

TWITTER WEB