Jump to content

Paypal Auth Token request fails


 Share

Recommended Posts

Hi all,

I'm not smart enough to clearly understand how to "translate this code into AutoIt call, so I used what I did in the past but it doesn't work properly.

Basically I need to receive the Auth Token in order to place further calls.

API docs on Paypal site says:

curl -v -X POST "https://api-m.sandbox.paypal.com/v1/oauth2/token" \
    -u "<CLIENT_ID>:<CLIENT_SECRET>" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials"

or api-m.paypal.com if using live server.

And this is my code (i just canceled the keys received from PP)

#include <Array.au3>
#include <string.au3>
#include <MsgBoxConstants.au3>

#Region Paypal Const
Global $sURL = "https://api-m.paypal.com"  ; /v1/oauth2/token - https://api-m.sandbox.paypal.com
#EndRegion Paypal Const

#Region keys
Global $ClientID = "****"
Global $SecretID = "****"
#EndRegion keys

_Test()

Func _Test()
    Local $sResult = PayPalGet("/v1/oauth2/token")
    ConsoleWrite("$sResult: " & $sResult & @CRLF)
EndFunc   ;==>_Test


Func PayPalGet($sMethod)
    Local $sJsonReq = '-u "' & $ClientID & ':' & $SecretID & '"' & _
            '\ -H "Content-Type: application/x-www-form-urlencoded" \' & _
            '-d "grant_type=client_credentials"'
    Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1")
    $oHTTP.Open("POST", $sURL & $sMethod, False)
    $oHTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
    $oHTTP.Send(Binary($sJsonReq))
    Local $sReceived = $oHTTP.ResponseText
    Return $sReceived

    Return $sReceived
EndFunc   ;==>PayPalGet

When I run it I get:

$sResult: {"error":"invalid_client","error_description":"Client Authentication failed"}

Can you please help me to sort it out?

Thanks in advance

Marco

Link to comment
Share on other sites

Hi @marko001.

This seems to be more of an issue with the PayPal API usage, than with AutoIt?

Here is a stackoverflow question with the same issues. The answer I've linked directly to could be why, if you are certain that your client id and secret are 100% correct.

Link to comment
Share on other sites

Damn, hmm... I'm 100% outside US and indeed credentials are correct, provided by Paypal.

But I did nothing to "approve" my Paypal credentials. I just have my business account.

And, obviously, web page is not available from Italy...

Shall I consider it a "not feasible" integration?

Link to comment
Share on other sites

Well, let's make things simpler:

I already have a "Buy Now" button available on my product. 

What I need is to check if/when the transactions has been done.  Basically I'd query Paypal to look for last transactions.

If there's and easier way to approach that, that is my needing.

Link to comment
Share on other sites

I don't think that you're building your request to PayPal correctly, you seem to mostly be copying the CURL code, with -u, -H, and -d, along with the slashes ( \ ), which are just code line breaks in their example.

I would recommend using PostMan if you have it (or downloading it) and follow their steps for putting it in there, and you can look at the request/response headers to see how it should be set up. Keep in mind that ClientID and SecretID are Base64 encoded together with a : separating them when you send the authorization yourself. CURL is doing this when you provide that information through the -u param

I did a quick test in PostMan, and looking at the code that it generates you likely want to change it to something like this:

#include <Array.au3>
#include <string.au3>
#include <MsgBoxConstants.au3>

#Region Paypal Const
Local $sURL = "https://api-m.sandbox.paypal.com"  ; /v1/oauth2/token - https://api-m.sandbox.paypal.com
#EndRegion Paypal Const

#Region keys
;Make sure that you're using the IDs from https://developer.paypal.com/dashboard/applications/edit
Local $ClientID = '****'
Local $SecretID = '****'

Local $sEncodedAuth = StringStripWS(base64($ClientID & ':' & $SecretID), 8)
__CW('$sEncodedAuth: ' & $sEncodedAuth)
#EndRegion keys

_Test()

Func _Test()
    Local $sResult = PayPalGet("/v1/oauth2/token")
    __CW("$sResult: " & $sResult)
EndFunc   ;==>_Test


Func PayPalGet($sMethod)
    Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1")
    Local $sParams = 'grant_type=client_credentials'
    __CW('Url: ' & $sURL & $sMethod)
    $oHTTP.Open("POST", $sURL & $sMethod, False)
    $oHTTP.SetRequestHeader("Authorization", 'Basic ' & $sEncodedAuth)
    __CW('Adding header: Authorization = Basic ' & $sEncodedAuth)
    $oHTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
    __CW('Adding header: Content-Type = application/x-www-form-urlencoded')
    $oHTTP.Send($sParams)
    __CW($oHTTP.Status)
    Local $sReceived = $oHTTP.ResponseText
    Return $sReceived

    Return $sReceived
EndFunc   ;==>PayPalGet

Func __CW($sMsg)
    ConsoleWrite($sMsg & @CRLF)
EndFunc

; https://www.autoitscript.com/forum/topic/192404-base64-autoit-encode-decode-extra-fast/
;==============================================================================================================================
; Function:         base64($vCode [, $bEncode = True [, $bUrl = False]])
;
; Description:      Decode or Encode $vData using Microsoft.XMLDOM to Base64Binary or Base64Url.
;                   IMPORTANT! Encoded base64url is without @LF after 72 lines. Some websites may require this.
;
; Parameter(s):     $vData      - string or integer | Data to encode or decode.
;                   $bEncode    - boolean           | True - encode, False - decode.
;                   $bUrl       - boolean           | True - output is will decoded or encoded using base64url shema.
;
; Return Value(s):  On Success - Returns output data
;                   On Failure - Returns 1 - Failed to create object.
;
; Author (s):       (Ghads on Wordpress.com), Ascer
;===============================================================================================================================
Func base64($vCode, $bEncode = True, $bUrl = False)

    Local $oDM = ObjCreate("Microsoft.XMLDOM")
    If Not IsObj($oDM) Then Return SetError(1, 0, 1)

    Local $oEL = $oDM.createElement("Tmp")
    $oEL.DataType = "bin.base64"

    If $bEncode Then
        $oEL.NodeTypedValue = Binary($vCode)
        If Not $bUrl Then Return $oEL.Text
        Return StringReplace(StringReplace(StringReplace($oEL.Text, "+", "-"), "/", "_"), @LF, "")
    Else
        If $bUrl Then $vCode = StringReplace(StringReplace($vCode, "-", "+"), "_", "/")
        $oEL.Text = $vCode
        Return $oEL.NodeTypedValue
    EndIf

EndFunc   ;==>base64

I got a successful response like this:
 

$sResult: {"scope":"https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller 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://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks","access_token":""}

 

Edit: Also make sure that you're using the provided Client and Secret for your account, NOT your username and password to login.

Edited by mistersquirrle

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

@mistersquirrle let me check it out now. Proably it's as I said before, I made a wrong call but credentials were fine.
I'll edit this post

 

$sResult: {"scope":"https://uri.paypal.com/services/checkout/one-click-with-merchant-issued-token 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 https://uri.paypal.com/services/payments/payment/authcapture openid 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://uri.paypal.com/services/billing-agreements https://api.paypal.com/v1/payments/.* https://uri.paypal.com/services/reporting/search/read 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/servicessubscriptions https://uri.paypal.com/services/applications/webhooks","access_token":"XXXXXXXXXXXXXXXX","token_type":"Bearer","app_id":"APP-XXXXXXXXXXX","expires_in":32252,"nonce":"2023-01-31T23:19:59Zo_XXXXXXXXXXX"}
 

I believe that's what I was looking for!

Let's see now if' I'm able to use that token to make API calls...

Should I just JSONDecode() the result and pick the token to use for further calls, right?

Edited by marko001
Link to comment
Share on other sites

@marko001 If that is your uneditted/original access_token, I strongly recommend editing and deleting part of it, with the access_token someone else can use your account to do/access anything (aka why I excluded it from my post).

 

Otherwise, correct, decode the JSON and use the access_token in further calls, adding it using something like:

$oHTTP.SetRequestHeader("Authorization", 'Bearer ' & $sAccessToken)

You may also want to either keep track of the token and renew it when it expires, or you could/should be able to just get a new token each time.

 

Edit: It might also be a good idea to decode the 'token_type' and use it like $sTokenType & ' ' & $sAccessToken, just in case it changes from 'Bearer' at some point, but I HIGHLY doubt that'd happen.

Edited by mistersquirrle

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

@mistersquirrle I have an issue when trying to create an order:

Local $sEncodedAuth = StringStripWS(base64($ClientID & ':' & $SecretID), 8)
Global $token

_Test()

Func _Test()
    Local $sResult = PayPalGetToken("/v1/oauth2/token")
    Local $oJsonReturned = Json_Decode($sResult)
    $token = Json_Get($oJsonReturned, '["access_token"]')
    __CW("Token: " & $token)
    $sResult = PaypalCreateOrder($token,"/v2/checkout/orders")
    __CW("Result: " & $sResult)
EndFunc   ;==>_Test

Func PayPalGetToken($sMethod)
    Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1")
    Local $sParams = 'grant_type=client_credentials'
    $oHTTP.Open("POST", $sURL & $sMethod, False)
    $oHTTP.SetRequestHeader("Authorization", 'Basic ' & $sEncodedAuth)
    $oHTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
    $oHTTP.Send($sParams)
    Local $sReceived = $oHTTP.ResponseText
    Return $sReceived
EndFunc   ;==>PayPalGet

Func PaypalCreateOrder($token,$sMethod)
    Local $sJsonOrder='{' & _
            '"intent" : "CAPTURE",' & _
            '"purchase_units" : [' & _
                '{' & _
                    '"reference_id" : ' & _NewGUID() & '",' & _
                    '"amount": {' & _
                        '"currency_code": "EUR",' & _
                        '"value": "1' & _
                    '}' & _
                '}' & _
            '],' &  _
            '}'
    Local $oHTTP = ObjCreate("winhttp.winhttprequest.5.1")
    $oHTTP.Open("POST", $sURL & $sMethod, False)
    $oHTTP.SetRequestHeader("Content-Type", "application/json")
    $oHTTP.SetRequestHeader("Authorization", 'Bearer ' & $token)
    $oHTTP.SetRequestHeader("PayPal-Request-Id", _NewGUID())
    $oHTTP.Send($sJsonOrder)
    Local $sReceived = $oHTTP.ResponseText
    Return $sReceived
EndFunc

Func _NewGUID()
    Return StringLower(StringReplace(StringReplace(StringReplace(_WinAPI_CreateGUID(), "-", ""), "{", ""), "}", ""))
EndFunc   ;==>_NewGUID

This is the format as from developer Paypal website:

image.png.effd50bc83f7fe00e0ed32eb3a5f51bc.png

My return is:

{"name":"INVALID_REQUEST","message":"Request is not well-formed, syntactically incorrect, or violates schema.","debug_id":"c068984d77db3","details":[{"field":"/purchase_units/0","location":"body","issue":"MALFORMED_REQUEST_JSON","description":"The request JSON is not well formed."}],"links":[{"href":"https://developer.paypal.com/docs/api/orders/v2/#error-MALFORMED_REQUEST_JSON","rel":"information_link","encType":"application/json"}]}

So probably there's something in the array containing purchase_units that's not working properly.
Thanks in advance,

M.

Link to comment
Share on other sites

If you displayed the value of $sJsonOrder, you would see something like this:

{"intent" : "CAPTURE","purchase_units" : [{"reference_id" : 0769578d09fe424fa8f776e5001b3a57","amount": {"currency_code": "EUR","value": "1}}],}

I see 3 errors:

  1. The "reference_id" value is missing a leading double quote.
  2. The "value" value is missing a trailing double quote.
  3. The comma right before the ending brace should not be there.

Assuming everything else is correct, the JSON should look something like this:

{"intent" : "CAPTURE","purchase_units" : [{"reference_id" : "99bc9f7ac7934e318ba6c2cd29265af2","amount": {"currency_code": "EUR","value": "1"}}]}

 

Edited by TheXman
Link to comment
Share on other sites

Result1: {"id":"8Y870588JT158264A","status":"CREATED","links":[{"href":"https://api.paypal.com/v2/checkout/orders/XXXXXXXXXX","rel":"self","method":"GET"},{"href":"https://www.paypal.com/checkoutnow?token=XXXXXXXXXX","rel":"approve","method":"GET"},{"href":"https://api.paypal.com/v2/checkout/orders/XXXXXXXXXXXX","rel":"update","method":"PATCH"},{"href":"https://api.paypal.com/v2/checkout/orders/XXXXXXXXXXXXX/capture","rel":"capture","method":"POST"}]}

@TheXman You were right! Thanks a lot for the support!

 

I suppose I should then forward my client to https://www.paypal.com/checkoutnow?token=XXXXXXXXXX to proceed with payment.

Something is going wrong because I can't complete it but probably is because I didn't set after-purchase page URL.

How can I verify if the payment went through? 

I thought to use /v1/reporting/transactions API call but, as from documentation

  • It takes a maximum of three hours for executed transactions to appear in the list transactions call.

So how can I get in real time information when a payment has correctly done? (I can wait on my software with an adlibregister() to check it)?

Thanks

 

Edited by marko001
Link to comment
Share on other sites

I don't know anything really about PayPal development, certainly not enough to answer 

Quote

How can I verify if the payment went through? 

 

I merely know (kind of) how to use the WinHTTP functions.

But I think you would want to look at: https://developer.paypal.com/docs/api/orders/v2/#orders_get and check the status for "APPROVED", and then later likely for "COMPLETED" to make sure it went through, and didn't transition into "VOIDED" or "PAYER_ACTION_REQUIRED".

 

I don't know what you're using PayPal to buy/sell, if it's something physical that you have to package up and send out, you could simply wait to do that until the payment is completed. If it for some software that has some kind of license key, provide it as soon as it's "APPROVED", check the status later, and revoke the key (or access with it) if it moves to an incomplete state. Again though I'm not familiar with how this side of PayPal works, this is just a thought.

We ought not to misbehave, but we should look as though we could.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...