Payment on Registration?

Best way is to handle this with user roles. A user can register but not read. After paying with paypal, the role will be updated (e.g. from reader to subscriber). The update can be done through PayPals IPN.

I made this for one of my clients. You will be paid by your customer. I will be paid by you. You got the plugin.

Update

As you want to write a theme and do not used it for your professional buisness, here a some parts of my plugin solution.

PayPal IPN

Let start with PayPal IPN. PayPal have a well documented API and a PayPal Developer Network. An account in the developer network will help you much, PayPal offers a sandbox and a lot of scripts and tutorials.
Digging throuh the dev network, I found the IPN (Instant Payment Notification). IPN means, PayPal send a post request with detailed informations about the payer to an url you specified in your PayPal acconut. During development, you will love the IPN simulator.
Let start the first experimantal script. Go to the PayPal IPN simulatro (see link above). The first thing the simulator needs, is an url to your script. PayPal send the data with a post request, so you cannot use your local webserver, you need a public one. Assuming your server is reachable under http://yourdoma.in, then upload the script below with ftp (e.g. name it pplog.php) and enter http://yourdoma.in/pplog.php into the field or IPN handler URL. Select Cart checkout from the next dropdown and push Send IPN

<?php
if (empty($_POST))
    exit( 'No post request' );

define('MAILMODE', true);

$filename="paypal-ipn.log";
$file_exists = file_exists(filename);

if ($file_exists && MAILMODE) {
    unlink($filename);
    $mode="w";
} else {
    $mode =  $file_exists ? 'a+' : 'w';
}

$handle = fopen($filename, $mode);
if ( $handle ) {
    fputs($handle, date( 'Y-m-d')."\n");
    fputs($handle, var_export( $_POST, true));
    fputs($handle, "\n");
    fputs($handle, str_repeat('-', 80)."\n");
    fclose($handle);
}

if (MAILMODE)
    mail('[email protected]', 'PayPal IPN Log', file_get_contents($filename));

The script should send a email with the data what PayPal send as post request. After the email arrive and you open it, you will see a lot of data. We need only a handful of them, don’t be irritated. We are just interesetd at last_name, first_name and payer_email.

Now we have to do something with the received post data. At first you can verify the post data. If you think the data are ok and nobody will treat you, skip the next step. If you know that everybody can send a post request with the needed post data (because your theme is open source), you have to verify the post data.
To verify the received data, we have to send back them to PayPal. PayPal will answer with VERIFIED or INVALID. The next code snippet is from the PayPal Dev Network

class PayPal_IPN_API
{
//const PAYPALURI = 'ssl://www.sandbox.paypal.com';
const PAYPALURI = 'ssl://ipnpb.paypal.com';

private function talk_to_paypal() {
    /*
     * Original code from PayPal Developer Network
     */

    // read the post from PayPal system and add 'cmd'
    $req = 'cmd=_notify-validate';

    foreach( $_POST as $key => $value ){

        $value = urlencode( stripslashes( $value ) );
        $req .= "&$key=$value";

    }

    // post back to PayPal system to validate
    $header  = "POST /cgi-bin/webscr HTTP/1.0\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen( $req ) . "\r\n\r\n";

    $fp = fsockopen( self::PAYPALURI, 443, $errno, $errstr, 30 );

    /*
     * End PayPal Developer Code
     */
  }
}

This codesnippet is from a class I used. At the beginning of the class you can see the const PAYPALURI. PayPal offers a sandbox (account needed) so you can test your scxripts without dealing with ‘real’ data. I leave this code as it is, it could be optimized and instead of using fsockopen(), you could also use wp_remote_get().
Now we have to handle the answer from PayPal, the next snippet is again from my class:

/*
 * Modified PayPal Developer Code
 */
if( ! $fp ){

    $this->log( "connection failed" );
    die( "Can not open connection to PayPal" );

} else {

    fputs( $fp, $header . $req );

    while( ! feof( $fp ) ){

        $res = fgets( $fp, 1024 );

        // the received data was verified by PayPal
        if( strcmp( $res, "VERIFIED" ) == 0 ){

            $payment_status = isset( $_POST['payment_status'] ) ? strtolower( $_POST['payment_status'] ) : '';

            // check payment status. only completed payments will be processed
            if( 'completed' == $payment_status ){

                // create the userdata-array
                $udata = array(
                    'payer_email'   => $_POST['payer_email'],
                    'first_name'    => $_POST['first_name'],
                    'last_name'     => $_POST['last_name']
                );

                $this->modify_wp_user( $udata );
            }

        // the received data could not be verified by PayPal
        } elseif( strcmp( $res, "INVALID" ) == 0 ){

                $this->log( "invalid request" );
                die( "Invalid request at PayPal" );

        } else {
            // something went terrible wrong
            $this->log( "unknown error while connection" );

        }
    }
}

// be nice. close the connections you have opened
fclose( $fp );

/*
 * End modified PayPal Code
 */

Quiet simple. Check if the connection was succesful. If so, check the answer. If the answer is INVALID, do nothing. If it is VERIFIED, grab the first- and last name and the email adress of the payer from the first post request.

Creating or updating an user

Now we have a name and email adress, so we can create an user or updating an existing user. Basically you have to find a user with the given data. If there is an existing user, update the user. Else create a new user.
Fetching a user by his first- and last name is possible, but not the topic of this post. We do the easy way and try to fetch a user by the email adress we have just received. This is easy because WordPress have a nice function for this: get_user_by(). Above we copied the name and email adress in an array and pass this array to the method modify_wp_user()

/**
 * 
 * Create or modify the WP-user
 * If the user already exists, the method will upgrade the user
 * to a higher user-role.
 * If the user does not exists, the method create an user with
 * the email as user-name, first and last name and the predefined
 * user-role. The password should be send via email to the user! 
 * 
 * @param array $userdata Array with the user-email, first and last name.
 * @access private
 * @since 0.1
 */
private function modify_wp_user( $userdata = array() ) {

    if( empty( $userdata ) )
        return FALSE;
    else
        extract( $userdata );

    $user = get_user_by( 'email', $userdata['payer_email'] );

    if ( ! $user ) {
        $random_password = wp_generate_password( $length=12, $include_standard_special_chars=false );

        $data = array(
            'user_pass'     => $random_password,
            'user_login'    => $payer_email,
            'user_email'    => $payer_email,
            'first_name'    => $first_name,
            'last_name'     => $last_name,
            'role'          => 'subscriber' // adjust the role to your needs
        );

        wp_insert_user( $data );
    } else {
        $data = array(
            'ID'    => $user->ID,
            'role'  => 'contributor' // adjust the role to your needs, maybe use a custom role
        );

        wp_update_user( $data );
    }
}

It is a simple switch. Fetch the user and update or create. in this solution the payer have to use the same email adress in PayPal and in the blog. And when a new user is created, the login is the same as the email adress (from send by payPal).

The API endpoint

Ok, we now got an interface to PayPal. If a payer pays, PayPal send us the data with a post request. And we can work with the data (verify them, creating a new user or updating existing ones).
The next problem, could be the biggest one. You have to create an endpoint where PayPal send the data to. You remember the IPN handler URL? And you remember the first script I posted at the beginning?
Now you have to create an endpoint, something like http://customer-website.tld/paypalapi. This endpoint is the IPN handler URL. You can do this with WordPress functions, there are some good answers on WPSE on this topic. I will not discuss this here.
After creating your endpoint and pointing it on a php file, use the first script for your tests. in this way you can easily test if your endpoint work and if the needed data are present.
If your endpoint work and you receive the needed data, replace the script with the two methods above, but use the sandbox url instead the ‘live’ url. Now log in your PayPal developer account and create some payments. Using the sandbox URL, you got a fine development and testing environment.

Summary

  • At first you should register at PayPals Developer Network to got access to the example scripts, sandox and a lot more.
  • Create a script to test if the post request from PayPal arrive. Start testing some IPN simulations.
  • Create a script to register or update WordPress users based on the data sended by PayPal
  • Create an endpoint as target for the PayPal IPN post request
  • Put all together, test it with the PayPal sandbox.