Implementing PayPal’s NVP API to send Mass Payments

Recently I was involved in a project where we needed to send payments to customers for different services. I decided to implement PayPal MassPay in order to submit these payments through an interface. The following code will give you all the code you need to send payments through the PayPal NVP API.

Post any questions!


// Use this class to construct the NVP calls to the paypal service

class PayPal_CallerService {

    public $API_UserName;
    public $API_Password;
    public $API_Signature;
    public $API_Endpoint;
    public $last_request;
    public $last_response;
    public $version;
    public $subject;
    
    public function __construct($credentials)
    {
        
        if(is_array($credentials)) {
            
            $this->API_UserName  = (isset($credentials['API_USERNAME'])  && !empty($credentials['API_USERNAME']))  ? $credentials['API_USERNAME'] : '';
            $this->API_Password  = (isset($credentials['API_PASSWORD'])  && !empty($credentials['API_PASSWORD']))  ? $credentials['API_PASSWORD'] : '';
            $this->API_Signature = (isset($credentials['API_SIGNATURE']) && !empty($credentials['API_SIGNATURE'])) ? $credentials['API_SIGNATURE'] : '';
            $this->API_Endpoint  = (isset($credentials['API_ENDPOINT'])  && !empty($credentials['API_ENDPOINT']))  ? $credentials['API_ENDPOINT'] : 'https://api-3t.sandbox.paypal.com/nvp';
            $this->version       = (isset($credentials['VERSION'])       && !empty($credentials['VERSION']))       ? $credentials['VERSION'] : '60.0';
            $this->subject       = (isset($credentials['SUBJECT'])       && !empty($credentials['SUBJECT']))       ? $credentials['SUBJECT'] : '';
        
        } else {
            
            throw new Exception('You must pass in an array of credentials to instantiate this class.');
            
        }
        
    }
    
    public function callPayPal($methodName,$base_call = NULL)
    {
            
        $header_call = "&PWD=".urlencode($this->API_Password).
        	            "&USER=".urlencode($this->API_UserName).
        		    "&SIGNATURE=".urlencode($this->API_Signature).
        		    "&SUBJECT=".urlencode($this->subject).
                            "&VERSION=".urlencode($this->version);
            
        $call = $header_call.$base_call;
        
        //setting the curl parameters.
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$this->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);
        
        //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);*/ 
        
        //Check if version is included in $nvpStr else include the version.
        if(strlen(str_replace('VERSION=', '', strtoupper($call))) == strlen($call)) {
        	
            $nvpStr = "&VERSION=" . urlencode($this->version) . $call;	
        }
        
        $nvpreq="METHOD=".urlencode($methodName).$call;
        
        $this->last_request = $nvpreq;
        
        //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 = $this->deformatNVP($response);
        $nvpReqArray = $this->deformatNVP($nvpreq);
        $_SESSION['nvpReqArray'] = $nvpReqArray;
        
        if (curl_errno($ch)) {
        	
            throw new Exception('Curl error: '.curl_errno($ch).' - '.curl_error($ch));
        	  
         } else {
        	//Closing the curl
            curl_close($ch);
         }
        
         $this->last_response = $nvpResArray;
         
        return $nvpResArray;
    }
    
    /** 
     * This function will take NVPString and convert it to an Associative Array and it will decode the response.
      * It is usefull to search for a particular key and displaying arrays.
      * 
      * @nvpstr is NVPString.
      * @nvpArray is Associative Array.
      */
    
    public static 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;
    }
}

The following is a script used to instantiate the PayPal class and send a mass payment.

                $credentials['API_USERNAME']  = 'username';
                $credentials['API_PASSWORD']  = 'password';
                $credentials['API_SIGNATURE'] = 'signature';
                $credentials['API_ENDPOINT']  = 'https://api-3t.sandbox.paypal.com/nvp';

                $amount  = urlencode($_POST['Amount']);
                $id          = urlencode(123456);
                $note      = urlencode('This is a note that will show up in the email.');
                $subject  = urlencode("Payment from Routy Design");
                $type      = urlencode('EmailAddress');
                $currency = urlencode($_POST['Currency']);
                $customer_email = urlencode($_POST['EmailAddress']);
                
                $base_call  = "&L_EMAIL0=".$customer_email.
                		      "&L_AMT0=".$amount.
                		      "&L_UNIQUEID0=".$id.
                		      "&L_NOTE0=".$refund_note.
                		      "&EMAILSUBJECT=".$subject.
                                      "&RECEIVERTYPE=".$type.
                                      "&CURRENCYCODE=".$currency;
                        
                $PayPal = new PayPal_CallerService($credentials);
                
                $status  = $PayPal->callPayPal("MassPay", $base_call);

                //Do something if it is successfully sent
                if($status) {
                    
                    if($status['ACK'] == "Success") {

                        //Check the balance of the account that was just debited, and make sure we have at least $500 left in our account
                        $balance = $PayPal->callPayPal("GetBalance", '');
                        
                        if($balance['L_AMT0'] < 500) {
                            
                            $message = "The balance is low... Please replenish the funds... \n\n".$balance['L_AMT0']." ".$balance['L_CURRENCYCODE0'];
                            
                            mail('notifysomeone@routydesign.com','PayPal Refund Balance Low',$message);
                            
                        }
                        
                    } else if($status['ACK'] == "Failure"){
                        
                        if($status['L_ERRORCODE0'] == '10321') {
                            
                            $error = "Insufficient Funds to Send Refund to: ".$_POST['FirstName'].' '.$_POST['LastName'].'('.$_POST['EmailAddress'].') via PayPal in the amount of: '.$_POST['Amount'].' '.$_POST['Currency'].'. ';
                            
                        } else if($status['L_ERRORCODE0'] == '10004') {
                            
                            $this->view->error = "Invalid Amount of Refund to: ".$_POST['FirstName'].' '.$_POST['LastName'].'('.$_POST['EmailAddress'].') via PayPal in the amount of: '.$_POST['Amount'].' '.$_POST['Currency'].'. Must be more than 0 '.$_POST['Currency'].'.';
                            
                        } else {
                            
                            $this->view->error = "There was an unknown error when attempting to submit the payment.";
                            
                            var_dump($status);
                            
                        }
                        
                    }
                    
                }
                
            }

It is important to also setup a script to receive updates from PayPal via IPN’s (Instant Payment Notification).

Basically it’s a web address you give to paypal (http://blog.routydesign.com/paypal_ipn_updates.php) that they will send status updates to of the payments that are sent through their API. It is a way for your backend to keep tabs on what is happening with the payments to others. They send you an NVP formated string as a POST – all the variable they send in the post can be studied in their documentation, check the link below.

View their IPN PDF:
https://cms.paypal.com/cms_content/US/en_US/files/developer/IPNGuide.pdf

Tuesday, January 26th, 2010 API Services

2 Comments to Implementing PayPal’s NVP API to send Mass Payments

  • wayne says:

    Thanks for this I found it much easier to implement your class.

    Thank you

  • Renaud says:

    Hi,

    Very nice tutorial, many thanks. However I’d like to ask for a question, please : how is it possible to pass the notify_url to the API (as a standard paypal form) if I don’t want to configure a IPN url in my Paypal account ?

    I tried :
    &notify_url=http://mysite.com/notify.php
    but no success.

    Thanking you in advance

  • Leave a Reply

    You must be logged in to post a comment.

    [ad code=1 align=center]