Chapter 4. PayPal Adaptive Payments

Overview of Adaptive Payments

PayPal’s Adaptive Payments allow you to send money in a variety of scenarios. Using Adaptive Payments, you can build a “send money” application for a social networking site or build a robust payment system. Through the Adaptive Payments API, you can build an application that can handle payments, payment preapprovals, and refunds. You can also create foreign currency exchange rates for a set list of amounts, which then allows you to build a payment interface that shows the buyer her order total in different currencies (e.g., U.S. dollars and euros).

Another advantage of Adaptive Payments is that you can be an application owner—for example, an online merchant who owns a website, a payment application provider for cell phones, or a widget developer of a payments widget on a social networking site—without assuming the responsibility of sending or receiving the transactions.

PayPal Adaptive Payments API Operations Overview

Adaptive Payments is made up of 10 key API operations, listed in Table 4-1.

Table 4-1. Adaptive Payments API operations
API operationDescription
CancelPreapprovalCancels a preapproval
ConvertCurrencyObtains foreign exchange currency conversion rates for a list of amounts
ExecutePaymentExecutes a payment
GetPaymentOptionsObtains the settings specified with the SetPaymentOptions API operation
PayTransfers fund from a sender’s (buyer’s) PayPal account to on or more receivers’ PayPal accounts (up to six recipients)
PaymentDetailsObtains information about a payment set up with the Pay API operation
PreapprovalSets up preapprovals, which is an approval to make future payments on the sender’s behalf
PreapprovalDetailsObtains information about a preapproval
RefundRefunds all or part of a payment
SetPaymentOptionsSets payment options

Adaptive Payments Permission Levels

PayPal’s Adaptive Payments adds an additional layer of security and permission levels over other API functionality. Most of the Adaptive Payments API operations are available to all API callers, but some of the higher-level features are limited to those with advanced permission levels.

A merchant using a third-party Adaptive Payments application, at minimum, must have the same permission level required for the Adaptive Payments APIs called by the application. If, for example, your application supports chained payments but the merchant using it has a standard permission level, chained payments will not work for that merchant. Table 4-2 outlines the current permission restrictions. This list is subject to change, and so I suggest referring to http://www.paypal.com or http://www.x.com for more information about current permission levels.

Note

If you are distributing an application based upon Adaptive Payments, I highly recommend putting the required permission levels in your application distribution notes.

Table 4-2. Adaptive Payments permission levels and allowed features
Standard permission levelAdvanced permission level

Simple and parallel payments with explicit approval

Get payment details

Refunds

Currency conversion

Chained payments

Payments with implicit approval

Preapprovals and preapproval cancellations

Get preapproval details

Pay request with CREATE action type

SetPaymentOptions API operation

GetPaymentOptions API operation

ExecutePayment API operation

Personal payments

Adaptive Payments Application Workflows

Adaptive Payments facilitates payments between a sender and one or more receivers of that payment. You as the application owner and your application are the caller of the Adaptive Payments API operations. The application owner must have a PayPal business-level account, but senders and receivers can have a PayPal account of any type. Given the complexity and power for Adaptive Payments, you can be both the application owner and the receiver of payments. Outlined in Figure 4-1, this is referred to as a simple payment, where a sender makes a payment to a single recipient. This type of payment is equivalent to what is done with Express Checkout.

Adaptive Payments owner as recipient workflow
Figure 4-1. Adaptive Payments owner as recipient workflow

The Adaptive Payments API allows you and your application to act as an intermediary that facilitates payments for others, without you being a recipient of the funds, as outlined in Figure 4-2. This is referred to as a parallel payment, in which the sender transmits a single payment to multiple recipients and can see who those recipients are. Parallel payments are commonly used in aggregated shopping, and allow a customer to order from multiple vendors with a single shopping cart.

Adaptive Payments owner as intermediary workflow: parallel
Figure 4-2. Adaptive Payments owner as intermediary workflow: parallel

Another way in which your application can function as an intermediary is facilitating a chained payment, outlined in Figure 4-3. In a chained transaction, your application receives the payment, and the funds are then split between multiple recipients on the backend. In a chained setup, your application can take a percentage off the top and then disperse the remaining funds to the other recipients. You can even set up what is called a delayed chained payment. This can be used when your secondary receivers are required to ship goods, for example, before they receive their payment for the transaction.

Adaptive Payments owner as intermediary workflow: chained
Figure 4-3. Adaptive Payments owner as intermediary workflow: chained

And finally, your application also can be the sender of the transaction, outlined in Figure 4-4. This could be used to facilitate commission payments to your sales reps, for example.

Adaptive Payments application as sender
Figure 4-4. Adaptive Payments application as sender

So, as you can see from these workflow diagrams and notes, Adaptive Payments is probably the most powerful payment method PayPal provides to application developers.

Payment Approval and Payment Flows

Once a payment transaction via your application has been submitted, the sender of the payment must take an additional step and approve the transfer of funds. This can be one of four different payment approval types: Explicit, Preapproved, Implicit, or Guest Payments.

Explicit Payments

Explicit Payments require the sender to log into PayPal.com and approve each individual payment. This is the traditional method for paying via PayPal and is the only option a sender has, unless he has previously set up a preapproval agreement with you, or unless the sender is also the application provider. You can control the interaction between your application and PayPal during the transaction process by providing URLs for redirecting the sender, dependent on the situation. Figure 4-5 outlines an Explicit Payment flow, which consists of the following steps:

Explicit Payment flow
Figure 4-5. Explicit Payment flow
  1. Your application sends a Pay request to PayPal.

  2. PayPal responds with a key that you use to redirect the sender to PayPal.

  3. You redirect the sender to PayPal.com.

  4. The sender approves the transfer of the payment, and PayPal redirects the sender to a URL you specify.

  5. PayPal sends both you and the sender an email summarizing the payment that was made.

Preapproved Payments

Preapproved Payments require senders to log into PayPal.com and set up preapprovals for future payments, for example, payments to a supplier they use frequently. Once the preapproval is set up, payments automatically are considered approved, and the sender will not have to log in to approve payments to that vendor in the future. During the preapproval setup process, the sender can specify the following:

  • Duration of the preapproval, including the start date and end date. This comes in handy if you are paying a specific vendor for supplies on a particular project that has a known start and end date, for example.

  • The maximum amount being approved at one time.

  • The maximum number of payments allowed for the vendor.

Figure 4-6 outlines a Preapproved Payment flow, which consists of the following steps:

  1. Your application sends a preapproval request to PayPal.

  2. PayPal responds with a key, called a preapproval key, that you use in redirecting the sender to PayPal. If the preapproval has already been established, you will use this key to complete payments automatically on the sender’s behalf.

  3. You redirect the sender to PayPal.

  4. After the sender approves the preapproval, PayPal redirects the sender to a URL you specify.

  5. PayPal sends both you and the sender an email summarizing the payment that was made.

Once the sender approves the preapproval setup, you can make payments on behalf of the sender directly, as outlined in Figure 4-7:

  1. Your application sends a Pay request to PayPal that includes a preapproval key identifying the payment agreement.

  2. PayPal responds with a payment key that you can use for other API functions.

Preapproved Payment flow
Figure 4-6. Preapproved Payment flow
Preapproved Payment direct sending
Figure 4-7. Preapproved Payment direct sending

Implicit Payments

Implicit Payments are payments sent directly by your application in which the sender and API caller are using the same PayPal account. Since your account is the one sending the payments, no approval is necessary for the payment transaction. Figure 4-8 outlines an Implicit Payment:

  1. Your application sends a Pay request to PayPal.

  2. PayPal responds with a key to use for other API operations.

Implicit Payment flow
Figure 4-8. Implicit Payment flow

Guest Payments

Adaptive Payments also supports Guest Payments, where the sender can pay using her credit card, similar to using Direct Payment. The recipient must have either a business- or premier-level PayPal account. Guest Payments are handled in the same manner as Explicit Payments, except that the sender provides credit card information directly on the PayPal payment screen.

Adaptive Payments API Operations in Depth

In the remaining pages of this chapter, we look at the following Adaptive Payments API operations in depth:

  • Pay API

  • SetPaymentOptions API

  • ExecutePayment API

A complete list of all the defined Adaptive Payments API operations can be found at http://developer.paypal.com or http://x.com.

Pay API Operation

All payments made via Adaptive Payments have the same required fields. These are outlined in Table 4-3.

Table 4-3. Common required fields
FieldDescription
actionTypeWill be one of three possible values:
  • PAY: Use this value if not using the request in combination with ExecutePaymentRequest.

  • CREATE: Use to set up payment instructions with a SetPaymentOptions request and then execute at a later time with an ExecutePaymentRequest.

  • PAY_PRIMARY: Used for chained payments only. This allows you to delay payments to secondary receivers at the time of the transaction and process only the primary receiver. To process the secondary payments, initiate ExecutePaymentRequest and pass the pay key obtained from the PayResponse.

receiverList.receiver(n).emailThe receiver’s email address.
receiverList.receiver(n).amountThe amount to be credited to the receiver’s account.
currencyCodeThe code for the currency in which the payment is made. You can specify only one currency, regardless of the number of receivers.
cancelUrlThe URL for sender redirection if the sender cancels the payment approval. This value is required, but used only for explicit payments.
returnUrlThe URL for sender redirection after completion of the payment. This value is required, but used only for explicit payments.
requestEnvelope.errorLanguageThe code for the language used when returning errors (must be en_US).

If you are performing a parallel payment, you must provide the additional fields outlined in Table 4-4.

Table 4-4. Parallel payments fields
FieldDescription
receiverList.receiver(n).emailThe email address for each receiver. n is an integer between 0 and 5, allowing for a maximum of six receivers.
receiverList.receiver(n).amountThe amount to send to each corresponding receiver.

If you are performing a chained payment, you must provide the additional fields outlined in Table 4-5.

Table 4-5. Chained payments fields
FieldDescription
receiverList.receiver(n).emailThe email address for each receiver. n is an integer between 0 and 5, for a total of one primary receiver and one to five secondary receivers.
receiverList.receiver(n).amountThe amount to send to each corresponding receiver.
receiverList.receiver(n).primarySet this value to true to indicate that this is a chained payment. Only one receiver can be the primary receiver.

As discussed previously, a payment requires explicit approval by default. To initiate the approval process, your application must redirect the sender to PayPal as follows:

https://www.paypal.com/webscr?cmd=_ap-payment&paykey=value

If you are the API caller and you specify your email address in the senderEmail field, PayPal will implicitly approve the payment without redirecting to PayPal. You can also use a preapproval to execute the payment and avoid explicit approval. The required preapproval fields are outlined in Table 4-6.

Table 4-6. Preapproval fields
FieldDescription
preapprovalKeyPreapproval key for the approval set up between you and the sender
pinSender’s personal identification number, if one was specified when the approval agreement was created

SetPaymentOptions API Operation

The SetPaymentOptions API is used to specify settings for a payment of the actionType CREATE. SetPaymentOptions has four different request fields, outlined in Table 4-7.

Table 4-7. SetPaymentOptionsRequest fields
FieldDescriptions
displayOptions

The container used to specify which images should be used when emailing customers.

initiatingEntity

The PayPal financial partner initiating the payment. These must be set up via the Admin tool prior to using the PayPal APIs.

payKey

This field identifies the payment for which you wish to set up payment options. This is the key that is returned in the PayResponse message.

requestEnvelope

This is required information common to each API operation. This would include things such as the language in which error messages are displayed.

Next, we look at the different request fields and their additional values.

displayOptions

displayOptions has two optional fields that you can specify, outlined in Table 4-8.

Table 4-8. displayOptions fields
FieldDescription
emailHeaderImageUrl

The URL that points to the location of the image used in the header of customer emails. The image dimensions are 43 pixels high x 240 pixels wide.

emailMarketingImageUrl

The URL that points to the location of the marketing image used in customer emails. The image dimensions are 80 pixels high x 530 pixels wide.

initiatingEntity

initiatingEntity has only one optional field, outlined in Table 4-9.

Table 4-9. initiatingEntity field
FieldDescription
institutionCustomer

Details about the party that initiated this payment. This payment is made by the API caller on behalf of the initiating party. The initiating party can be an institution or a customer of that institution. The initiating party must be set up by PayPal Merchant Services.

institutionCustomer has the additional fields outlined in Table 4-10.

Table 4-10. institutionCustomer fields
FieldDescription
countryCode

The two-character country code of the end user’s home country (required)

displayName

The full name of the consumer as known by the institution (required)

email

The email address of the consumer as known by the institution

firstName

The first name of the consumer as known by the institution (required)

institutionCustomerId

The unique identifier assigned to the consumer by the institution (required)

institutionId

The unique identifier assigned to the institution (required)

lastName

The last name of the consumer as known by the institution (required)

requestEnvelope

requestEnvelope has two fields, outlined in Table 4-11.

Table 4-11. requestEnvelope fields
FieldDescription
detailLevel

The level of detail required by the client application for components such as Item and Transaction. One possible value is ReturnAll , which provides the maximum level of detail (default).

errorLanguage

The RFC 3066 language in which error messages are returned (required). By default, it is en_US, which is the only language currently supported.

ResponseEnvelope

Once you execute your SetPaymentOptions request, you will receive a ResponseEnvelope that contains information about the success or failure of the SetPaymentOptions request. The response fields are outlined in Table 4-12.

Table 4-12. SetPaymentOptions response fields
FieldDescription
ack

The acknowledgment code. Possible values are:

  • Success: The operation completed successfully.

  • Failure: The operation failed.

  • Warning: Warning.

  • SuccessWithWarning: The operation completed successfully, but there is a warning message.

  • FailureWithWarning: The operation failed with a warning message.

buildThe build number; used only by Developer Technical Support.
correlationIdThe correlation ID; used only by Developer Technical Support.
timestampThe date on which the response was sent. The time currently is not supported.

ExecutePayment API Operation

The ExecutePayment API operation allows you to execute a payment setup with the Pay API operation using the actionType CREATE. The request is comprised of two fields, outlined in Table 4-13.

Table 4-13. ExecutePayment request fields
FieldDescription
payKey

The pay key that identifies the payment to be executed. This is the pay key returned in the PayResponse message.

requestEnvelope

Information common to each API operation, such as the language in which an error message is returned.

Additionally, requestEnvelope contains two subfields, outlined in Table 4-14.

Table 4-14. requestEnvelope fields
FieldDescription
detailLevel

The level of detail required by the client application for components such as Item and Transaction. One possible value is ReturnAll , which provides the maximum level of detail (default).

errorLanguage

The RFC 3066 language in which error messages are returned (required). By default, it is en_US, which is the only language currently supported.

ExecutePayment returns several elements in its response, outlined in Table 4-15.

Table 4-15. ExecutePayment response fields
FieldDescription
payErrorList

Information about why a payment failed.

paymentExecStatus

The status of the payment. Possible values are:

  • CREATED: The payment request was received, and funds will be transferred once the payment is approved.

  • COMPLETED: The payment was successful.

  • INCOMPLETE: Some transfers succeeded and some failed for a parallel payment.

  • ERROR: The payment failed, and either all attempted transfers failed or all completed transfers were successfully reversed.

  • REVERSALERROR: One or more transfers failed when attempting to reverse a payment.

responseEnvelope

Common response information, including a timestamp and the response acknowledgment status.

Additional values and information related to the ExecutePayment response fields can be found in the online documentation located at http://www.x.com/community/ppx/documentation.

Adaptive Payments Integration

PayPal provides an Integration Wizard for Adaptive Payments as well. It can be found at https://www.paypal-labs.com/integrationwizard/adaptive/main.php. The wizard for Adaptive Payments contains only five steps.

The Integration Wizard starts by presenting an overview of the different types of payment methods you can implement via Adaptive Payments, as shown in Figure 4-9.

Step 1 asks you for your programming language and whether you are working with the sandbox or a live implementation (see Figure 4-10).

Step 2 allows you to download the paypalplatform.php code, shown in Example 4-1 (see Figure 4-11). This code contains all the core handlers for Adaptive Payments information. You will need to provide your API credentials in this file. For instructions on creating those credentials, refer to Creating an API Signature. Once you have completed your testing and are ready to go live, change $Env="sandbox" to $Env="live". This will change the endpoint to the live PayPal endpoint.

Adaptive Payments Integration Wizard overview
Figure 4-9. Adaptive Payments Integration Wizard overview

Step 3 generates the code to implement for your Preapproval Flow handler, shown in Example 4-2 (see Figure 4-12).

Step 4 generates the different payment type handlers for use in your code (see Figure 4-13). You will have an Implicit Payment handler (Example 4-3), a Basic Payment Handler (Example 4-4), a Parallel Payment Handler (Example 4-5), and a Chained Payment Handler (Example 4-6).

Step 5 completes the Integration Wizard, as shown in Figure 4-14.

Adaptive Payments Integration Wizard step 1
Figure 4-10. Adaptive Payments Integration Wizard step 1
Adaptive Payments Integration Wizard step 2
Figure 4-11. Adaptive Payments Integration Wizard step 2
Adaptive Payments Integration Wizard step 3
Figure 4-12. Adaptive Payments Integration Wizard step 3
Adaptive Payments Integration Wizard step 4
Figure 4-13. Adaptive Payments Integration Wizard step 4
Adaptive Payments Integration Wizard step 5
Figure 4-14. Adaptive Payments Integration Wizard step 5
Example 4-1. paypalplatform.php

<?php
 /********************************************
 PayPal Adaptive Payments API Module
  
 Defines all the global variables and the wrapper functions 
 ********************************************/
 $PROXY_HOST = '127.0.0.1';
 $PROXY_PORT = '808';

 $Env = "sandbox";

 //------------------------------------
 // PayPal API Credentials
 // Replace <API_USERNAME> with your API Username
 // Replace <API_PASSWORD> with your API Password
 // Replace <API_SIGNATURE> with your Signature 
 //------------------------------------
 $API_UserName = "<API_USERNAME>";
 $API_Password = "<API_PASSWORD>";
 $API_Signature = "<API_SIGNATURE>";
 // AppID is preset for sandbox use
 //   If your application goes live, you will be assigned a value for the live 
 //   environment by PayPal as part of the live onboarding process.
 $API_AppID = "APP-80W284485P519543T";
 $API_Endpoint = "";
 
 if ($Env == "sandbox") 
 {
  $API_Endpoint = "https://svcs.sandbox.paypal.com/AdaptivePayments";
 }
 else
 {
  $API_Endpoint = "https://svcs.paypal.com/AdaptivePayments";
}

 $USE_PROXY = false;

 if (session_id() == "") 
  session_start();

 function generateCharacter () {
  $possible = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
  return $char;
 }

 function generateTrackingID () {
 $GUID = generateCharacter().generateCharacter().generateCharacter();
 $GUID .=generateCharacter().generateCharacter().generateCharacter();
 $GUID .=generateCharacter().generateCharacter().generateCharacter();
 return $GUID;
 }

 /*
 '-------------------------------------------------------------------------------
 ' Purpose: Prepares the parameters for the Refund API Call.
 '   The API credentials used in a Pay call can make the Refund call
 '   against a payKey, or a tracking id, or to specific receivers of a payKey or 
 '   a tracking id that resulted from the Pay call.
 '
 '   A receiver itself with its own API credentials can make a Refund call against 
 '   the transactionId corresponding to their transaction.
 '   The API credentials used in a Pay call cannot use transactionId to issue a refund
 '   for a transaction for which they themselves were not the receiver.
 '
 '   If you do specify specific receivers, you must provide the amounts as well.
 '   If you specify a transactionId, then only the receiver of that transactionId 
 '   is affected. Therefore the receiverEmailArray and receiverAmountArray should 
 '   have 1 entry each if you do want to give a partial refund.
 ' Inputs:
 '
 ' Conditionally Required:
 '  One of the following:  payKey or trackingId or trasactionId or
 '                         (payKey and receiverEmailArray and receiverAmountArray) or
 '                         (trackingId and receiverEmailArray and receiverAmountArray)
 '                         or (transactionId and receiverEmailArray 
 '                         and receiverAmountArray)
 ' Returns: 
 '  The NVP Collection object of the Refund call response.
 '------------------------------------------------------------------------------------
 */
 function CallRefund( $payKey, $transactionId, $trackingId, 
          $receiverEmailArray, $receiverAmountArray )
 {
  /* Gather the information to make the Refund call.
   The variable nvpstr holds the name-value pairs.
  */

  $nvpstr = "";

  // conditionally required fields
  if ("" != $payKey)
  {
   $nvpstr = "payKey=" . urlencode($payKey);
   if (0 != count($receiverEmailArray))
   {
    reset($receiverEmailArray);
    while (list($key, $value) = each($receiverEmailArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").email=" . urlencode($value);
     }
    }
   }
   if (0 != count($receiverAmountArray))
   {
    reset($receiverAmountArray);
    while (list($key, $value) = each($receiverAmountArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").amount=" . urlencode($value);
     }
    }
   }
  }
  elseif ("" != $trackingId)
  {
   $nvpstr = "trackingId=" . urlencode($trackingId);
   if (0 != count($receiverEmailArray))
   {
    reset($receiverEmailArray);
    while (list($key, $value) = each($receiverEmailArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").email=" . urlencode($value);
     }
    }
   }
   if (0 != count($receiverAmountArray))
   {
    reset($receiverAmountArray);
    while (list($key, $value) = each($receiverAmountArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").amount=" . urlencode($value);
     }
    }
   }
  }
  elseif ("" != $transactionId)
  {
   $nvpstr = "transactionId=" . urlencode($transactionId);
   // the caller should only have 1 entry in the email and amount arrays
   if (0 != count($receiverEmailArray))
   {
    reset($receiverEmailArray);
    while (list($key, $value) = each($receiverEmailArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").email=" . urlencode($value);
     }
    }
   }
   if (0 != count($receiverAmountArray))
   {
    reset($receiverAmountArray);
    while (list($key, $value) = each($receiverAmountArray))
    {
     if ("" != $value)
     {
      $nvpstr .= "&receiverList.receiver(" . $key . ").amount=" . urlencode($value);
     }
    }
   }
  }

  /* Make the Refund call to PayPal */
  $resArray = hash_call("Refund", $nvpstr);

  /* Return the response array */
  return $resArray;
 }
 
 /*
 '------------------------------------------------------------------------------------
 ' Purpose: Prepares the parameters for the PaymentDetails API Call.
 '   The PaymentDetails call can be made with either
 '   a payKey, a trackingId, or a transactionId of a previously successful Pay call.
 ' Inputs:
 '
 ' Conditionally Required:
 '  One of the following:  payKey or transactionId or trackingId
 ' Returns: 
 '  The NVP Collection object of the PaymentDetails call response.
 '------------------------------------------------------------------------------------
 */
 function CallPaymentDetails( $payKey, $transactionId, $trackingId )
 {
  /* Gather the information to make the PaymentDetails call.
   The variable nvpstr holds the name-value pairs.
  */

  $nvpstr = "";

  // conditionally required fields
  if ("" != $payKey)
  {
   $nvpstr = "payKey=" . urlencode($payKey);
  }
  elseif ("" != $transactionId)
  {
   $nvpstr = "transactionId=" . urlencode($transactionId);
  }
  elseif ("" != $trackingId)
  {
   $nvpstr = "trackingId=" . urlencode($trackingId);
  }

  /* Make the PaymentDetails call to PayPal */
  $resArray = hash_call("PaymentDetails", $nvpstr);

  /* Return the response array */
  return $resArray;
 }

 /*
 '------------------------------------------------------------------------------------
 ' Purpose: 	Prepares the parameters for the Pay API Call.
 ' Inputs:
 '
 ' Required:
 '
 ' Optional:
 '
 ' Returns: 
 '  The NVP Collection object of the Pay call response.
 '------------------------------------------------------------------------------------
 */
 function CallPay( $actionType, $cancelUrl, $returnUrl, $currencyCode, 
     $receiverEmailArray, $receiverAmountArray, $receiverPrimaryArray, 
     $receiverInvoiceIdArray, $feesPayer, $ipnNotificationUrl, $memo, 
     $pin, $preapprovalKey, $reverseAllParallelPaymentsOnError, 
     $senderEmail, $trackingId )
 {
  /* Gather the information to make the Pay call.
   The variable nvpstr holds the name-value pairs.
  */

  // required fields
  $nvpstr = "actionType=" . urlencode($actionType) . "&currencyCode=";  
  $nvpstr .= urlencode($currencyCode) . "&returnUrl=";
  $nvpstr .= urlencode($returnUrl) . "&cancelUrl=" . urlencode($cancelUrl);

  if (0 != count($receiverAmountArray))
  {
   reset($receiverAmountArray);
   while (list($key, $value) = each($receiverAmountArray))
   {
    if ("" != $value)
    {
     $nvpstr .= "&receiverList.receiver(" . $key . ").amount=" . urlencode($value);
    }
   }
  }

  if (0 != count($receiverEmailArray))
  {
   reset($receiverEmailArray);
   while (list($key, $value) = each($receiverEmailArray))
   {
    if ("" != $value)
    {
     $nvpstr .= "&receiverList.receiver(" . $key . ").email=" . urlencode($value);
    }
   }
  }

  if (0 != count($receiverPrimaryArray))
  {
   reset($receiverPrimaryArray);
   while (list($key, $value) = each($receiverPrimaryArray))
   {
    if ("" != $value)
    {
     $nvpstr = $nvpstr . "&receiverList.receiver(" . $key . ").primary=" . 
             urlencode($value);
    }
   }
  }

  if (0 != count($receiverInvoiceIdArray))
  {
   reset($receiverInvoiceIdArray);
   while (list($key, $value) = each($receiverInvoiceIdArray))
   {
    if ("" != $value)
    {
     $nvpstr = $nvpstr . "&receiverList.receiver(" . $key . ").invoiceId=" . 
               urlencode($value);
    }
   }
  }

  // optional fields
  if ("" != $feesPayer)
  {
   $nvpstr .= "&feesPayer=" . urlencode($feesPayer);
  }

  if ("" != $ipnNotificationUrl)
  {
   $nvpstr .= "&ipnNotificationUrl=" . urlencode($ipnNotificationUrl);
  }

  if ("" != $memo)
  {
   $nvpstr .= "&memo=" . urlencode($memo);
  }

  if ("" != $pin)
  {
   $nvpstr .= "&pin=" . urlencode($pin);
  }

  if ("" != $preapprovalKey)
  {
   $nvpstr .= "&preapprovalKey=" . urlencode($preapprovalKey);
  }

  if ("" != $reverseAllParallelPaymentsOnError)
  {
   $nvpstr .= "&reverseAllParallelPaymentsOnError=";
   $nvpstr .= urlencode($reverseAllParallelPaymentsOnError);
  }

  if ("" != $senderEmail)
  {
   $nvpstr .= "&senderEmail=" . urlencode($senderEmail);
  }

  if ("" != $trackingId)
  {
   $nvpstr .= "&trackingId=" . urlencode($trackingId);
  }

  /* Make the Pay call to PayPal */
  $resArray = hash_call("Pay", $nvpstr);

  /* Return the response array */
  return $resArray;
 }

 /*
 '---------------------------------------------------------------------------
 ' Purpose: Prepares the parameters for the PreapprovalDetails API Call.
 ' Inputs:
 '
 ' Required:
 '  preapprovalKey:A preapproval key that identifies the agreement 
 '                 resulting from a previously successful Preapproval call.
 ' Returns: 
 '  The NVP Collection object of the PreapprovalDetails call response.
 '---------------------------------------------------------------------------
 */
 function CallPreapprovalDetails( $preapprovalKey )
 {
  /* Gather the information to make the PreapprovalDetails call.
   The variable nvpstr holds the name-value pairs.
  */

  // required fields
  $nvpstr = "preapprovalKey=" . urlencode($preapprovalKey);

  /* Make the PreapprovalDetails call to PayPal */
  $resArray = hash_call("PreapprovalDetails", $nvpstr);

  /* Return the response array */
  return $resArray;
 }

 /*
 '---------------------------------------------------------------------------
 ' Purpose: Prepares the parameters for the Preapproval API Call.
 ' Inputs:
 '
 ' Required:
 '
 ' Optional:
 '
 ' Returns: 
 '  The NVP Collection object of the Preapproval call response.
 '---------------------------------------------------------------------------
 */
 function CallPreapproval( $returnUrl, $cancelUrl, $currencyCode, 
        $startingDate, $endingDate, $maxTotalAmountOfAllPayments,
        $senderEmail, $maxNumberOfPayments, $paymentPeriod, $dateOfMonth, 
        $dayOfWeek, $maxAmountPerPayment, $maxNumberOfPaymentsPerPeriod, $pinType )
 {
  /* Gather the information to make the Preapproval call.
   The variable nvpstr holds the name-value pairs.
  */

  // required fields
  $nvpstr = "returnUrl=" . urlencode($returnUrl) . "&cancelUrl=" . urlencode($cancelUrl);
  $nvpstr .= "&currencyCode=" . urlencode($currencyCode) . "&startingDate=";
  $nvpstr .= urlencode($startingDate) . "&endingDate=" . urlencode($endingDate);
  $nvpstr .= "&maxTotalAmountOfAllPayments=" . urlencode($maxTotalAmountOfAllPayments);

  // optional fields
  if ("" != $senderEmail)
  {
   $nvpstr .= "&senderEmail=" . urlencode($senderEmail);
  }

  if ("" != $maxNumberOfPayments)
  {
   $nvpstr .= "&maxNumberOfPayments=" . urlencode($maxNumberOfPayments);
  }

  if ("" != $paymentPeriod)
  {
   $nvpstr .= "&paymentPeriod=" . urlencode($paymentPeriod);
  }

  if ("" != $dateOfMonth)
  {
   $nvpstr .= "&dateOfMonth=" . urlencode($dateOfMonth);
  }

  if ("" != $dayOfWeek)
  {
   $nvpstr .= "&dayOfWeek=" . urlencode($dayOfWeek);
  }

  if ("" != $maxAmountPerPayment)
  {
   $nvpstr .= "&maxAmountPerPayment=" . urlencode($maxAmountPerPayment);
  }

  if ("" != $maxNumberOfPaymentsPerPeriod)
  {
   $nvpstr .= "&maxNumberOfPaymentsPerPeriod=" . urlencode($maxNumberOfPaymentsPerPeriod);
  }

  if ("" != $pinType)
  {
   $nvpstr .= "&pinType=" . urlencode($pinType);
  }

  /* Make the Preapproval call to PayPal */
  $resArray = hash_call("Preapproval", $nvpstr);

  /* Return the response array */
  return $resArray;
 }

 /**
   '----------------------------------------------------------------------------------
   * hash_call: Function to perform the API call to PayPal using API signature
   * @methodName is name of API method.
   * @nvpStr is nvp string.
   * returns an associative array containing the response from the server.
   '----------------------------------------------------------------------------------
 */
 function hash_call($methodName, $nvpStr)
 {  //declaring of global variables
  global $API_Endpoint, $API_UserName, $API_Password, $API_Signature, $API_AppID;
  global $USE_PROXY, $PROXY_HOST, $PROXY_PORT;

  $API_Endpoint .= "/" . $methodName;
  //setting the curl parameters.
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,$API_Endpoint);
  curl_setopt($ch, CURLOPT_VERBOSE, 1);

  //turning off the server and peer verification(TrustManager Concept).
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

  curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
  curl_setopt($ch, CURLOPT_POST, 1);

  // Set the HTTP Headers
  curl_setopt($ch, CURLOPT_HTTPHEADER,  array(
  'X-PAYPAL-REQUEST-DATA-FORMAT: NV',
  'X-PAYPAL-RESPONSE-DATA-FORMAT: NV',
  'X-PAYPAL-SECURITY-USERID: ' . $API_UserName,
  'X-PAYPAL-SECURITY-PASSWORD: ' .$API_Password,
  'X-PAYPAL-SECURITY-SIGNATURE: ' . $API_Signature,
  'X-PAYPAL-SERVICE-VERSION: 1.3.0',
  'X-PAYPAL-APPLICATION-ID: ' . $API_AppID
  ));

    //if USE_PROXY constant set to TRUE in Constants.php, 
    //then only proxy will be enabled.
  //Set proxy name to PROXY_HOST and port number to PROXY_PORT in constants.php 
  if($USE_PROXY)
   curl_setopt ($ch, CURLOPT_PROXY, $PROXY_HOST. ":" . $PROXY_PORT); 

  // RequestEnvelope fields
  $detailLevel = urlencode("ReturnAll"); // See DetailLevelCode in the WSDL 
                                         // for valid enumerations
  $errorLanguage = urlencode("en_US");  // This should be the standard RFC 
                                        // 3066 language identification tag, 
                                        // e.g., en_US

 // NVPRequest for submitting to server
 $nvpreq = "requestEnvelope.errorLanguage=$errorLanguage&requestEnvelope";
 $nvpreq .= "detailLevel=$detailLevel&$nvpStr";

  //setting the nvpreq as POST FIELD to curl
  curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);

  //getting response from server
  $response = curl_exec($ch);

  //converting NVPResponse to an Associative Array
  $nvpResArray=deformatNVP($response);
  $nvpReqArray=deformatNVP($nvpreq);
  $_SESSION['nvpReqArray']=$nvpReqArray;

  if (curl_errno($ch)) 
  {
   // moving to display page to display curl errors
     $_SESSION['curl_error_no']=curl_errno($ch) ;
     $_SESSION['curl_error_msg']=curl_error($ch);

     //Execute the Error handling module to display errors. 
  } 
  else 
  {
    //closing the curl
    curl_close($ch);
  }

  return $nvpResArray;
 }

 /*'----------------------------------------------------------------------------
  Purpose: Redirects to PayPal.com site.
  Inputs:  $cmd is the querystring
  Returns: 
 -------------------------------------------------------------------------------
 */
 function RedirectToPayPal ( $cmd )
 {
  // Redirect to paypal.com here
  global $Env;

  $payPalURL = "";

  if ($Env == "sandbox") 
  {
   $payPalURL = "https://www.sandbox.paypal.com/webscr?" . $cmd;
  }
  else
  {
   $payPalURL = "https://www.paypal.com/webscr?" . $cmd;
  }

  header("Location: ".$payPalURL);
 }

 
 /*'----------------------------------------------------------------------------
   * This function will take NVPString and convert it to an Associative Array 
   * and then will decode the response.
   * It is useful to search for a particular key and display arrays.
   * @nvpstr is NVPString.
   * @nvpArray is Associative Array.
    ----------------------------------------------------------------------------
   */
 function deformatNVP($nvpstr)
 {
  $intial=0;
  $nvpArray = array();

  while(strlen($nvpstr))
  {
   //postion of Key
   $keypos= strpos($nvpstr,'=');
   //position of value
   $valuepos = strpos($nvpstr,'&') ? strpos($nvpstr,'&'): strlen($nvpstr);

   /*getting the Key and Value values and storing in a Associative Array*/
   $keyval=substr($nvpstr,$intial,$keypos);
   $valval=substr($nvpstr,$keypos+1,$valuepos-$keypos-1);
   //decoding the respose
   $nvpArray[urldecode($keyval)] =urldecode( $valval);
   $nvpstr=substr($nvpstr,$valuepos+1,strlen($nvpstr));
      }
  return $nvpArray;
 }
?>
Example 4-2. preapprovalflow.php

<?php

//-------------------------------------------------
// When you integrate this code,
// look for TODO as an indication
// that you may need to provide a value or take 
// action before executing this code.
//-------------------------------------------------

require_once ("paypalplatform.php");


// ==================================
// PayPal Platform Preapproval Module
// ==================================

// Request specific required fields
$cancelUrl = "http://wwww.yoursite.com/PreapprovalCancelHandler.xxx";
            // TODO - The landing page on your site where the customer 
            // is sent when they cancel the Preapproval action on PayPal
$returnUrl = "http://wwww.yoursite.com/PreapprovalReturnHandler.xxx";
            // TODO - The landing page on your site where the customer 
            // returns to after the Preapproval is agreed to on PayPal
$currencyCode = "USD";
$startingDate = "2009-06-17T13:00:00"; // TODO - The datetime when this 
                                       // preapproval agreement starts, 
                                       // cannot be in the past
$endingDate = "2009-09-17T13:00:00"; // TODO - The datetime when this 
                                     // preapproval agreement ends
$maxTotalAmountOfAllPayments = "2000"; // TODO - The maximum total amount 
                                       // of all payments, cannot exceed 
                                       // $2,000 USD or the equivalent 
                                       // in other currencies

// Request specific optional fields
//   Provide a value for each field that you want to include in the request; 
//   if left as an empty string, the field will not be passed in the request.
$senderEmail = ""; // TODO - The PayPal account email address of the sender
$maxNumberOfPayments = ""; // TODO - The maximum number of payments for 
                           // this preapproval
$paymentPeriod = ""; // TODO - If this preapproval is for periodic payments, 
                     // this defines the payment period as one of the following:
                     //   NO_PERIOD_SPECIFIED
                     //   DAILY - each day
                     //   WEEKLY - each week
                     //   BIWEEKLY - every other week
                     //   SEMIMONTHLY - twice a month
                     //   MONTHLY - each month
                     //   ANNUALLY - each year
$dateOfMonth = ""; // TODO - The day of the month on which a monthly payment is 
                   // to be made, number between 1 and 31
$dayOfWeek = ""; // TODO - The day of the week that a weekly payment is to be 
                 // made, see DayOfWeek in the WSDL for valid enumerations
$maxAmountPerPayment = ""; // TODO - The maximum amount per payment, it 
                           //cannot exceed the value in maxTotalAmountOfAllPayments
$maxNumberOfPaymentsPerPeriod = ""; // TODO - The maximum number of all 
                                    // payments per period
$pinType = ""; // TODO - Whether or not a personal identification number (PIN) 
               // is required each time a Payment is made via the Pay API call
               //   NOT_REQUIRED - a PIN is not required (default)
               //   REQUIRED - a PIN is required; the sender must specify a PIN 
               //    when setting up the preapproval on PayPal, and the PIN must 
               //    be in the request of each subsequent Pay API call corresponding 
               //    to this preapproval
               // A PIN is typically required if a Pay call against the preapproval 
               // can be made for a purchase or payment in which the sender takes an
               // explicit action to send the money.

//-------------------------------------------------
// Make the Preapproval API call
//
// The CallPreapproval function is defined in the paypalplatform.php file,
// which is included at the top of this file.
//-------------------------------------------------
$resArray = CallPreapproval ($returnURL, $cancelURL, $currencyCode, $startingDate, 
        $endingDate, $maxTotalAmountOfAllPayments, $senderEmail, 
        $maxNumberOfPayments, $paymentPeriod, $dateOfMonth, $dayOfWeek,
        $maxAmountPerPayment, $maxNumberOfPaymentsPerPeriod, $pinType
);

$ack = strtoupper($resArray["responseEnvelope.ack"]);
if($ack=="SUCCESS")
{
 $cmd = "cmd=_ap-preapproval&preapprovalkey=" . urldecode($resArray["preapprovalKey"]);
 RedirectToPayPal ( $cmd );
} 
else  
{
 //Display a user-friendly Error on the page using any of the following error information 
 //returned by PayPal.
 //TODO - There can be more than 1 error, so check for "error(1).errorId", 
 //then "error(2).errorId", and so on until you find no more errors.
 $ErrorCode = urldecode($resArray["error(0).errorId"]);
 $ErrorMsg = urldecode($resArray["error(0).message"]);
 $ErrorDomain = urldecode($resArray["error(0).domain"]);
 $ErrorSeverity = urldecode($resArray["error(0).severity"]);
 $ErrorCategory = urldecode($resArray["error(0).category"]);
 
 echo "Preapproval API call failed. ";
 echo "Detailed Error Message: " . $ErrorMsg;
 echo "Error Code: " . $ErrorCode;
 echo "Error Severity: " . $ErrorSeverity;
 echo "Error Domain: " . $ErrorDomain;
 echo "Error Category: " . $ErrorCategory;
}

?>
Example 4-3. implicitpayment.php

<?php

//-------------------------------------------------
// When you integrate this code,
// look for TODO as an indication
// that you may need to provide a value or take 
// action before executing this code.
//-------------------------------------------------

require_once ("paypalplatform.php");


// ==================================
// PayPal Platform Implicit Payment Module
// ==================================

// Request specific required fields
$senderEmail = ""; // TODO - The PayPal account email address of the sender
                   // Think of it as required for an implicit payment and
                   // set to the same account that owns the API credentials
$actionType = "PAY";
$cancelUrl = "https://NoOp";	// There is no approval step for implicit payment
$returnUrl = "https://NoOp";	// There is no approval step for implicit payment
$currencyCode = "USD";

// An implicit payment can be a simple or parallel or chained payment
// TODO - Specify the receiver emails
//        Remove or set to an empty string the array entries for receivers 
//        that you do not have for a simple payment, specify only 
//        receiver0email, and remove the other array entries
$receiverEmailArray = array(
  'receiver0email',
  'receiver1email',
  'receiver2email',
  'receiver3email',
  'receiver4email',
  'receiver5email'
  );

// TODO - Specify the receiver amounts as the amount of money, 
//        for example, '5' or '5.55'. Remove or set to an empty 
//        string the array entries for receivers that you do not have
//        for a simple payment, specify only receiver0amount, and remove 
//        the other array entries
$receiverAmountArray = array(
  'receiver0amount',
  'receiver1amount',
  'receiver2amount',
  'receiver3amount',
  'receiver4amount',
  'receiver5amount'
  );

// TODO - Specify values ONLY if you are doing a chained payment
//        If you are doing a chained payment, then set ONLY 1 receiver in the 
//        array to 'true' as the primary receiver, and set the other receivers
//        corresponding to those indicated in receiverEmailArray to 'false'
//        Make sure that you do NOT specify more values 
//        in this array than in the receiverEmailArray
$receiverPrimaryArray = array(
  '',
  '',
  '',
  '',
  '',
  ''
  );

// TODO - Set invoiceId to uniquely identify the transaction 
//        associated with each receiver
//        Set the array entries with value for receivers that you have
//        Each of the array values must be unique
$receiverInvoiceIdArray = array(
  '',
  '',
  '',
  '',
  '',
  ''
  );

// Request specific optional fields
//   Provide a value for each field that you want to include in the 
//   request; if left as an empty string, the field will not be passed 
//   in the request.
$feesPayer = ""; // For an implicit payment use case, this cannot be "SENDER"
$ipnNotificationUrl = "";
$memo = ""; // maxlength is 1000 characters
$pin = ""; // No pin for an implicit payment use case
$preapprovalKey = ""; // No preapprovalKey for an implicit use case
$reverseAllParallelPaymentsOnError = ""; // Only specify if you are doing a 
                                         //parallel payment as your implicit 
                                         //payment
$trackingId = generateTrackingID(); // generateTrackingID function is found 
                                    // in paypalplatform.php

//-------------------------------------------------
// Make the Pay API call
//
// The CallPay function is defined in the paypalplatform.php file,
// which is included at the top of this file.
//-------------------------------------------------
$resArray = CallPay ($actionType, $cancelUrl, $returnUrl, $currencyCode, 
      $receiverEmailArray, $receiverAmountArray, $receiverPrimaryArray, 
      $receiverInvoiceIdArray, $feesPayer, $ipnNotificationUrl, $memo, 
      $pin, $preapprovalKey, $reverseAllParallelPaymentsOnError, 
      $senderEmail, $trackingId
);

$ack = strtoupper($resArray["responseEnvelope.ack"]);
if($ack=="SUCCESS")
{
 // payKey is the key that you can use to identify the payment resulting 
 // from the Pay call.
 $payKey = urldecode($resArray["payKey"]);
 // paymentExecStatus is the status of the payment
 $paymentExecStatus = urldecode($resArray["paymentExecStatus"]);
} 
else  
{
 //Display a user-friendly Error on the page using any of the following 
 //error information returned by PayPal.
 //TODO - There can be more than 1 error, so check for "error(1).errorId", 
 // then "error(2).errorId", and so on until you find no more errors.
 $ErrorCode = urldecode($resArray["error(0).errorId"]);
 $ErrorMsg = urldecode($resArray["error(0).message"]);
 $ErrorDomain = urldecode($resArray["error(0).domain"]);
 $ErrorSeverity = urldecode($resArray["error(0).severity"]);
 $ErrorCategory = urldecode($resArray["error(0).category"]);
 
 echo "Preapproval API call failed. ";
 echo "Detailed Error Message: " . $ErrorMsg;
 echo "Error Code: " . $ErrorCode;
 echo "Error Severity: " . $ErrorSeverity;
 echo "Error Domain: " . $ErrorDomain;
 echo "Error Category: " . $ErrorCategory;
}
?>
Example 4-4. basicpayment.php

<?php

//-------------------------------------------------
// When you integrate this code,
// look for TODO as an indication
// that you may need to provide a value or take 
// action before executing this code.
//-------------------------------------------------

require_once ("paypalplatform.php");


// ==================================
// PayPal Platform Basic Payment Module
// ==================================

// Request specific required fields
$actionType = "PAY";
$cancelUrl = "https://mycancelurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid
                                    // cancelUrl for the web approval flow that 
                                    // immediately follows this Pay call
$returnUrl = "https://myreturnurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid
                                    // returnUrl for the web approval flow that 
                                    // immediately follows this Pay call
$currencyCode = "USD";

// A basic payment has 1 receiver.
// TODO - specify the receiver email
$receiverEmailArray = array(
 'receiver0email'
  );

// TODO - specify the receiver amount as the amount of money, for example, '5' or '5.55'
$receiverAmountArray = array(
 'receiver0amount'
 );

// For basic payment, no primary indicators are needed, so set empty array.
$receiverPrimaryArray = array();

// TODO - Set invoiceId to uniquely identify the transaction associated with the receiver
//  You can set this to the same value as trackingId if you wish
$receiverInvoiceIdArray = array(
  '1234abcd'
  );

// Request specific optional or conditionally required fields
//   Provide a value for each field that you want to include in the request; 
//   If left as an empty string, the field will not be passed in the request
$senderEmail = ""; // TODO - If you are executing the Pay call against a 
                   // preapprovalKey, you should set senderEmail
                   // It is not required if the web approval flow immediately 
                   // follows this Pay call
$feesPayer = "";
$ipnNotificationUrl = "";
$memo = ""; // maxlength is 1000 characters
$pin = ""; // TODO - If you are executing the Pay call against an 
           // existing preapproval that requires a pin, then you 
           // must set this
$preapprovalKey = ""; // TODO - If you are executing the Pay call 
                      // against an existing preapproval, set the 
                      // preapprovalKey here
$reverseAllParallelPaymentsOnError = ""; // Do not specify for basic payment
$trackingId = generateTrackingID(); // generateTrackingID function is 
                                    // found in paypalplatform.php

//-------------------------------------------------
// Make the Pay API call
//
// The CallPay function is defined in the paypalplatform.php file,
// which is included at the top of this file.
//-------------------------------------------------
$resArray = CallPay ($actionType, $cancelUrl, $returnUrl, $currencyCode, 
      $receiverEmailArray, $receiverAmountArray, $receiverPrimaryArray, 
      $receiverInvoiceIdArray, $feesPayer, $ipnNotificationUrl, $memo, 
      $pin, $preapprovalKey, $reverseAllParallelPaymentsOnError, 
      $senderEmail, $trackingId
);

$ack = strtoupper($resArray["responseEnvelope.ack"]);
if($ack=="SUCCESS")
{
 if ("" == $preapprovalKey)
 {
  // redirect for web approval flow
  $cmd = "cmd=_ap-payment&paykey=" . urldecode($resArray["payKey"]);
  RedirectToPayPal ( $cmd );
 }
 else
 {
  // payKey is the key that you can use to identify the payment resulting 
  // from the Pay call.
  $payKey = urldecode($resArray["payKey"]);
  // paymentExecStatus is the status of the payment
 $paymentExecStatus = urldecode($resArray["paymentExecStatus"]);
 }
} 
else  
{
 //Display a user-friendly Error on the page using any of the following 
 //error information returned by PayPal.
 //TODO - There can be more than 1 error, so check for "error(1).errorId", 
 //       then "error(2).errorId", and so on until you find no more errors.
 $ErrorCode = urldecode($resArray["error(0).errorId"]);
 $ErrorMsg = urldecode($resArray["error(0).message"]);
 $ErrorDomain = urldecode($resArray["error(0).domain"]);
 $ErrorSeverity = urldecode($resArray["error(0).severity"]);
 $ErrorCategory = urldecode($resArray["error(0).category"]);
 
 echo "Preapproval API call failed. ";
 echo "Detailed Error Message: " . $ErrorMsg;
 echo "Error Code: " . $ErrorCode;
 echo "Error Severity: " . $ErrorSeverity;
 echo "Error Domain: " . $ErrorDomain;
 echo "Error Category: " . $ErrorCategory;
}
?>
Example 4-5. parallelpayment.php

<?php

//-------------------------------------------------
// When you integrate this code,
// look for TODO as an indication
// that you may need to provide a value or take 
// action before executing this code.
//-------------------------------------------------

require_once ("paypalplatform.php");


// ==================================
// PayPal Platform Parallel Payment Module
// ==================================

// Request specific required fields
$actionType = "PAY";
$cancelUrl = "https://mycancelurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid 
                                    // cancelUrl for the web approval flow that 
                                    // immediately follows this Pay call
$returnUrl = "https://myreturnurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid 
                                    // returnUrl for the web approval flow that 
                                    // immediately follows this Pay call
$currencyCode = "USD";

// A parallel payment can be made among two to six receivers
// TODO - Specify the receiver emails
//        Remove or set to an empty string the array entries for receivers that you 
//        do not have
$receiverEmailArray = array(
  'receiver0email',
  'receiver1email',
  'receiver2email',
  'receiver3email',
  'receiver4email',
  'receiver5email'
 );

// TODO - Specify the receiver amounts as the amount of money, for example, '5' or '5.55'
//        Remove or set to an empty string the array entries for receivers that you 
//        do not have
$receiverAmountArray = array(
  'receiver0amount',
  'receiver1amount',
  'receiver2amount',
  'receiver3amount',
  'receiver4amount',
  'receiver5amount'
 );

// For parallel payment, no primary indicators are needed, so set empty array.
$receiverPrimaryArray = array();

// TODO - Set invoiceId to uniquely identify the transaction associated with 
//        each receiver
//        Set the array entries with value for receivers that you have
//        Each of the array values must be unique
$receiverInvoiceIdArray = array(
  '',
  '',
  '',
  '',
  '',
  ''
  );

// Request specific optional fields
//   Provide a value for each field that you want to include in the request;
//   if left as an empty string, the field will not be passed in the request
$senderEmail = ""; // TODO - If you are executing the Pay call against a 
                   // preapprovalKey, you should set senderEmail
                   // It is not required if the web approval flow immediately 
                   // follows this Pay call
$feesPayer = "";
$ipnNotificationUrl = "";
$memo = ""; // maxlength is 1000 characters
$pin = ""; // TODO - If you are executing the Pay call against an existing 
           // preapproval that requires a pin, then you must set this
$preapprovalKey = ""; // TODO - If you are executing the Pay call against 
                      // an existing preapproval, set the preapprovalKey here
$reverseAllParallelPaymentsOnError = ""; // TODO - Set this to "true" if you would 
                                         // like each parallel payment to be reversed 
                                         // if an error occurs
                                         // Defaults to "false" if you don't specify
$trackingId = generateTrackingID(); // generateTrackingID function is found 
                                    // in paypalplatform.php

//-------------------------------------------------
// Make the Pay API call
//
// The CallPay function is defined in the paypalplatform.php file,
// which is included at the top of this file.
//-------------------------------------------------
$resArray = CallPay ($actionType, $cancelUrl, $returnUrl, $currencyCode, 
      $receiverEmailArray, $receiverAmountArray, $receiverPrimaryArray, 
      $receiverInvoiceIdArray, $feesPayer, $ipnNotificationUrl, $memo, 
      $pin, $preapprovalKey, $reverseAllParallelPaymentsOnError, 
      $senderEmail, $trackingId
);

$ack = strtoupper($resArray["responseEnvelope.ack"]);
if($ack=="SUCCESS")
{
 if ("" == $preapprovalKey)
 {
  // redirect for web approval flow
  $cmd = "cmd=_ap-payment&paykey=" . urldecode($resArray["payKey"]);
  RedirectToPayPal ( $cmd );
 }
 else
 {
  // payKey is the key that you can use to identify the result from this Pay call
  $payKey = urldecode($resArray["payKey"]);
  // paymentExecStatus is the status of the payment
  $paymentExecStatus = urldecode($resArray["paymentExecStatus"]);
 }
} 
else  
{
 //Display a user-friendly Error on the page using any of the following error 
 //information returned by PayPal.
 //TODO - There can be more than 1 error, so check for "error(1).errorId", 
 //       then "error(2).errorId", and so on until you find no more errors.
 $ErrorCode = urldecode($resArray["error(0).errorId"]);
 $ErrorMsg = urldecode($resArray["error(0).message"]);
 $ErrorDomain = urldecode($resArray["error(0).domain"]);
 $ErrorSeverity = urldecode($resArray["error(0).severity"]);
 $ErrorCategory = urldecode($resArray["error(0).category"]);
 
 echo "Preapproval API call failed. ";
 echo "Detailed Error Message: " . $ErrorMsg;
 echo "Error Code: " . $ErrorCode;
 echo "Error Severity: " . $ErrorSeverity;
 echo "Error Domain: " . $ErrorDomain;
 echo "Error Category: " . $ErrorCategory;
}
?>
Example 4-6. chainedpayment.php

<?php

//-------------------------------------------------
// When you integrate this code,
// look for TODO as an indication
// that you may need to provide a value or take 
// action before executing this code.
//-------------------------------------------------

require_once ("paypalplatform.php");


// ==================================
// PayPal Platform Chained Payment Module
// ==================================

// Request specific required fields
$actionType = "PAY";
$cancelUrl = "https://mycancelurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid
                                    //  cancelUrl for the web approval flow that 
                                    // immediately follows this Pay call
$returnUrl = "https://myreturnurl"; // TODO - If you are not executing the Pay call 
                                    // for a preapproval, then you must set a valid 
                                    // returnUrl for the web approval flow that 
                                    // immediately follows this Pay call
$currencyCode = "USD";

// A chained payment can be made with 1 primary receiver and between 1 and 5 secondary 
// receivers
// TODO - Specify the receiver emails
//        Remove or set to an empty string the array entries for receivers that you 
//        do not have
$receiverEmailArray = array(
  'receiver0email',
  'receiver1email',
  'receiver2email',
  'receiver3email',
  'receiver4email',
  'receiver5email'
  );

// TODO - Specify the receiver amounts as the amount of money, for example, '5' or '5.55'
//        Remove or set to an empty string the array entries for receivers that you 
//        do not have
$receiverAmountArray = array(
  'receiver0amount',
  'receiver1amount',
  'receiver2amount',
  'receiver3amount',
  'receiver4amount',
  'receiver5amount'
  );

// TODO - Set ONLY 1 receiver in the array to 'true' as the primary receiver, and set the
//        other receivers corresponding to those indicated in receiverEmailArray to
//        'false'. Make sure that you do NOT specify more values in this array than 
//        in the receiverEmailArray.
$receiverPrimaryArray = array(
  '',
  '',
  '',
  '',
  '',
  ''
  );

// TODO - Set invoiceId to uniquely identify the transaction associated with each receiver
//        Set the array entries with value for receivers that you have each of the array 
//        Values must be unique across all Pay calls made by the caller's API credentials
$receiverInvoiceIdArray = array(
  '',
  '',
  '',
  '',
  '',
  ''
  );

// Request specific optional fields
//   Provide a value for each field that you want to include in the request;
//   if left as an empty string, the field will not be passed in the request
$senderEmail = ""; // TODO - If you are executing the Pay call against a preapprovalKey, 
                   // you should set senderEmail
                   // It is not required if the web approval flow immediately 
                   // follows this Pay call
$feesPayer = "";
$ipnNotificationUrl = "";
$memo = ""; // maxlength is 1000 characters
$pin = ""; // TODO - If you are executing the Pay call against an existing preapproval
           // that requires a pin, then you must set this
$preapprovalKey = ""; // TODO - If you are executing the Pay call against an existing 
                      // preapproval, set the preapprovalKey here
$reverseAllParallelPaymentsOnError = ""; // TODO - Do not specify for chained payment
$trackingId = generateTrackingID(); // generateTrackingID function is found 
                                    // in paypalplatform.php

//-------------------------------------------------
// Make the Pay API call
//
// The CallPay function is defined in the paypalplatform.php file,
// which is included at the top of this file.
//-------------------------------------------------
$resArray = CallPay ($actionType, $cancelUrl, $returnUrl, $currencyCode, 
      $receiverEmailArray, $receiverAmountArray, $receiverPrimaryArray, 
      $receiverInvoiceIdArray, $feesPayer, $ipnNotificationUrl, $memo, 
      $pin, $preapprovalKey, $reverseAllParallelPaymentsOnError, 
      $senderEmail, $trackingId
);

$ack = strtoupper($resArray["responseEnvelope.ack"]);
if($ack=="SUCCESS")
{
 if ("" == $preapprovalKey)
 {
  // redirect for web approval flow
  $cmd = "cmd=_ap-payment&paykey=" . urldecode($resArray["payKey"]);
  RedirectToPayPal ( $cmd );
 }
 else
 {
  // The Pay API call was made for an existing preapproval agreement, so no approval  
  // flow follows.
  // payKey is the key that you can use to identify the result from this Pay call.
  $payKey = urldecode($resArray["payKey"]);
  // paymentExecStatus is the status of the payment
  $paymentExecStatus = urldecode($resArray["paymentExecStatus"]);
  // Note that in order to get the exact status of the transactions resulting from
  // a Pay API call, you should make the PaymentDetails API call for the payKey
 }
} 
else  
{
 //Display a user-friendly Error on the page using any of the following error information 
 //returned by PayPal.
 //TODO - There can be more than 1 error, so check for "error(1).errorId", 
 //       then "error(2).errorId", and so on until you find no more errors.
 $ErrorCode = urldecode($resArray["error(0).errorId"]);
 $ErrorMsg = urldecode($resArray["error(0).message"]);
 $ErrorDomain = urldecode($resArray["error(0).domain"]);
 $ErrorSeverity = urldecode($resArray["error(0).severity"]);
 $ErrorCategory = urldecode($resArray["error(0).category"]);
 
 echo "Pay API call failed. ";
 echo "Detailed Error Message: " . $ErrorMsg;
 echo "Error Code: " . $ErrorCode;
 echo "Error Severity: " . $ErrorSeverity;
 echo "Error Domain: " . $ErrorDomain;
 echo "Error Category: " . $ErrorCategory;
}
?>

Get PayPal APIs: Up and Running now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.