paypal / Payouts-PHP-SDK

PHP SDK for Payouts RESTful APIs
Other
49 stars 33 forks source link

PayPal-Auth-Assertion Header Not Working #7

Open tamodaleko opened 4 years ago

tamodaleko commented 4 years ago

Hello!

Is third party payments working for Payouts API? In documentation it clearly says that we can use this header to do the third party api calls, but this is not working?

https://developer.paypal.com/docs/api/reference/api-requests/#http-request-headers

thisisbalajibabu commented 4 years ago

Hi Can you please give more information like any error messages or negative response that you see while using Payouts API for third party payments? This will help us to understand the issue better.

tamodaleko commented 4 years ago

Hey @thisisbalajibabu,

I tried sending request directly using Postman, as well as using this SDK - but always getting the same error, although "PayPal-Auth-Assertion" header is formatted well using official documentation.

Screenshot of error using Postman:

postman_error

Screenshot of error using SDK:

sdk_error

Screenshot of my code (with auth assertion header):

sdk_code

In official documentation it's mentioned that 3rd party calls can be used for REST API, but i don't understand what's wrong here?

Thanks for the help!

thisisbalajibabu commented 4 years ago

HI @tamodaleko Lets debug your problem one after another.

I will suggest lets fix it using the Postman client where you directly hit the REST API followed which we can see how your SDK integration works.

I believe you should be familiar with below understanding. For 3rd pary payments, we have an account which will be the Subject Which grants permission to another account actor using which the payments is requested for Subject.

I see that you get an error mentioning "Subject Authentication failed" in your postman client.

_One quick request here. Next time when you try hitting using the postman, in the response Headers, you will see debug_id which if you can share will help us to exactly find the reason for the error message returned._

For now, looking at Subject Authentication failed error message, this happens due to several reasons. Very quickly can you validate 1) Basically your account from where you wanted to process payments might not have been given with correct signature credentials while generating access token. 2) if there is an issue with PayPal-Auth-assertion header value wherein you are using the values interchanged between the 2 parties. 3) if your email ID format is not valid

Can you try reviewing once on the first 2 items? I see from your sour code screenshot, the email seems to be fine. Also when you try reviewing it and retrying it using postman, please do get the debug_id in response headers and share it with us.

tamodaleko commented 4 years ago

Hey @thisisbalajibabu

I have created two REST API clients, using two different sandbox merchant accounts:

Client #1

Screenshot_1

Client #2

Screenshot_2

Then i logged in using second sandbox account (client named "Msp") and granted access to first sandbox account (client named "Prism").

Screenshot_3

I guess the issue here is that i've used NVP/SOAP API username (from client #1) when granting access. But i'm clearly not using NVP/SOAP, i'm using REST API.

Screenshot_4

But there is no option to grant permissions for REST API, if i try to use client ID as API username i get the not found error...

What am i doing wrong? How to grant permissions for REST API properly?

Here is debug_id: 37ec6c2be6482

thisisbalajibabu commented 4 years ago

@tamodaleko - Apologies for the delay.

For providing grant permission, kindly follow the documentation as below, https://developer.paypal.com/docs/connect-with-paypal/#user-experience https://developer.paypal.com/docs/connect-with-paypal/integrate/#5-get-authorization-code

The client Id and secret for MSP has to be accurate and it should not mix with Prism credential details while forming the JWT, else the user might not be recognized as a valid user at all. Also, can you make sure, you have the emails of the mentioned accounts to be confirmed email in PayPal account?

tamodaleko commented 4 years ago

@thisisbalajibabu

The links you provided have nothing to do with my issue. I'm trying to do the batch payouts on behalf of other users.

With the information you provided i can only get user's basic information, they can't grant me permissions to do the batch payouts on their behalf.

I've contacted Paypal technical support and they told me that "PayPal-Auth-Assertion" header is not available for REST API. It's only available for NVP/SOAP Api.

They told me that i have to use Paypal Partner Program and Partner-Referrals API, but i'm still in the dark here.

You can close this since you are not understanding my issue.

Thanks for the help.

thisisbalajibabu commented 4 years ago

Hi @tamodaleko PayPal-Auth-Assertion header is available for REST. I am not sure about the other version you mention. Let me try to engage an integration engineer to help you here.

Bobsson commented 4 years ago

@thisisbalajibabu Any update on this? I happen to be in almost exactly same boat as @tamodaleko. I'm trying to proof-of-concept a site that will support taking payments on behalf of multiple merchants. Per the "API Requests" page in the REST documentation, my choices appear to be either storing a clientId and secret for every merchant and doing some kind of caching of bearer tokens per merchant, or to use the PayPal-Auth-Assertion header, as documented here:

You might want to use a JWT if you act on behalf of multiple merchants at the same time, because it is difficult and expensive to generate and manage multiple access tokens. Instead of managing multiple access tokens, you can use this header to provide a JWT assertion that identifies the merchant when you call the API.

However, when I build it as per that page (or the example on the Refund page it points to) and call https://api.sandbox.paypal.com/v1/oauth2/token, I get

{
    "error": "invalid_subject",
    "error_description": "Subject Authentication failed"
}

This happens regardless of whether I use the application's client Id or the target merchant's client ID in building the value. I'm using the application's client ID and secret for a basic Authorization header.

tamodaleko commented 4 years ago

@Bobsson

You should quit trying to achieve this using Paypal Payouts API. I was going back and forth with their Paypal technical support and it appears they have no idea if this is possible or not, but their documentation clearly says they support this option.

They suggested using Paypal Partner Program - https://developer.paypal.com/docs/api/partner-referrals/v1/ I've also signed up for this program and tried everything (also contacted the support - they couldn't help me at all) - and i wasn't able to configure 3rd party payouts.

So i had to stick with getting client id / secret from clients directly....

MohamedJakkariya commented 3 years ago

I was tried a lot for the same issue to fix it. So finally what I realized was made mistake when the generating base64 token.

Actually, I have faced the same issue with different APIs. I'll give my solution below

/**
 *@description For generating assertion token for refund API call
 * @param email correspond merchant email for generating this header for refund
 * @returns string - base64 assertion token
 */
const generateAuthAssertionHeader = async email => {
  const auth_1 = Buffer.from('{"alg":"none"}').toString('base64');

  const auth_2 = Buffer.from(`{"email":"${email}","iss":"${PAYPAL_CLIENT_ID}"}`).toString('base64');

  const auth_assertion_header = `${auth_1}.${auth_2}.`;

  return auth_assertion_header;
};

const refundAmount = async (capture_id) => {
  try {

       const request = new checkout_nodeJs_sdk.payments.CapturesRefundRequest(capture_id);
       const auth_header = await generateAuthAssertionHeader(order.owner_email);

       request.headers['PayPal-Auth-Assertion'] = auth_header;    // * Important
       request.requestBody({});
       refund = await paypal_client.client().execute(request);
       return { id: refund.id, status: refund.status };

    } catch (e) {
    throw new UnprocessableEntity("Refund couldn't be processed.");
  }
}

Finally, It was work for me. Hopefully, someone can fix the same issue in the future.

I was faced two errors Error: 1 before added "Paypal-Auth-Assertion" header.

 {
  statusCode: 403,
  headers: {
    'cache-control': 'max-age=0, no-cache, no-store, must-revalidate',
    'content-length': '441',
    'content-type': 'application/json',
    date: 'Thu, 15 Apr 2021 19:35:27 GMT',
    'paypal-debug-id': 'f9185671ebddd',
    connection: 'close'
  },
  _originalError: {
    text: '{"name":"NOT_AUTHORIZED","message":"Authorization failed 
due to insufficient permissions.","debug_id":"f9185671ebddd","details":[{"issue":"PERMISSION_DENIED","field":"capture_id","value":"39B3775678444273N","description":"You do not have permission to access or perform operations on this resource.","location":"path"}],"links":[{"href":"https://developer.paypal.com/docs/api/payments/v2/#error-PERMISSION_DENIED","rel":"information_link"}]}',
    statusCode: 403,
    headers: {
      'cache-control': 'max-age=0, no-cache, no-store, must-revalidate',
      'content-length': '441',
      'content-type': 'application/json',
      date: 'Thu, 15 Apr 2021 19:35:27 GMT',
      'paypal-debug-id': 'f9185671ebddd',
      connection: 'close'
    }
  }
}

Error - 2: After made mistake while generating base64 token.

{
  statusCode: 401,
  headers: {
    'cache-control': 'max-age=0, no-cache, no-store, must-revalidate',
    'content-length': '79',
    'content-type': 'application/json',
    date: 'Thu, 15 Apr 2021 19:36:35 GMT',
    'paypal-debug-id': '893902251901e',
    connection: 'close'
  },
  _originalError: {
    text: '{"error":"invalid_subject","error_description":"Subject Authentication failed"}',
    statusCode: 401,
    headers: {
      'cache-control': 'max-age=0, no-cache, no-store, must-revalidate',
      'content-length': '79',
      'content-type': 'application/json',
      date: 'Thu, 15 Apr 2021 19:36:35 GMT',
      'paypal-debug-id': '893902251901e',
      connection: 'close'
    }
  }
}

It was resolved after properly generated the base64 token.

bilalrahim commented 1 year ago

Hi @all. If someone is still stuck on this here is the nodejs solution that worked for me.

const header = Buffer.from(JSON.stringify({ alg: "none" })).toString("base64");
const payload = Buffer.from(JSON.stringify({ iss: PAYPAL_CLIENT_ID, payer_id: merchant_id })).toString("base64")
stillwyw commented 4 months ago

Just in case someone is as careless as I am, pay attention, there is a trailing dot at the end:

const auth_assertion_header = `${auth_1}.${auth_2}.`;
munroe7 commented 3 months ago

I was literally spent so much wasted time while following the PayPal standard checkout documentation. The first example for generating the auth assertion ALWAYS returns a "Subject Authentication failed" error.

Don't follow the first example PayPal gives for generating the header, use the SECOND ONE. This is a working function I made in node.js:

export const getAuthAssertionValue = (paypal_client_id, sellerPayerId) => {
    const clientID = paypal_client_id;
    const merchantIDOrEmail = sellerPayerId;
    const auth1 = Buffer.from('{"alg":"none"}').toString("base64");
    const auth2 = Buffer.from(`{"iss":${clientID},"payer_id":${merchantIDOrEmail}}`).toString("base64");
    return `${auth1}.${auth2}.`;
}