Introduction

Over the years I’ve been able to take part in the launch of a variety of e-commerce websites.  One thing that remains consistent regardless of the type of the product, site structure, or any other variables involved, is that site owners will undoubtedly want an affiliate (commissioned sales) system included with their solution.

Affiliate systems come with a wide range of complexity.  They can be as simple as a basic tracker that includes an affiliate ID in a sales record, or they can be extremely in depth with professional reporting tools for affiliates, automated payment systems based on given factors (ie. minimum payouts), tiered structures for a “pyramid scheme” type of effect, and a whole lot more.

There are many affiliate systems available as open-source or as paid products available today.  The release of PayPal’s Adaptive Payments system, though, has introduced a new possibility that the majority of these systems do not yet have in place; a real-time split of payments between the website owner and the affiliate during checkout.

This new possibility included with PayPal’s web services can greatly simplify the overall solution for an affiliate system.  While reporting systems are great, they take time to develop.  Site owners are also then forced to manage payout systems of some sort.  This, again, can be time-consuming and more costly to develop a system that handles these things for you.  Adaptive Payments can eliminate some of these issues by providing the following through a Parallel or Chained Payment.

  • Automatically split payments between the website owner and the affiliate sales person during checkout.
    • Affiliates get paid instantly.  No further payment management is necessary (unless you want to provide the option of a check.)
    • Reporting tools are not nearly as important because payouts happen in real-time.  As such, affiliates can use their own PayPal account to generate their own reports.
    • Fees can be split accordingly so the website owner and the affiliate each pay fees based on the amount they receive.
      • During development, you’ll have the option of splitting the fees, or pushing all of the fees to a single receiver if you wish.

With that in mind, let’s walk through an example of how one might develop a basic affiliate system that uses PayPal Adaptive Payments to pay out commissions to affiliates in real-time during checkout.

Configuring a Basic Affiliate System

My primary focus for this article is utilizing PayPal’s Adaptive Payments API’s.  Therefore, I will cover the setup of a very basic affiliate system pretty quickly so that we can get into the payment processing.

Our affiliate system should:

  • Track affiliate referrals
  • Provide the PayPal email address of the affiliate
  • Calculate amounts to be sent to the website owner and the affiliate based on the order amount and commission rate and make them available to the application.

The first thing necessary is a simple affiliate sign-up form for people to create an affiliate account.  When a new user signs up using your form you’ll simply save the information in a database table which should include their PayPal email address, and also generate a unique ID for the affiliate.  Here is a screenshot of the simple database I’m using for this sample.

Using this ID, referral links can be generated for the affiliate to use on their website, in emails, forums, etc.  For example, an affiliate with the ID 1234 might have a referral link like this:

http://www.domain.com/refer.php?affiliate_id=1234

By checking for an existing URL parameter called affiliate_id we can easily pull its value and notify our application that an affiliate referral has taken place, and commissions should be paid accordingly.

I like to use client-side cookies for such a task so that the affiliate will still get credit if the user decides not to purchase right away, but returns later to complete a purchase.  Using PHP, we can easily handle all of this with a small snippet of code:

if(!isset($_COOKIE['affiliate_id']) && isset($_GET['affiliate_id']))
{
  // Set cookie to expire in 30 days
  setcookie('affiliate_id',$_GET['affiliate_id'], time()+60*60*24*30);
}

This snippet of code will check for an existing cookie as well as an existing value for the URL parameter.  If a cookie already exists it will be left alone.  If no cookie exists but an ID was passed, a new cookie will be created.

Immediately following that we can write another small snippet to check again for the existing cookie, and, if one is set, pull all of the necessary information from the database to calculate a commission and prepare everything we need in order to handle splitting the payment during checkout.

I’ll be using the MySQL Wrapper Class for Library PHP in this snippet.

if(isset($_COOKIE['affiliate_id']))
{
	// Pull data from database.
	$sql = "SELECT paypal_email_address, commission_rate FROM affiliates WHERE affiliate_id = ".$_COOKIE['affiliate_id'];
	$record = $db->query_first($sql);

	// Store in session vars for use through-out the application.
	$_SESSION['affiliate_email_address'] = $record['paypal_email_address'];
	$_SESSION['affiliate_commission_rate'] = $record['commission_rate'];
}

Now we have everything we need to move into the payment processing portion of our solution:

  • We know an affiliate referral was involved.
  • We know who the affiliate is and their PayPal email address for payment.
  • We know the rate at which we should calculate their commissions based on the order total.

Processing the Payment with PayPal Adaptive Payments (Chained)

Once you have your affiliate system configured and ready to go, whether you’re using an existing solution, custom building, or following along with my simple sample, we’re ready to actually process the payment information with PayPal.

I will be using my own PHP class library for PayPal that you may be familiar with.  This will make it very quick and easy for us to implement the Chained payment, which will automatically send the correct amount to each receiver, but only display the primary receiver to your buyer during checkout.

First, let’s take a look at the actual code.  Then I’ll it down in sections in order to provide all of the details about exactly what is happening.

<?php
// Part 1 – Load sessions and include the Angell EYE PayPal class library
if(!session_id()) session_start();

require_once($_SERVER['DOCUMENT_ROOT'].'/paypal/class/includes/config.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/paypal/class/includes/paypal.class.php');

// Part 2 – Bogus data for testing purposes.
$OrderAmount = 100;
$_SESSION['affiliate_email_address'] = 'agbc_1296755893_biz@angelleye.com';
$_SESSION['affiliate_commission_rate'] = 10;
$WebsiteOwnerEmail = 'tester_1267304351_biz@angelleye.com';
$AffiliateEmail = isset($_SESSION['affiliate_email_address']) ? $_SESSION['affiliate_email_address'] : '';

// Part 3 – Setup the PayPal object.
$PayPalConfig = array('Sandbox' => $sandbox, 'APIVersion' => $api_version, 'ApplicationID' => $application_id, 'DeveloperAccountEmail' => $developer_account_email, 'APIUsername' => $api_username, 'APIPassword' => $api_password, 'APISignature' => $api_signature);
$PayPal = new PayPal_Adaptive($PayPalConfig);

// Part 4 – Setup PayPal request data.
$PayRequestFields = array(
	'ActionType' => 'PAY', // Required. Whether the request pays the receiver or whether the request is set up to create a payment request, but not fulfill the payment until the ExecutePayment is called. Values are: PAY, CREATE, PAY_PRIMARY
	'CancelURL' => $domain.'paypal-cancel.php', // Required. The URL to which the sender's browser is redirected if the sender cancels the approval for the payment after logging in to paypal.com. 1024 char max.
	'CurrencyCode' => 'USD', // Required. 3 character currency code.
	'FeesPayer' => 'EACHRECEIVER', // The payer of the fees. Values are: SENDER, PRIMARYRECEIVER, EACHRECEIVER, SECONDARYONLY
	'IPNNotificationURL' => $domain.'paypal-ipn-listener.php', // The URL to which you want all IPN messages for this payment to be sent. 1024 char max.
	'Memo' => '', // A note associated with the payment (text, not HTML). 1000 char max
	'Pin' => '', // The sener's personal id number, which was specified when the sender signed up for the preapproval
	'PreapprovalKey' => '', // The key associated with a preapproval for this payment. The preapproval is required if this is a preapproved payment.
	'ReturnURL' => $domain.'paypal-return.php', // Required. The URL to which the sener's browser is redirected after approvaing a payment on paypal.com. 1024 char max.
	'ReverseAllParallelPaymentsOnError' => '', // Whether to reverse paralel payments if an error occurs with a payment. Values are: TRUE, FALSE
	'SenderEmail' => '', // Sender's email address. 127 char max.
	'TrackingID' => '' // Unique ID that you specify to track the payment. 127 char max.
);

$ClientDetailsFields = array(
	'CustomerID' => '', // Your ID for the sender 127 char max.
	'CustomerType' => '', // Your ID of the type of customer. 127 char max.
	'GeoLocation' => '', // Sender's geographic location
	'Model' => '', // A sub-identification of the application. 127 char max.
	'PartnerName' => '' // Your organization's name or ID
);

$FundingTypes = array('ECHECK', 'BALANCE', 'CREDITCARD');

// We'll generate the list of receivers based on whether or not an affiliate referral is involved in this sale or not.
$Receivers = array();

if(isset($_SESSION['affiliate_commission_rate']) && $_SESSION['affiliate_commission_rate'] > 0)
{
	// Calculate the affiliate amount.
	// We also handle whether the commission was entered as a whole number or a decimal.
	$AffiliateAmount = $_SESSION['affiliate_commission_rate'] < 1 ? round($OrderAmount * $_SESSION['affiliate_commission_rate'],2) : round($OrderAmount * ($_SESSION['affiliate_commission_rate'] / 100),2);
	$WebsiteOwnerAmount = $OrderAmount – $AffiliateAmount;

	$Receiver = array(
		'Amount' => number_format($OrderAmount,2), // Required. Amount to be paid to the receiver.
		'Email' => $WebsiteOwnerEmail, // Receiver's email address. 127 char max.
		'InvoiceID' => '', // The invoice number for the payment. 127 char max.
		'PaymentType' => '', // Transaction type. Values are: GOODS, SERVICE, PERSONAL, CASHADVANCE, DIGITALGOODS
		'PaymentSubType' => '', // The transaction subtype for the payment.
		'Phone' => array('CountryCode' => '', 'PhoneNumber' => '', 'Extension' => ''),
		'Primary' => 'true' // Whether this receiver is the primary receiver. Values are: TRUE, FALSE
	);
	array_push($Receivers,$Receiver);

	$Receiver = array(
		'Amount' => number_format($AffiliateAmount,2), // Required. Amount to be paid to the receiver.
		'Email' => $AffiliateEmail, // Receiver's email address. 127 char max.
		'InvoiceID' => '', // The invoice number for the payment. 127 char max.
		'PaymentType' => '', // Transaction type. Values are: GOODS, SERVICE, PERSONAL, CASHADVANCE, DIGITALGOODS
		'PaymentSubType' => '', // The transaction subtype for the payment.
		'Phone' => array('CountryCode' => '', 'PhoneNumber' => '', 'Extension' => ''), // Receiver's phone number. Numbers only.
		'Primary' => '' // Whether this receiver is the primary receiver. Values are: TRUE, FALSE
	);
	array_push($Receivers,$Receiver);
}
else
{
	$Receiver = array(
		'Amount' => number_format($OrderAmount,2), // Required. Amount to be paid to the receiver.
		'Email' => $WebsiteOwnerEmail, // Receiver's email address. 127 char max.
		'InvoiceID' => '', // The invoice number for the payment. 127 char max.
		'PaymentType' => '', // Transaction type. Values are: GOODS, SERVICE, PERSONAL, CASHADVANCE, DIGITALGOODS
		'PaymentSubType' => '', // The transaction subtype for the payment.
		'Phone' => array('CountryCode' => '', 'PhoneNumber' => '', 'Extension' => ''), // Receiver's phone number. Numbers only.
		'Primary' => '' // Whether this receiver is the primary receiver. Values are: TRUE, FALSE
	);
	array_push($Receivers,$Receiver);
}

$SenderIdentifierFields = array(
	'UseCredentials' => '' // If TRUE, use credentials to identify the sender. Default is false.
);

$AccountIdentifierFields = array(
	'Email' => '', // Sender's email address. 127 char max.
	'Phone' => array('CountryCode' => '', 'PhoneNumber' => '', 'Extension' => '') // Sender's phone number. Numbers only.
);

// Part 5 – Setup nested array with all request data to pass into PayPal class library.
$PayPalRequest = array(
	'PayRequestFields' => $PayRequestFields,
	'ClientDetailsFields' => $ClientDetailsFields,
	'FundingTypes' => $FundingTypes,
	'Receivers' => $Receivers,
	'SenderIdentifierFields' => $SenderIdentifierFields,
	'AccountIdentifierFields' => $AccountIdentifierFields
);

// Part 6 – Send request to PayPal and handle response accordingly.
$PayPalResult = $PayPal->Pay($PayPalRequest);
if(strtoupper($PayPalResult['Ack']) != 'SUCCESS' && strtoupper($PayPalResult['Ack']) != 'SUCCESSWITHWARNING')
{
	// Fail
	$_SESSION['PayPalErrors'] = $PayPalResult['Errors'];
	header('Location: error.php');
}
else
{
	// Success
	$_SESSION['PayPalPayKey'] = $PayPalResult['PayKey'];
	header('Location: '.$PayPalResult['RedirectURL']);
}
?>

Code Review

Part 1

The first part in this script simply enables sessions in PHP if they have not already been enabled and also includes the required PayPal library.  This is very straight forward.

Part 2

In part 2, we’re simply setting some sample data for use with this particular example.  In reality, the session/cookie data for affiliate information would be set prior to this point in the solution as explained previously in this article.  Also, the order amount would generally be pulled from a shopping cart of some sort.  Here, we’re using a static value of $100.00 for the order amount, and I’ve also included my own PayPal sandbox email accounts as the receivers.

Part 3

This is a simple snippet of code to handle setting up the PayPal object for use with the Angell EYE PayPal PHP library.  This makes all of the methods included in the library available to your application.  In this case, we’re using the extended PayPal_Adaptive class built in to the library.

Part 4

Here we begin setting up all of our request data that we will pass into the library for processing with PayPal.  In our case, we are setting up a chained payment, so we’ll set up the request with the following parameters and values:

ActionType – This will be set to PAY and notifies the PayPal system that we are setting up a payment for immediate settlement.

CancelURL – This will hold the value of a URL you would like the user to be returned to if they decide to cancel the payment after landing on the PayPal checkout page.

FeesPayer – We will set this to EACHRECEIVER so that PayPal will automatically take the fee from each receiver based on the amount of money they receive and their current merchant rate with PayPal.  You could change this to SENDER, PRIMARYRECEIVER, or SECONDARYONLY to adjust it accordingly.

ReturnURL – This will hold the value of a URL you would like the user to be returned to after they’ve completed their payment.

Receivers – Here we will generate an array that includes all of the receiver(s) information on this particular transaction.

We need to handle both affiliate referred orders and non-referred orders, so we’ve wrapped everything within an if/else statement accordingly.

If an affiliate referral is included, the amount is calculated based on the current order amount and the rate pulled from the affiliate’s record in the database.  Again, in our static example we are using an order amount of $100 with an affiliate commission of 10%.  Two receivers are then added to our Receivers array:  one for the website owner and another for the affiliate.  Each receiver includes the following parameters:

Amount – This is the amount this particular merchant should receive on the payment.  In the case of a Chained Payment, like we’re using here, we’ll set this to the full amount of the order. Note that when using Parallel Payments, this would only be the amount this user should end up with.

Email – This is the email address of the receiver’s PayPal account.  Each receiver needs to have either a value for Email or Phone, but cannot have both.

Primary – If an affiliate is included, we’ll set the first receiver, the website owner, to the primary receiver by setting this value to TRUE.  This informs the PayPal system that we will be using a Chained Payment as oppose to a Parallel Payment.  Only one receiver can/should be set as the primary receiver.  In our case, we are treating that as the website owner.

Our second user in our Receivers array will be for the affiliate receiver.  We’ll use their email address and amount they should receive accordingly.  We’ll leave Primary empty, or you could also set it to FALSE as we can only have a single primary receiver and we’ve already set that for the website owner.

Part 5

Here we simply gather all of our separate request arrays into a single, nested array that houses everything.  This single array will be passed in to the class library as the sole parameter for the Pay() method that we’ll be using to complete this transaction.

Part 6

Here we’re passing all of the request data into the Pay() method of the class library and storing the result in an array variable called $PayPalResult.  This result array contains all of the response parameters returned from PayPal’s API, pre-parsed and ready for easy access.

Using this response array we check for a successful response and either load up errors and redirect to an error page, or redirect to the PayPal checkout pages.  We’re making use of the additional parameter returned by the library, RedirectURL, which appends the PayKey to the PayPal URL for you.

PayPal Checkout and Payment Details

Once the user is redirected to PayPal they will be presented with a screen that looks like the following.

You’ll notice that as the payer, we are only seeing a single payment to a single recipient of $100.  This is because of the Chained Payment system.  The payer will only see the total amount being paid and they will see it going directly to the primary receiver on the payment request.

Upon signing in to the payer’s PayPal account we are presented with another review page where we can update our payment method if we like and complete the payment by clicking the Pay button.

When the payment has been completed we will see the final confirmation page and will be returned back to the ReturnURL specified in the payment request.

Now that the chained payment has been completed successfully, let’s take a look at the PayPal account of the primary receiver to confirm we received the full $100 and then $10 of that was immediately sent to our affiliate’s PayPal account.

Don’t let the names on this sample confuse you.  They both say “Drew Angell’s Test Store” because they are auto-created sandbox test accounts and were both given the same name.  Those are actually two separate accounts, of course.  The $10.00 was sent to the affiliate account just as we planned.

Conclusion

Real-time payments for affiliate systems are just one of the many possibilities with the Adaptive Payments web services.

In this lesson we’ve covered the setup of a very simple affiliate system and we dived more deeply into the heart of setting up a Chained Payment will PayPal to pay out affiliates in real-time during checkout.

I hope this has helped you better understand how the Adaptive Payments system works so that you can utilize it within your own applications.