Closed maulikpat closed 6 years ago
It's been a long while, but if you don't have a midtier, then you can just refresh the token yourself from the client. This looks like a "first party" token - does it actually work to call the various PPH functions? i.e. can you process a transaction with it? If so, then you can just redo the call above periodically to give the SDK a new access token. If you wanted to be fancy, you could give the SDK a fake refresh url and then intercept the SDK network requests looking for that one, and refresh the token at that time.
(Disclaimer: I don't work at PayPal anymore)
@djMax Well, as I can see in demo project there is no way set access token directly to the SDK. There is 2 methods to setup SDK setupWithCredentials and setupWithCompositeTokenString.
Is there a way to set token without these methods ?
I only wanted to accept payments via card readers.
First party tokens won't work for the PPH SDK, unfortunately. You'll need to build out the onboarding flow, simply to get your own refresh token for the first time and then you'll use that, going forward, to generate your access tokens. More info on that process can be found here. If you already have PPH enabled on your PayPal account, then you only need to do the 'Permissions flow' piece from that page.
Secondly, if you're simply generating the access token from the refresh token directly, then you'll utilize the setupWithCredentials
method and pass in the access token. You can also pass in a refresh URL to automatically update the access token when needed, else you can do a timer of sorts to know when you need to refresh the access token. More on that process can be found here
It should be in those docs, but I will state it here just for the record. For security purposes, we do not recommend storing client credentials (client ID & secret) along with refresh tokens within the app itself. This is the reason for a server-side integration, cloud or otherwise, to handle such token management.
@ppmtscory how can I use setupWithCredentials
without refresh url ? Should I pass fake url as djmax mentioned ?
I tried to provide fake url like below
[PayPalHereSDK setupWithCredentials:[dict valueForKey:@"access_token"] refreshUrl:@"www.google.com" tokenExpiryOrNil:[dict valueForKey:@"expires_in"] thenCompletionHandler:^(PPHInitResultType status, PPHError *error, PPHMerchantInfo *info)
App is crashes :
2017-12-04 12:51:21.816534+0530 TakePayment[783:43621] Assertion failure in -[PPHAccessAccount fetchMerchantInfoAndThen:], /Users/schandrashekar/Documents/Workspace/ios-int-dist/source/PayPalHereSDK/Core/PPHAccessAccount.m:156 2017-12-04 12:51:21.828128+0530 TakePayment[783:43621] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OpenID userInfo request failed to return email. Perhaps the scope was invalid?' *** First throw call stack: ( 0 CoreFoundation 0x00000001130391cb exceptionPreprocess + 171 1 libobjc.A.dylib 0x000000011299bf41 objc_exception_throw + 48 2 CoreFoundation 0x000000011303e362 +[NSException raise:format:arguments:] + 98 3 Foundation 0x00000001117da089 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193 4 TakePayment 0x000000010fad767b 45-[PPHAccessAccount fetchMerchantInfoAndThen:]_block_invoke + 3019 5 TakePayment 0x000000010f98a9d3 83+[PPSDKCoreServices addNetworkRequest:withID:allowExternalReqHandlers:withHandler:]_block_invoke232 + 1763 6 TakePayment 0x000000010fa2c40a 57-[_OBSCURO_EMFNSNetOperation connectionDidFinishLoading:]_block_invoke_2 + 58 7 libdispatch.dylib 0x0000000115ed03f7 _dispatch_call_block_and_release + 12 8 libdispatch.dylib 0x0000000115ed143c _dispatch_client_callout + 8 9 libdispatch.dylib 0x0000000115edc6f0 _dispatch_main_queue_callback_4CF + 628 10 CoreFoundation 0x0000000112ffbef9 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 9 11 CoreFoundation 0x0000000112fc0662 __CFRunLoopRun + 2402 12 CoreFoundation 0x0000000112fbfa89 CFRunLoopRunSpecific + 409 13 GraphicsServices 0x000000011818d9c6 GSEventRunModal + 62 14 UIKit 0x0000000113c6dd30 UIApplicationMain + 159 15 TakePayment 0x000000010f9646ff main + 111 16 libdyld.dylib 0x0000000115f4dd81 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException
App Permissions :
That error doesn't have anything to do with the refresh URL. For that, you can just submit an empty string. That error has to do with the scopes selected on your REST app that you posted a screen shot of.
You'll need to click 'Advanced Options' next to Log In with PayPal and then further expand those options that open to make sure that you have all the address fields and email fields selected.
I can't remember - are first party tokens a supported use case?
@djMax Nope. I've tried to talk to the folks responsible (and I believe you did when you were here as well) but it's still sitting as feedback at this point, unfortunately.
@ppmtscory I am still getting crash and same crash log. Below is my setup
I am providing Return url fake is it ok in Live as well ?
Also, will this approach work with Card Readers or PPH ? I mean getting access token without server.
@djMax @ppmtscory any help ? suggestion here ?
Please let me know the sandbox account email address and client ID that you are using so that I can take a look.
Sure. email : maulik@dataimpactsol.com Client ID : AaJvdrSKIvQ3tYPJM0oaYLl6Oc1ihKatXNelBYeXJXNwD3Aj77ad2nXuPjf8EzVBTqowk22oW_e64X29
@ppmtscory
Any help ?
My apologies on the delay. Your REST app looks ok, can you provide a code sample of how you're generating the access token? Can you validate that you're passing all the right scopes to the /authorize
endpoint as outlined here?
@ppmtscory Please take a look.
` [PayPalHereSDK selectEnvironmentWithType:ePPHSDKServiceType_Sandbox];
`- (void)getToken
{
NSString *clientID = @"AaJvdrSKIvQ3tYPJM0oaYLl6Oc1ihKatXNelBYeXJXNwD3Aj77ad2nXuPjf8EzVBTqowk22oW_e64X29";
NSString *secret = @"EOirpfzTMY......3CMC4Cy";
NSString *authString = [NSString stringWithFormat:@"%@:%@", clientID, secret];
NSData * authData = [authString dataUsingEncoding:NSUTF8StringEncoding];
NSString *credentials = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedStringWithOptions:0]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
[configuration setHTTPAdditionalHeaders:@{
@"Accept": @"application/json",
@"Accept-Language": @"en_US",
@"Content-Type": @"application/x-www-form-urlencoded",
@"Authorization": credentials }];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:@"https://api.sandbox.paypal.com/v1/oauth2/token"]];
request.HTTPMethod = @"POST";
NSString *dataString = @"grant_type=client_credentials";
NSData *theData = [dataString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
__weak typeof(self) weakSelf = self;
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:theData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error)
{
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(@"data = %@", dict);
[PayPalHereSDK setupWithCredentials:[dict valueForKey:@"access_token"] refreshUrl:@"" tokenExpiryOrNil:[dict valueForKey:@"expires_in"] thenCompletionHandler:^(PPHInitResultType status, PPHError *error, PPHMerchantInfo *info) {
if (error) {
[weakSelf loginWithPayPal];
} else {
[weakSelf gotoPaymentScreen];
}
}];
}
}];
[task resume];
}`
So like I explained above, you are getting a first party token and those do not work within the SDK. Even though you are processing on your own behalf, it needs to be a third party token that's used so, therefore, you need to implement some internal version of the onboarding so that you can generate a refresh token for your account. You'll then use that refresh token to generate the access tokens going forward. Please review the links in my previous comments above for more info on how to handle the onboarding to get your own refresh token.
oh ok :(
So I need to have mid tier server right ?
Also can you suggest any .NET/C# code example to implement at server side ?
@ppmtscory
I don't see the option for the initial redirect to the /authorize
endpoint within the SDK, but I see code, here, to turn the authorization code into a refresh token and also refresh the access token based on the refresh token.
We're revisiting the issue of first/third party token management within the SDK so will mark this as an enhancement and report back with the results of those talks.
@ppmtscory thanks for your reply !
I have been trialing this with a bound Xamarin library on version 1.6 and the composite token required consists of AccessToken+TheTokenExpiry+RefreshUrl it would be great not have a dependency on a mid tier server to achieve this.
@ppmtscory @DJ92 Issue closed ? So it means there is no chance of this enhancement ?
Hey @maulikpat, we have marked this as an enhancement in our internal tasks. Will send an update once this is released.
@DJ92 hi. Any update on this ?
Hi @maulikpat , the enhancement to eliminate mid-tier server is still in planning and will take time to get prioritized. For now, the suggestion by @ppmtscory in the last comment should be a valid workaround.
@DJ92 thanks for your reply. I had already tried that way but as mentioned 1st party token is not working with SDK.
@DJ92 also can you tell me if PayPal has any solution like Square has ? Like our app opens PayPal app to take payments and after process of payment PayPal opens our app with info like success or fail ?
Hey @maulikpat . Sorry in the delay of my response. I'm checking internally with the team to check on first party authentication using the .NET SDK. For your use case, yes we do have a browser based solution at Sideloader. Here's a demo webapp showing the same use case: Demo App
@DJ92 thanks for reply. I will look into it.
@DJ92 @ppmtscory Hi, is there any news regarding this? I am still looking for this.
You can use a first party token with the SDK now, but you will still have to have some sort of service to generate the access token and provide it to the SDK. Also, for token expiry, it's good to have it set up so that the SDK can reach out to the URL you provide to get the access token automatically when it expires (access tokens have an 8 hour expiry).
@ppmtscory thanks for the quick reply. So mid tire server is required. Is there any demo available for server-side code implementation?
We have multiple code samples in our documentation.
@ppmtscory I have implemented the new SDK v2 and running the sample App. I am able to do test transactions as well. Thanks for the help.
In the sample app, it opens http://pph-retail-sdk-sample.herokuapp.com/toPayPal/
for the login page but I don't have that page so do I need to follow the same step and create that page to open login? I mean can I bypass this with a Client ID and secrete?
@ppmtscory if my uses only first-party transactions do I need to have mid-tier server? I mean First Party requires an access token to be refreshed?
Yes, access tokens only last 8 hours so you need a way to either have the SDK refresh it (refreshUrl) or you would have to reinitialize every 8 hours when the token expires.
@ppmtscory is there any way to check access token is valid or expired?
There's an expiry param that comes back in the response from generating the access token that you can keep track of else if it expires in the process of a HTTP request from the SDK to the PayPal backend, the SDK will provide a 401 unauthorized error back if the access token is expired or invalid. There's a token expired listener that you can set which will allow you to simply provide an access token if you don't have a refresh URL.
@ppmtscory ok thank you so much for help. I have set up a mid-tier server (https://github.com/paypal/paypal-retail-node) with Heroku and login flow is working and transactions as well for sandbox by using a card reader.
I want to test the refresh token but as you said token will expire after 8 hours so do I need to wait for 8 hours? or is there any way to check before 8 hours? My code is as below
SdkCredential *sdkCreds = [[SdkCredential alloc] initWithAccessToken:accessToken refreshUrl:[tokenDefault stringForKey:@"https://distestapp123.herokuapp.com/refresh/"] environment:@"sandbox"];
You can test refresh logic by passing an invalid token there. If you pass in a string as "accessToken" for example, it's not valid so the SDK will get an invalid token back from the PayPal back end and then reach out to your refreshUrl.
@ppmtscory ok. I did like below
SdkCredential *sdkCreds = [[SdkCredential alloc] initWithAccessToken:@"abcd" refreshUrl:[tokenDefault stringForKey:@"https://distestapp123.herokuapp.com/refresh/"] environment:@"sandbox"];
It gave me errors as below:
Debug ID: (null) Error Message: 401 Error Code: Could not initialize merchant with token
Does it mean an invalid token? I guess no.
To be clear, by https://distestapp123.herokuapp.com/refresh/
means endpoint from https://github.com/paypal/paypal-retail-node/blob/master/index.js
Am I doing it right?
Yes, 401
is an invalid token error. The SDK got that response from the PayPal backend when you called initializeMerchant and the SDK is giving that back to you. At this point, the SDK would reach out to that refresh URL, and you should see the same in the console logs. If that refresh is not happening, then there's something wrong with your heroku instance. You can check those logs to see what happened there. Keep in mind the sample heroku instance that you linked to is meant for instructional purposes only and not for any production deployments.
You can always just host code on your heroku instance that will generate an access token and echo the response.
@ppmtscory Heroku doesn't show any error logs for /refresh
endpoint
I guess the refresh URL is not working. The demo paypal-retail-node
has any issue? Because I am just pointing to https://github.com/paypal/paypal-retail-node/blob/0fb2c14fb35d35f5b6710a83427a8253832e752e/index.js#L111 to get refresh token. So demo is not working? or I am doing anything wrong here?
Is there any working demo or more focused document that can guide to create refresh ULR function in Node? So I can add that function code to Heroku.
The sample service is, by default, meant for third party token management and you can see that by the code
parameter in your logs (code
is returned and then used to get a refresh token which is a third party use case). In order to use first party with that, you'll need to enable the setup flow like it explains in the README. Otherwise, my recommendation, would be to just set your heroku instance to generate an access token via your own code.. https://developer.paypal.com/docs/api/get-an-access-token-curl/
@ppmtscory I think, to invalid the token by passing random string is not the correct way to check refresh URL token. Because when I do login 1st time, everything is working and I getting access_token, refresh_url, and env from Paypal login. And log says:
2020-04-22 16:46:38.577723-0700 PPHSDKSampleApp[29412:2723787] TRACK: [merchant] MerchantInitSuccessful { "appId": "None", "appVersion": "None", "appBuild": "None", "appName": "None", "osName": "None", "sdkVersion": "None", "partnerType": "NOREF", "isSideloader": false, "partnerId": "NA", "softDescriptor": "NA", "storeId": "NA", "firmwareImageClientType": "sdk", "payerId": "XGG89PYVT6D86", "isOffline": false }
Now when I change the access token here,
SdkCredential *sdkCreds = [[SdkCredential alloc] initWithAccessToken:@"fasdf" refreshUrl:[tokenDefault stringForKey:@"REFRESH_URL"] environment:[tokenDefault stringForKey:@"ENVIRONMENT"]];
log says:
2020-04-22 16:52:11.560616-0700 PPHSDKSampleApp[29433:2727876] INFO: [paypalRest] Attempting an AccessToken refresh from RefreshURL 2020-04-22 16:52:11.568378-0700 PPHSDKSampleApp[29433:2727876] Task <6202C01F-9434-42C5-AB60-1829D60D8870>.<10> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSUnderlyingError=0x6000015ad5c0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}, NSErrorFailingURLStringKey=https%3A%2F%2Fdistestapp123.herokuapp.com%2Frefresh%3F%26token%3DM8S2MclCjOfINY%252FSrbmsMdKYoMyudA66CzCLCua02zWeXnXAIPZLSeEIKhIavEoNejqVfY1kzikV9B%252BXguMN%252BP8Ja721QUA8jIPUnWVYprfjxWYHwEsAvO2mOH4P1%252BI7GsubUapyFwu5ibksxZhrUgH553AwmYSMBI0cwMyJD9mRDkTPIclIDX%252FB0utOjID07zkvziVDc5Ee%252F2wZggZexYRKcG7pypF9neyjl7W8cjT7JYzpMcqJGzpRPsSE63jjjSvan%252FVyWF4YBMgV%252FpDTyGmqruO%252FIkv0ZHY3vihZiSKT%252FPV2, NSErrorFailingURLKey=https%3A%2F%2Fdistestapp123.herokuapp.com%2Frefresh%3F%26token%3DM8S2MclCjOfINY%252FSrbmsMdKYoMyudA66CzCLCua02zWeXnXAIPZLSeEIKhIavEoNejqVfY1kzikV9B%252BXguMN%252BP8Ja721QUA8jIPUnWVYprfjxWYHwEsAvO2mOH4P1%252BI7GsubUapyFwu5ibksxZhrUgH553AwmYSMBI0cwMyJD9mRDkTPIclIDX%252FB0utOjID07zkvziVDc5Ee%252F2wZggZexYRKcG7pypF9neyjl7W8cjT7JYzpMcqJGzpRPsSE63jjjSvan%252FVyWF4YBMgV%252FpDTyGmqruO%252FIkv0ZHY3vihZiSKT%252FPV2, NSLocalizedDescription=unsupported URL} 2020-04-22 16:52:11.568781-0700 PPHSDKSampleApp[29433:2727876] ERROR: [paypalRest] Failed to refresh token: { "errMessage": "NSURLErrorDomain", "rzDesc": "{\"headers\":null,\"statusCode\":0,\"body\":null}" }
I confused now. What is the correct implementation at mid-tier server for refresh URL? Any working example available?
Again, for first party, my recommendation would be to just set your heroku instance to generate an access token via your own code. This doc page has curl and postman examples for you to deploy. The refresh URL would just be the link to your heroku instance that has the code to generate the access token.
@ppmtscory I think its working now. Though, I didn't change anything in the paypal-retail-node
demo code it's still running as it was. But I created another project on Heroku and add just 1 function for the refresh token. And it's returning the access token if I pass any random string in SDK.
So I am opening the Paypal login page by the paypal-retail-node
demo code https://distestapp123.herokuapp.com/toPayPal/
.
And I am passing refresh URL of my another app/project at Heroku https://pp-integration.herokuapp.com/refresh
, which has curl code to get access token, to the SDK.
SdkCredential *sdkCreds = [[SdkCredential alloc] initWithAccessToken:@"dfasd" refreshUrl:@"https://pp-integration.herokuapp.com/refresh" environment:@"sandbox"];
I will try to put it all together on the server-side.
Thanks for your help.
Hello,
I am integrating PayPal Here SDK with iOS. In Documentation it says we need to have mid tier server for getting access tokens and refresh tokens.
But I want to know that is there any way without server need I can get access token ? Currently I am able to generate access by : `
Response :
Now how can I use this token ? As in SDK there is 2 methods
I am OK with having credential stored at app side as my app wont go at apple stores.
Any suggestions ?