Web Connection
Still struggling with paypal api 400 error
Gravatar is a globally recognized avatar based on your email address. Still struggling with paypal api 400 error
  Michael B
  All
  Jun 1, 2020 @ 05:56am

Rick,

I am simply trying to reach this paypal endpoint. When I run this through WWWC, paypal responds with a 400 'bad request' error.

https://api.paypal.com/v1/customer/partners/WQJNL8USHWHZV/merchant-integrations/BPD5SC7FRK45K

To do it you need to send a valid Bearer Token in the header and application/json content type.

If I run this through postman, it works fine (using same valid bearer token). If I run it with Fiddler running, it works fine.

Today I installed CURL and by default it would not work either. I then read an article that if I have SSL issues I should download the firefox CA bundle and call curl and pass it specifically. Once I did that I had success.

From there, I found this article - https://docs.microsoft.com/en-us/windows/win32/winhttp/ssl-in-winhttp which I think you need to look at. We should be able to pass a specific certificate bundle when we do instantiate ohttp. I think if we can do that, this issue can be solved. I dont want to hack the framework, and was hoping you could comment and or try it.

I discovered this code in wwHttp which sure looks like it is the culprit. If I could simply tell wwHttp which CA cert I wanted to be sent with my httpget() I am sure my prob would be solved. I believe you have that code in wwipstuff though, so I am stuck.

IF this.nError = 12044 && ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED
***  http://www.codeguru.com/cpp/i-n/internet/generalinternet/article.php/c3367
lnCertNo = this.nClientCertNumberIndex   && the certificate to find by its numeric 0 based order - no API to get cert list

*** For debugging you can uncomment this code to see all certs available				
*!*				DECLARE integer InternetErrorDlg IN WinInet.dll ;
*!*					integer hWindow,;
*!*					integer hInternet,;
*!*					integer hRequest,;
*!*					integer uiflags,;
*!*					DWORD lppvData
*!*			
*!*				lnResult = InternetErrorDlg(_Screen.HWnd,hHttpResult,12044,0x04 + 0x01 + 0x02,0)			
			
	DECLARE INTEGER InternetSetOption IN WinInet.dll integer, integer, integer @, integer
	*** INTERNET_OPTION_SECURITY_SELECT_CLIENT_CERT = 47 && 12047
	InternetSetOption(hHttpResult,47,@lnCertNo,4)
	llLoop = .T.
	LOOP  && retry now
  ENDIF

FWIW I tried uncommenting that code block and it does error out, so I could not confirm, but the bottom line is that paypal needs a trusted client certificate for me to make this call, and for whatever reason NONE of my WWWC servers are able to call it. I have to assume since you trap for this error at the framework level, there is something you know that I do not.

Gravatar is a globally recognized avatar based on your email address. re: Still struggling with paypal api 400 error
  Rick Strahl
  Michael B
  Jun 1, 2020 @ 01:43pm

wwHttp doesn't send client certificates to a server unless you explicitly specify it and that doesn't work very well because Windows sucks in getting access to certificates. But none of this matters for calling PayPal - you're not sending a client cert to PayPal. PayPal uses standard TLS certificate authorities - those are determined by the certificate chain installed on your computer and which is updated through Windows.

If you're getting Bad Request errors those are sent from the server - which means it's not a TLS/SSL issues - otherwise you'd get a protocol level error and you wouldn't see anything.

+++ Rick ---

Gravatar is a globally recognized avatar based on your email address. re: Still struggling with paypal api 400 error
  Michael B
  Rick Strahl
  Jun 1, 2020 @ 02:02pm

Are you ready for a paid support call?

ps: I can give you the end point and my Bearer token to try on your own machine. If that works, it means my machine is the issue.

pss: Think about the logic of this, it is working in tools other than my WWWC app, and it works in my WWWC app when in sandbox mode. If not a security issue of some sort, what else could it be?

Gravatar is a globally recognized avatar based on your email address. re: Still struggling with paypal api 400 error
  Michael B
  Michael B
  Jun 1, 2020 @ 03:44pm

Rick - I do have to say it you are not only the voice of reason but you are also a life saver. Our short and to the point call saved my butt (again).

The solution to discover the issue was to extract from my function the simplest amount of code possible and then to test it outside of the workflow. When we did this, we were able to see that the framework was NOT the problem. "As usual" my code was the problem.

The issue turned out to be that my function was recursively creating tokens over and over, instead of re-using a token. So to be clear, the first thing we do is create a token after first checking to see if we have one stored in the session.

	m.lcAccessToken = _sg('PAYPALACCESSTOKEN')
        m.lcDemo = request.querystring('demo')

	* create paypal token (this needs to be renewed at some point)
	IF EMPTY(m.lcAccessToken)
	        m.lcUrl = IIF(!EMPTY(lcDemo),[https://api.sandbox.paypal.com/],[https://api.paypal.com/])
		m.lcBase64 = Strconv(lcClientid + ":" + lcSecret, 13)
		lohttp = Createobject("wwHttp")
		lohttp.addheader("Authorization","Basic " + m.lcBase64)
		lohttp.cContentType = "application/x-www-form-urlencoded"
		lohttp.AddPostKey("grant_type","client_credentials")
		_cresult = lohttp.HttpGet(m.lcurl+'v1/oauth2/token')
		loSerializer = Createobject("wwJsonSerializerSophio")
		loResponse = loSerializer.DeserializeJson(_cResult)
		m.lcAccessToken = loresponse.access_token
		session.setSessionVar('PAYPALACCESSTOKEN',m.lcAccessToken)
	ELSE
		m.lcAccessToken = session.sessionGet('PAYPALACCESSTOKEN')
	ENDIF

Once we have the token, we can use it in recursive calls like the one below.

m.lcMerchantidInPaypal = request.querystring('merchantIdInPayPal') && store this in sellers account

* now get the paypal seller status and save the api keys to their store
m.lcApiUrl = [v1/customer/partners/] + m.lcPartnerId + [/merchant-integrations/] + m.lcMerchantidInPaypal
m.lcFullUrl = m.lcurl + m.lcApiUrl
			
loHttp = Createobject("wwHttp")
lohttp.cContentType = "application/json"
ohttp.addheader("Authorization",'Bearer ' + m.lcAccessToken)
_cresult = lohttp.HttpGet(m.lcFullUrl)

This code is being used in the Paypal Commerce Platform (for Partners).

© 1996-2020