Search code examples
paypal

Paypal, IPN/Webhooks create invoice in my paypal account, to send money


I want to create via IPN or Webhooks the following situation. The user want to withdraw some money, when he clicks the button to withdraw, an invoice will be made in my Paypal account where I will have the option to Accept or Deny to send the money. After accept or deny, my app will receive an notification if the money was sent or not.

I am reading their documentation, but I don't find what I want.


Solution

  • I think you're a little bit confused with what PayPal features you would need for this.

    IPN and Webhooks are post-transaction processing tools. They wouldn't trigger anything until AFTER an invoice was already created, a payment was received, a dispute was submitted, etc.

    Also, you don't want to do this with the actual Invoicing API because PayPal charges higher fees for that.

    If you provide your user with a button to withdrawal money you could trigger the payout directly on that action using the Payouts API.

    You did not specify what language you are working with, but here's a sample of a PHP script that would trigger a payout:

    <?php
    
    $paypal_client_id = "your_client_id";
    $paypal_secret = "your_secret";
    
    $payee_email = "payee@example.com";
    $amount = 10.00;
    $currency = "USD";
    
    $curl = curl_init();
    
    curl_setopt_array($curl, array(
      CURLOPT_URL => "https://api.paypal.com/v1/oauth2/token",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => "",
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 30,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      CURLOPT_CUSTOMREQUEST => "POST",
      CURLOPT_POSTFIELDS => "grant_type=client_credentials",
      CURLOPT_HTTPHEADER => array(
        "Authorization: Basic " . base64_encode("$paypal_client_id:$paypal_secret"),
        "Content-Type: application/x-www-form-urlencoded"
      ),
    ));
    
    $response = curl_exec($curl);
    $err = curl_error($curl);
    
    curl_close($curl);
    
    if ($err) {
      echo "cURL Error #:" . $err;
    } else {
      $access_token = json_decode($response)->access_token;
      
      $curl = curl_init();
      
      curl_setopt_array($curl, array(
        CURLOPT_URL => "https://api.paypal.com/v1/payments/payouts",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => "{\"sender_batch_header\": {\"sender_batch_id\":\"batch_" . time() . "\",\"email_subject\":\"You have a payment\"},\"items\":[{\"recipient_type\":\"EMAIL\",\"amount\":{\"value\":$amount,\"currency\":\"$currency\"},\"receiver\":\"$payee_email\",\"note\":\"Thank you.\",\"sender_item_id\":\"item_" . time() . "\"}]}",
        CURLOPT_HTTPHEADER => array(
          "Content-Type: application/json",
          "Authorization: Bearer $access_token"
        ),
      ));
      
      $response = curl_exec($curl);
      $err = curl_error($curl);
      
      curl_close($curl);
      
      if ($err) {
        echo "cURL Error #:" . $err;
      } else {
        $payout = json_decode($response);
        
        if ($payout->batch_header->batch_status == "SUCCESS") {
          echo "Payout sent successfully!";
        } else {
          echo "Payout failed: " . $payout->batch_header->failure_reason;
        }
      }
    }
    

    To have your app receive notifications when the Payout was completed you can subscribe to the PAYMENT.PAYOUTSBATCH.SUCCESS Webhook.

    Here is a sample of a script that would subcribe that webhook:

    <?php
    
    $paypal_client_id = "your_client_id";
    $paypal_secret = "your_secret";
    
    $webhook_url = "https://www.example.com/webhooks/payouts_success";
    
    $curl = curl_init();
    
    curl_setopt_array($curl, array(
      CURLOPT_URL => "https://api.paypal.com/v1/oauth2/token",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => "",
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 30,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      CURLOPT_CUSTOMREQUEST => "POST",
      CURLOPT_POSTFIELDS => "grant_type=client_credentials",
      CURLOPT_HTTPHEADER => array(
        "Authorization: Basic " . base64_encode("$paypal_client_id:$paypal_secret"),
        "Content-Type: application/x-www-form-urlencoded"
      ),
    ));
    
    $response = curl_exec($curl);
    $err = curl_error($curl);
    
    curl_close($curl);
    
    if ($err) {
      echo "cURL Error #:" . $err;
    } else {
      $access_token = json_decode($response)->access_token;
      
      $curl = curl_init();
      
      curl_setopt_array($curl, array(
        CURLOPT_URL => "https://api.paypal.com/v1/notifications/webhooks",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_POSTFIELDS => "{\"url\":\"$webhook_url\",\"event_types\":[{\"name\":\"PAYMENT.PAYOUTSBATCH.SUCCESS\"}]}",
        CURLOPT_HTTPHEADER => array(
          "Content-Type: application/json",
          "Authorization: Bearer $access_token"
        ),
      ));
      
      $response = curl_exec($curl);
      $err = curl_error($curl);
      
      curl_close($curl);
      
      if ($err) {
        echo "cURL Error #:" . $err;
      } else {
        $webhook = json_decode($response);
        
        if ($webhook->name == "PAYMENT.PAYOUTSBATCH.SUCCESS") {
          echo "Webhook subscribed successfully!";
        } else {
          echo "Webhook subscription failed: " . $webhook->name;
        }
      }
    }
    

    Then you would setup a webhook handler at the webhook URL you provided. Here is an example of how that might look:

    <?php
    
    $webhook_data = json_decode(file_get_contents('php://input'), true);
    
    if ($webhook_data["event_type"] == "PAYMENT.PAYOUTSBATCH.SUCCESS") {
      // handle the payout success event
      $batch_id = $webhook_data["resource"]["batch_header"]["payout_batch_id"];
      $status = $webhook_data["resource"]["batch_header"]["batch_status"];
      
      // log the batch ID and status for reference
      error_log("Batch ID: $batch_id");
      error_log("Status: $status");
      
      // process the successful payout
      // ...
    } else {
      // handle other event types
      // ...
    }