PayPal Payouts API Integration in PHP
PayPal Payouts allows you to send payments to multiple recipients at the same time. This post will demonstrate how to use PayPal Payout API to send out payments in bulk.

You can send payments to multiple recipients as a batch with PayPal payout. There are three types of payouts PayPal provides.
- Large Batch Payout: Create a payouts file and upload it to secure FTP server. Number of recipients is unlimited.
- API Integration: Make programming calls to payout API. Number of recipients is up to 15000 per request.
- Payouts Web: Create a payouts file and upload it using Payouts Web. Number of recipients is 5000 per file.
We will be using API Integration to programmatically send payments to multiple receivers at a time. Before you can send payout requests to payouts API you must have:
- A PayPal account.
- Client ID and Client Secret.
- Sufficient funds in your account.
- Payout feature must be enabled.
How to Send PayPal Payouts in PHP
In this post we are going to learn how to integrate PayPal payout experience in a PHP application. We will show only an example request of payout. Steps to send request to PayPal payout API:
- Create PayPal sandbox account.
- Get OAuth 2.0 client ID and client secret of this sandbox account.
- Create a PHP class to handle PayPal API requests for creating and capturing orders.
- Create an example request and send payouts with PayPal.
We are going to create following files:
- constants.php: Contains PayPal constants.
- paypal-sdk/paypal_client.php: A PHP class to send requests to PayPal endpoints.
- index.php: HTML page containing the cart items and PayPal checkout button
Before you send any kind of resource request to PayPal you need an access token to authenticate your request. We will be sending two requests one to get an access token and other to send payouts so we will first write a function that can be used in both requests. To get an access token we need our client ID and Client Secret.
Create a PayPal Sandbox Account and Get Client Credentials
First setup a sandbox account and create an App. After your app is created get your client ID and client secret, next under "SANDBOX APP SETTINGS" enable payouts options to allow your app to send requests to PayPal Payouts API. You can follow instructions for creating an app and getting your client ID & Secret on this link PayPal Checkout with Payment Buttons in PHP.
I have divided the payouts integration into three basic steps:
- A function to send CURL requests.
- A request to get access token.
- Final request to send payouts.
Add Constants
Add constants to store PayPal sandbox credentials.
constants.php
<?php
define('PAYPAL_ENVIRONMENT', 'sandbox');
define('PAYPAL_CLIENT_ID', 'PAYPAL_CLIENT_ID');
define('PAYPAL_CLIENT_SECRET', 'PAYPAL_CLIENT_SECRET');
Create PayPal Client Class to Send Requests to PayPal API
A custom PHP class to send requests to PayPal endpoints. Class has the following properties in use:
- $base_url: Holds the endpoint URL for sandbox or production.
- $client_id: Client id created in previous steps.
- $client_secret: Client secret created in previous steps.
- $access_token: Access token for PayPal requests authorization.
- $token_type: The type of token i.e "Bearer".
- $expires_in: The expire time of current access token. Used to refresh/recreate the token when expired.
- $created_at: The creation time of current access token. Used to refresh/recreated the token when expired.
- __construct: Constructor of class when and instance of class is created. Sets $client_id, $client_secret $base_url and $access_token properties of the class.
- set_access_token: Sends the request to PayPal's authorization endpoint to retrieve access token for future requests. Sets $access_token, $token_type, $expires_in and $created_at properties of class.
- curl_request: A generic function used to send curl requests to PayPal endpoints.
paypal_client.php
<?php
class paypal_client{
public string $base_url;
private string $client_id;
private string $client_secret;
private ?string $access_token;
private string $token_type;
private int $expires_in;
private int $created_at;
public function __construct($client_id, $client_secret, $sandbox = false)
{
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->base_url = $sandbox
? 'https://api-m.sandbox.paypal.com'
: 'https://api-m.paypal.com';
$this->access_token = null;
}
/**
* @throws Exception
*/
public function set_access_token() :stdClass|array
{
$headers = [
'content-type' => 'application/x-www-form-urlencoded'
];
$curl_options = [
CURLOPT_USERPWD => $this->client_id . ':' . $this->client_secret
];
$body = [
'grant_type' => 'client_credentials'
];
$response = $this->curl_request('/v1/oauth2/token', 'POST', $headers, http_build_query($body), $curl_options);
$response = json_decode($response);
$this->access_token = $response->access_token;
$this->token_type = $response->token_type;
$this->expires_in = $response->expires_in;
$this->created_at = time();
return $response;
}
/**
* @param string $path
* @param string $method
* @param array $headers
* @param array|string $body
* @param array $curl_options
* @return bool|string
* @throws Exception
*/
public function curl_request(string $path, string $method, array $headers = [], array|string $body = [], array $curl_options = []): bool|string
{
$curl = curl_init();
array_change_key_case($headers);
$headers = array_merge(['accept' => 'application/json'], $headers);
curl_setopt($curl, CURLOPT_URL, $this->base_url . $path);
curl_setopt($curl, CURLOPT_TIMEOUT, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
if(str_starts_with($this->base_url, 'https://')){
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
}
if(!array_key_exists('authorization', $headers) && !str_ends_with($path, 'v1/oauth2/token')){
if (is_null($this->access_token) || time() > ($this->created_at + $this->expires_in)){
$this->set_access_token();
}
$headers['authorization'] = sprintf('%s %s', $this->token_type, $this->access_token);
}
// If any headers set add them to curl request
if(!empty($headers)){
curl_setopt($curl, CURLOPT_HTTPHEADER, array_map(function($key, $value){
return $key . ': '. $value;
}, array_keys($headers), array_values($headers)));
}
// Set the request type , GET, POST, PUT or DELETE
switch(strtoupper($method)){
case 'POST':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
break;
case 'PUT':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
break;
case 'DELETE':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
default:
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
break;
}
// If any data is supposed to be sent along with request add it to curl request
if(!empty($body)){
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
}
// Any extra curl options to add in curl object
if(!empty($curl_options)){
foreach($curl_options as $option_key => $option_value){
curl_setopt($curl, $option_key, $option_value);
}
}
$response = curl_exec($curl);
$error = curl_error($curl);
$error_code = curl_errno($curl);
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if($error_code > 0){
throw new Exception($error, $error_code);
}
if ($status_code < 200 || $status_code >= 300) {
throw new Exception($response, $status_code);
}
curl_close($curl);
return $response;
}
}
Send PayPal Payout Request in PHP
Prepare an array of receivers with required details and send request to PayPal endpoint and initiate a bulk payout.
Payouts Request
<?php
// Headers for our token request
$headers['accept'] = 'application/json';
$headers['content-type'] = 'application/json';
$request_body = [];
$items = [];
$time = time();
// Prepare sender batch header
$sender_batch_header['sender_batch_id'] = $time;
$sender_batch_header['email_subject'] = 'Payout Received';
$sender_batch_header['email_message'] = 'You have received a payout, Thank you for using our services';
// First receiver
$receiver['recipient_type'] = 'EMAIL';
$receiver['note'] = 'Thank you for your services';
$receiver['sender_item_id'] = $time++;
$receiver['receiver'] = '[email protected]';
$receiver['amount']['value'] = 10.00;
$receiver['amount']['currency'] = 'USD';
$items[] = $receiver;
// Second receiver
$receiver['recipient_type'] = "EMAIL";
$receiver['note'] = 'You received a payout for your services';
$receiver['sender_item_id'] = $time++;
$receiver['receiver'] = '[email protected]';
$receiver['amount']['value'] = 15.00;
$receiver['amount']['currency'] = 'USD';
$items[] = $receiver;
$request_body['sender_batch_header'] = $sender_batch_header;
$request_body['items'] = $items;
$paypal_client = new paypal_client(PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_ENVIRONMENT === 'sandbox');
$response = $paypal_client->curl_request('/v1/payments/payouts', 'POST', $headers, json_encode($request_body));
?>
For demo purpose I used static array of receivers, you can prepare your receivers list from database table or file. PayPal prevents duplicate payouts if sender_batch_id has been already used in past 30 days PayPal will reject the payout request and return an error. Below is the successful payout response.
{
"batch_header": {
"sender_batch_header": {
"sender_batch_id": "2014021801",
"email_subject": "You have a payout!"
},
"payout_batch_id": "12345678",
"batch_status": "PENDING"
}
}