INVALID_RESOURCE_ID when Authorization.Capture is called on an authorized payment using sandbox account - paypal

I am using PayPal.1.6.0\lib\net45\PayPal.dll
I created a payment with authorize intent and have the successfully authorized authID (Payment.Cart) and the PayPal.APi.Payment.id.
When I try to call the Authorization.Capture(apiContext, Capture) with the authID, I get
{
"name":"INVALID_RESOURCE_ID",
"message":"The requested resource ID was not found",
"information_link":"https://developer.paypal.com/webapps/developer/docs/api/#INVALID_RESOURCE_ID",
"debug_id":"d73f6a0c1b8bc"
}
I tested this using my sandbox account.
Trying the link gets me a 'page not found error'. Any clues?

I realized that i was looking at the wrong 'authorization code'. It is not the ((Payment)executedPayment).cart as posted in my original question but ((Payment)executedPayment).transactions.FirstOrDefault().authorization.id
I was able to use the correct authCode and am able to capture my authorization. executedPayment is the return value from Payment.Execute method.

Related

Paypal - "ID token context does not have required scope" error when trying to render paypal button using saved payment info

I have been trying to save customer payment details on a site, so that if a customer is logged in they can pay with a single click, instead of having to log in to paypal. To do so I have been following the official paypal guide: https://developer.paypal.com/beta/vault/during-purchase/js-sdk/paypal/
I was able to successfully save payment details. The problem is that when I try to use the saved customer_id to use those saved payment details, I get the following error: "Error: cannot get vault - ID token context does not have required scope".
What I did was the following:
Generate an id_token and access_token by sending a request to https://api-m.sandbox.paypal.com/v1/oauth2/token and specifying response_type=id_token
When rendering the paypal button, pass the generated id_token to the script tags data-user-id-token attribute.
I click the paypal button on the website. This sends a request to my backend server which generates an order by sending a request to https://api-m.sandbox.paypal.com/v2/checkout/orders/. When generating the order I use the generated access_token as a Bearer token in the "Authorization" header, as well as pass the following payment_source.paypal configuration:
"paypal": {
"attributes": {
"vault": {
"store_in_vault": "ON_SUCCESS",
"usage_type": "MERCHANT",
"customer_type": "CONSUMER"
}
}
I get an order id in the response. I pass this order id back to the client side, where the paypal sdk initates a payment flow using this id.
I finish the payment flow, and on success call my server's captureOrder endpoint. This calls https://api-m.sandbox.paypal.com/v2/checkout/orders/$id/capture, where $id is the orderID used before. I also pass the Beares access_token generated in the first step. This request returns a response containing a generated user ID that looks something like this: 560837847
I send another request to https://api-m.sandbox.paypal.com/v1/oauth2/token, this time passing a target_customer_id parameter in the request's body. The value of the parameter is the customerId generated in the 5th step.
I render the page with the paypal button again, this time passing the new id_token to the script tags data-user-id-token attribute.
The error "Error: cannot get vault - ID token context does not have required scope" appears in the developer tools console.
I have turned on "Accept payments" on the Paypal developer console as well as Vault functionality:
The id_token generated in the 6th step has the following scopes:
https://uri.paypal.com/services/invoicing
https://uri.paypal.com/services/vault/payment-tokens/read
https://uri.paypal.com/services/disputes/read-buyer
https://uri.paypal.com/services/payments/realtimepayment
https://uri.paypal.com/services/disputes/update-seller
openid
https://uri.paypal.com/services/payments/payment/authcapture
https://uri.paypal.com/services/disputes/read-seller
Braintree:Vault
https://uri.paypal.com/services/payments/refund
https://api.paypal.com/v1/vault/credit-card
https://api.paypal.com/v1/payments/.*
https://uri.paypal.com/payments/payouts
https://uri.paypal.com/services/vault/payment-tokens/readwrite
https://api.paypal.com/v1/vault/credit-card/.*
https://uri.paypal.com/services/shipping/trackers/readwrite
https://uri.paypal.com/services/subscriptions
https://uri.paypal.com/services/applications/webhooks
I cannot find any information online about what else I should do to make this work.

Facebook messenger platform webhook Verify Token not validated

I've created a facebook app on facebook developers
I've setup a local rails server and exposed it to public internet using ngrok. I'm receiving facebook's webhook validation GET request and I'm returning the hub_challenge code in response. The response status code is also 200. I've provided a secret Verify Token which is required to set up a messenger webhook. But after all this I'm getting error
The Callback URL or Verify Token couldn't be validated. Please verify
the provided information or try again later.
I've checked that the request is received and the response being sent back to the facebook server, but don't know why it fails and says Verify Token couldn't be validated. Is it some special token that I have to get from somewhere from facebook messenger platform? Currently I've provided it my own secret token. Any help will be appreciated. Thanks
when I verify Facebook Webhook with my website i got that kind error
The URL couldn't be validated. Response does not match challenge, expected value="1421256154", received="1421256154\u003Clink rel=..."
My code
public function verify_token(Request $request)
{
$mode = $request->get('hub_mode');
$token = $request->get('hub_verify_token');
$challenge = $request->get('hub_challenge');
if ($mode === "subscribe" && $this->token and $token === $this->token) {
return response($challenge,200);
}
return response("Invalid token!", 400);
}
my code everything is ok .I am using laravel thats why APP_DEBUG=true defalt when I change it APP_DEBUG=false its working and my problem solved.

PayPal API login error - "Sorry, we can't log you in.."

I am using the paypal api login with this:
paypal.use( ['login'], function (login) {
login.render ({
"appid":"myAppID",
"authend": "sandbox",
"scopes":"email",
"containerid":"lippButton",
"locale":"en-us",
"returnurl":"myReturnUrl"
});
});
when I click on lippButton paypal opens with forms for email and password. When they are filled in I get the error:
Sorry, we can't log you in. If you think there's a problem with your
account, contact us and we'll help resolve it.
Even though If I redirect the user to login/complete payment it is fine. Any ideas as to what the problem is?
EDIT:
Seems like it may just be the sandbox api is down. I went here:
https://devtools-paypal.com/guide/openid/php?success=true&env=sandbox
and clicked "Try it" then clicked on the link for step 2, which pops up an identical login form that I am using and the error message persists there as well.
EDIT:
Can anyone explain how I can log in to paypal, but not from their sandbox using my paypal credentials?
I found the problem. For anyone in the future with this same problem (I'm assuming many will because it is directly following their walk through), you seem to not be able to log in via "sandbox" mode (even though their provided code sample has authend: "sandbox" in it).
Simply put in your live credentials INSTEAD of your sandbox credentials. The log in should look like this (There should be no 'authend: "sandbox"' now):
paypal.use( ['login'], function (login) {
login.render ({
"appid":"myAppID", //Use your live client ID, not your sandbox client ID.
//No authend needed.
"scopes":"email",
"containerid":"lippButton",
"locale":"en-us",
"returnurl":"myReturnUrl"
});
});
And the login now works and allows you to login.

Google Sign-In with Passportjs not getting authenticated

I'm using Sails with Passport for authentication. I'm using passport-google-oauth(OAuth2Strategy) and passport-facebook for enabling Google Sign-in.
I'm not too well-versed with Passport, so pardon me if this is a rookie question. I've set up login via Facebook and it works just fine. With Google, I do receive an authorization code after allowing access to the app, but the I'm eventually not authenticated. I'm guessing the same code should work for both Facebook and Google since the strategies are both based on oauth2.
I'm not even sure what code to share, since I'm using the auto-generated code from sails-generate-auth, but do let me know if there's anything else I can share.
Any ideas on why this might be happening? The app is locally hosted but that's unlikely to be the problem since I am getting to the authorization stage anyway.
I faced the same problem and it was located here in in api/services/passport.js:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
The Google service was not returning a profile.username property.
Because of it, the user is not saved in the database and cannot be authenticated. Then the passport callback receives an empty user, so the function that handles errors is fired and the user is redirected to the login page.
This change allows to use the displayName property as the username:
// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
user.username = profile.username;
}
/** Content not generated BEGIN */
// If the username property was empty and the profile object
// contains a property "displayName", add it to the user.
if (!user.username && profile.hasOwnProperty('displayName')) {
console.log(profile); // <= Use it to check the content given by Google about the user
user.username = profile.displayName;
}
/** Content not generated END */
// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
return next(new Error('Neither a username nor email was available'));
}
You could also use the profile.id property because profile.displayName is not necessarily unique (ie: two Google accounts can have an identical displayName). But it is also true accross different services: a Twitter account could also have the same username than a Facebook account. If both register on your application, you will have a bug. This is a problem from the code generated by sails-generate-auth and you should adapt it with the behavior that you want.
I will propose a PR if this solution works for you too.
Alright, so this ultimately turned out to be a known issue with the API.
TL;DR: Enable the Google+ API and the Contacts API as mentioned here. (The Contacts API isn't required, as #AlexisN-o pointed out in the comments. My setup worked as desired with Contacts API disabled. This obviously depends on what scope you're using.)
I believe it's not a nice way of failing since this was an API error that was prevented from bubbling up. Anyway, I dug into passport.authenticate to figure out what was going wrong. This eventually calls the authenticate method defined in the package corresponding to the strategy (oauth2 in this case). In here (passport-google-oauth/lib/passport-google-oauth/oauth2.js) I found that the accessToken was indeed being fetched from Google, so things should be working. This indicated that there was a problem with the requests being made to the token urls. So I ventured a little further into passport-oauth2/lib/strategy.js and finally managed to log this error:
{ [InternalOAuthError: failed to fetch user profile]
name: 'InternalOAuthError',
message: 'failed to fetch user profile',
oauthError:
{ statusCode: 403,
data: '{
"error": {
"errors": [{
"domain": "usageLimits",
"reason": "accessNotConfigured",
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration.",
"extendedHelp": "https://console.developers.google.com"
}],
"code": 403,
"message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration."
}
}'
} }
This was the end of the hunt for me and the first result for the error search led to the correct answer. Weird fix though.

Validating the user of an access_token

In the Facebook dev article Manually Building a Login Flow, there is a section entitled "Confirming Identity". It mentions that you need to validate codes and tokens that you receive from them via your redirect_uri.
My question: Since you don't know anything about the user that just logged in, how do you validate that the user_id that you see in the response from the token inspection endpoint is correct?
The article says:
As a result, your app should confirm that the person using the app is the same person that you have response data for before generating an access token for them.
But, how can you actually do that? Are we expected to show publicly available info about that user_id back to the user with a UI that asks "Is this you?". I haven't seen any apps/sites that do that, so I'm assuming that this isn't practically done.
Am I missing something?
You can use FB.getLoginStatus to retrieve information about the logged in user. It returns a response object for the user. If the user has authenticated your application, the response object will look like this:
{
status: 'connected',
authResponse: {
accessToken: '...',
expiresIn:'...',
signedRequest:'...',
userID:'...'
}
}
You can use the UserId returned in this object to verify the user's identity.