1

I have a Paypal Button setup with the following code:

  paypal.Buttons({
      createSubscription: function(data, actions) {
        return actions.subscription.create({
          'plan_id': 'P-PLANID'
        });
      },
      onApprove: function(data, actions) {
        // Somehow pass info to Webhook
      }
  }).render('#paypal-button-container');

Is there a way to have this integrate with a webhook I have setup with BILLING.SUBSCRIPTION.ACTIVATED?

I just created the webhook but I am not sure how I connect my PayPal Buttons to it

Bijan
  • 7,737
  • 18
  • 89
  • 149
  • Why don't you use `fetch` with webhook? In that callback function. – kalle Aug 04 '21 at 06:30
  • `fetch` will not be useful, webhooks are a call from PayPal back to a listener URL/server route. If later doing a postback for webhook _verification_ (to validate that it came from PayPal), an HTTP client like node-fetch or any other could be used for this postback. – Preston PHX Aug 04 '21 at 07:56

2 Answers2

0

You need to subscribe to webhook events using the same app/client-id as you are using for the buttons. You can subscribe manually in the Developer Dashboard, or via an API call. Here is the documentation.


This comment in your code does not make sense:

// Somehow pass info to Webhook

Subscribing to webhook event notifications is a one-time, initial setup that you do somewhere else. It is not something you do from the buttons, and it will not call nor interact with the buttons in any way; webhooks are sent and processed later, asynchronously.

If you want to store some information as part of the subscription for later reconciliation (with the user who subscribed, for example, so your backend knows what to do when it receives a webhook) -- then you can set a custom_id as part of the subscription creation.

Preston PHX
  • 27,642
  • 4
  • 24
  • 44
  • Same using the same app/client-id? Where on the code of the user you see the fields for that? – Federico Schiocchet Mar 06 '23 at 16:19
  • 1
    The question above is JS SDK code (paypal.Buttons). A client-id is a required field when loading the JS SDK, and to receive webhooks they need to be registered in the app that client-id corresponds to in https://developer.paypal.com/dashboard/applications/ – Preston PHX Mar 06 '23 at 16:26
0

My example isn't exactly a webhook per-se, but it works great for me. I put a call to a Firebase Cloud Function call in the onApprove function like this:

paypal.Buttons({
  createSubscription: function(data, actions) {
    return actions.subscription.create({
      'plan_id': 'P-PLANID'
    });
  },
  onApprove: function(data, actions) {
    const orderID = data.orderID;
    let url = "myCloudFunction?orderID="+orderID;
    //this will verify the purchase and complete order on back end
    fetch(url);
    //carry on with user experince
  }
}).render('#paypal-button-container');

And then verify the purchase using the orderID in my cloud function, and update my DB or whatever else i need to do like so:

const checkoutNodeJssdk = require('@paypal/checkout-server-sdk');
const functions = require("firebase-functions");

function environment() {
   let clientId = process.env.PAYPAL_CLIENT_ID || 'yourClientID';
   let clientSecret = process.env.PAYPAL_CLIENT_SECRET || 'yourClientSecret';
   return new checkoutNodeJssdk.core.LiveEnvironment(
       clientId, clientSecret
   );
}

function payPalClient() {
   return new checkoutNodeJssdk.core.PayPalHttpClient(environment());
}

exports.completeOrder = functions.https.onRequest((req, res) => {
   const orderID = req.query.orderID;
   let request = new checkoutNodeJssdk.orders.OrdersGetRequest(orderID);
   let order;
   try {
       order = await payPalClient().execute(request);
       console.log(order);
   } catch (err) {
       console.error(err);
       res.send({response: "failed"});
   }

   if (order.result.status === 'APPROVED') {
       //update DB or whatever else needs to happen to complete order
       res.send({response: "success"});
   } else {
       res.send({response: "failed"});
   }
});

You can also verify the subscription is active by passing a subscriptionID from the data object returned in the onApprove function of the button to the cloud function. Use the following to verify the subscription:

async function verifySubscription(subscriptionID, callback) {   
   const authUrl = "https://api-m.paypal.com/v1/oauth2/token";
   const subscriptionsUrl = "https://api.paypal.com/v1/billing/subscriptions/" + subscriptionID;
   const clientIdAndSecret = "myClientID:myCLientSecret";
   const base64 = Buffer.from(clientIdAndSecret).toString('base64')
   fetch(authUrl, { 
       method: 'POST',
       headers: {
           'Content-Type': 'application/json',
           'Accept': 'application/json',
           'Accept-Language': 'en_US',
           'Authorization': `Basic ${base64}`,
       },
       body: 'grant_type=client_credentials'
   }).then(function(response) {
       return response.json();
   }).then(function(data) {
       fetch(subscriptionsUrl, { 
           method: 'get', 
           headers: {
             'Authorization': 'Bearer '+data.access_token, 
             'Content-Type': 'application/json'
           }, 
       }).then(function(response) {
           return response.json();
       }).then(function(subscriptionData) {
           console.log("subscriptionData.status: ", subscriptionData.status);
           if (subscriptionData.status === "ACTIVE") {
               callback(true);
           } else {
               callback(false);
           }
       }).catch(function(error) {
           console.log("couldnt verify subscription");
           console.log(error);
           callback(false);
       });
   }).catch(function() {
       console.log("couldnt get auth token");
       callback(false);
   });
}
Dustin Spengler
  • 5,478
  • 4
  • 28
  • 36