I am trying to verify webhooks for subscriptions in paypal using django python. I am receiving the webhooks but when i send them to get verified i get this error: {'name': 'VALIDATION_ERROR', 'message': 'Invalid request - see details', 'debug_id': 'ccc873865982', 'details': [{'field': '/', 'location': 'body', 'issue': 'MALFORMED_REQUEST_JSON'}], 'links': []}. I have checked what the status code is for the response which gives a 400 response. By looking at the API Docs i see that invalid request + 400 response is either a validation error (which is to do with the Json format, so most likely a syntax error) or an authorization error (which says i need to change the scope). I think it is the validation error because the error points to the body.
Here is the relevant code:
header_params = {
"Accept": "application/json",
"Accept-Language": "en_US",
}
param = {
"grant_type": "client_credentials",
}
cid = settings.PAYPAL_CLIENT_ID
secret = settings.PAYPAL_CLIENT_SECRET
token_i = requests.post('https://api-m.sandbox.paypal.com/v1/oauth2/token', auth=(cid, secret), headers=header_params, data=param).json()
token = token_i["access_token"]
bearer_token = "Bearer x".replace('x', token)
headers = {
"Content-Type": "application/json",
"Authorization": bearer_token,
}
print(request.body)
webhook_event = request.body.decode("utf-8")
data = {
"transmission_id": request.headers["PAYPAL-TRANSMISSION-ID"],
"transmission_time": request.headers["PAYPAL-TRANSMISSION-TIME"],
"cert_url": request.headers["PAYPAL-CERT-URL"],
"auth_algo": request.headers["PAYPAL-AUTH-ALGO"],
"transmission_sig": request.headers["PAYPAL-TRANSMISSION-SIG"],
"webhook_id": "3AJ143072C221060T",
"webhook_event": webhook_event,
}
print(json.dumps(data))
response = requests.post('https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature', headers=headers, json=json.dumps(data)).json()
print('worked')
print(response)
if response["verification_status"] == "SUCCESS":
print('success')
Here is the print of the print(json.dumps(data)) (i removed some ids):
{
"transmission_id": "id_is_here",
"transmission_time": "2021-07-04T23:37:29Z",
"cert_url": "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-7a8abba8",
"auth_algo": "auth_algo",
"transmission_sig": "EODOx5y8kIDycYiBYcIgByiBzHyEfu1/NS2nsumOIksVuw/2vJwHj2FcuHYxIa4n/s+s25xkeqk0CXPiSuqtNUGv4pvFtpwbCVAOCU+Msn304+wBgyb7G24rwUPwrof/5jHYQxqKKX5RzxTrff4oPnisKBDUUXV4s2+KO3h2RYAhrXtwTSPt7cK5ZbGZ6SmfpYJ8qDnYFh4PIesLeflSPQ4vHQrFbgr3NiW63sZruGFJc0hTWWc8L3BhzDuUfiSrxBJLAtrqReC8R0HSV8D+Ywmdeipep54yZeJZXfbmUUGvSYbmVMsVggyzZnltyl1hP5xUd3iIi2jdNWYpLESZzA==",
"webhook_id": "Webhook_id_is_here",
"webhook_event": "{\"id\":\"id_here\",\"event_version\":\"1.0\",\"create_time\":\"2021-07-04T23:37:26.733Z\",\"resource_type\":\"subscription\",\"resource_version\":\"2.0\",\"event_type\":\"BILLING.SUBSCRIPTION.CREATED\",\"summary\":\"Subscription created\",\"resource\":{\"start_time\":\"2021-07-04T23:37:26Z\",\"quantity\":\"1\",\"create_time\":\"2021-07-04T23:37:26Z\",\"custom_id\":\"custom_id_here\",\"links\":[{\"href\":\"https://www.sandbox.paypal.com/webapps/billing/subscriptions?ba_token=BA-2UF06918UT180770Y\",\"rel\":\"approve\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-4S15814RUE18\",\"rel\":\"edit\",\"method\":\"PATCH\"},{\"href\":\"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-4S15814RUE18\",\"rel\":\"self\",\"method\":\"GET\"}],\"id\":\"sub_id_here_\",\"plan_overridden\":false,\"plan_id\":\"P-0DA33732CG980003EMDQJ6BA\",\"status\":\"APPROVAL_PENDING\"},\"links\":[{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-91E32247D9338170B-4RF07261WK370823W\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/id_here/resend\",\"rel\":\"resend\",\"method\":\"POST\"}]}"
}
Your data for the key webhook_event is double encoded as JSON.
You have to decode it with
webhook_event = json.loads(webhook_event)
or not encode it in the first place.
Related
It shows credentials are missing when I try to use the Google Gmail API to send messages. I want to send an email to my other Gmail account using the Google Gmail API.
import sys
import requests
import base64
import sys
from email.mime.text import MIMEText
AccessToken = ""
params = {
"grant_type": "refresh_token",
"client_id": "xxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxx",
"refresh_token": "xxxxxxxxxxxxxxxxxxxx",
}
authorization_url = "https://www.googleapis.com/oauth2/v4/token"
r = requests.post(authorization_url, data=params)
if r.ok:
AccessToken = str((r.json()['access_token']))
EmailFrom = "Test1#gmail.com"
EmailTo = "test2#gmail.com"
def create_message(sender, to, subject, message_text):
message = MIMEText(message_text, 'html')
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
body = {'raw': raw}
return body
body = create_message(EmailFrom, EmailTo, "Just wanna Say Waka Waka!", "Waka Waka!")
url = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
header = {
'Authorization': 'Bearer ' + AccessToken,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
r = requests.post(
url,
header,
body
)
print("\n")
print(r.text)
print("\n")
Error:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"errors": [
{
"message": "Login Required.",
"domain": "global",
"reason": "required",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "caribou.api.proto.MailboxService.SendMessage",
"service": "gmail.googleapis.com"
}
}
]
}
}
I'm not a google api user, but I've used oauth several times, and your setup is a bit different than what I usually use or what I see from a quick sniff of Google's documention. For example, I use client-creds instead of a refresh token. For what I'm seeing above in your code, no reason to refresh an old token, when you can just mint another one. Compared to yours, usually with oauth, we'll do something like auth=(client_id, client_secret)
Lastly, before you change anything big, when you changed your header to place the AccessToken variable in quotes, did you use an f-string? What is the output of
print(header)
after you have defined it? Is it what you expect? If you didn't format it, it's going to have the variable's name rather than value.
If that's all ok, I'd try to write it according to OAuth standards that I've used several times. Not telling you how to do it, but you could try something like:
def getKey():
url = "https://www.googleapis.com/oauth2/v4/token"
client_id = "*yourgoogleclientid*"
client_secret = "*yourgoogleclientsecret*"
data = {
'grant_type': 'client_credentials'
}
r = requests.post(url, json=data, auth=(client_id, client_secret))
key = r.json()['access_token']
return key
def getWhatever(key, url):
header = {
'Authorization': f'Bearer {key} '
}
params = {
'whatever params': 'you might need'
}
response = requests.get(url, headers=header, params=params)
* parse, process, return, whatever you'd like to do with the response.*
now to use it....
if __name__ == '__main__':
myKey = getKey()
whatImLookingFor = getWhatever(myKey, "*https://google_api_endpoint*")
I've been trying to query the Notion API using Python, and as I have more than 100 results, I need to either filter them down or be able to paginate. I tried different variants of the following code to no avail:
headersNotion = {
"Authorization": "Bearer " + notionToken,
"Content-Type": "application/json",
"Notion-Version": "2021-05-13"
}
data = {
"start_cursor" : nextCursor
}
readUrl = f"https://api.notion.com/v1/databases/{databaseId}/query"
res = requests.request("POST", readUrl, data=data, headers=headers)
I've also tried with data = { "filter": {"cover" : "is_empty"} } or even with an idiotic or empty filter, but as soon as I add any sort of data to the request, I get a 400 error:
{"object": "error", "status": 400, "code": "invalid_json", "message": "Error parsing JSON body."}
Would anyone have any idea what I might be doing wrong?
look here headersNotion schould be headers.
https://developers.notion.com/reference/post-database-query
Just as example.
payload = {
'filter': {
'and': [
{
'property': 'aktualisiert am',
'date': {
'after': str(age)
}
},
{
'property': 'aktiv',
'text': {
'equals': 'yes'
}
}
]
}
}
headers = {
"Accept": "application/json",
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Notion-Version": "2021-08-16"
}
def readDatabase(databaseId, headers, payload):
readUrl = f"https://api.notion.com/v1/databases/{databaseId}/query"
res = requests.request("POST", readUrl, json=payload, headers=headers)
datadb = res.json()
print(res.text) #for Debug
# Errors Returns a 404 HTTP response if the database doesn't exist, or if the integration doesn't have access to the database.
# Returns a 400 or a 429 HTTP response if the request exceeds the request limits.
# Returns 200 HTTP responce if OK
#print ('')
print ('')
if res.status_code == 200:
print('200 Success!') # Yes everything OK!
elif res.status_code == 404:
print('404 Not Found.')
#sendmail
subject = " Fehler!! 404 Database Not Found"
send_email(user, password, recipient, subject, body)
return
elif res.status_code == 429:
print(' Fehler !! 429 request exceeds the request limits.')
#sendmail
subject = " Fehler !! 429 request exceeds the request limits."
send_email(user, password, recipient, subject, body)
return
elif res.status_code == 400:
print('400 request exceeds the request limits.')
#sendmail
subject = " Fehler !! 400 request exceeds the request limits. "
send_email(user, password, recipient, subject, body)
return
# print(res.text)
# write csv File
with open('./dbmain.json', 'w', encoding='utf8') as f:
json.dump(datadb, f, ensure_ascii=False)
Looks like I'm doing just about everything correct but I keep receiving this error....
Response text error:
response .text {"name":"INVALID_TRACKING_NUMBER","message":"The requested resource ID was not found","debug_id":"12345","details":[{"field":"tracker_id","value":"1234-567890","location":"path","issue":"INVALID_TRACKING_INFO"}],"links":[]}
Response status: <Response [404]>
I'm using a real transaction and a real tracking number.
I'm doing this through python and this is my code:
def paypal_oauth():
url = 'https://api-m.paypal.com/v1/oauth2/token'
headers = {
"Content-Type": "application/json",
"Accept-Language": "en_US",
}
auth = "1234-1234","0987"
data = {"grant_type":"client_credentials"}
response = requests.post(url, headers=headers, data=data, auth=(auth))
return response
def paypal_tracking(paypal_transaction_token, tracking_number, status, carrier):
try:
_paypal_oauth = paypal_oauth()
_paypal_oauth_response = _paypal_oauth.json()
except Exception as e:
print(e)
pass
access_token = _paypal_oauth_response['access_token']
url = 'https://api-m.paypal.com/v1/shipping/trackers/%s-%s/' % (paypal_transaction_token, tracking_number)
# https://api-m.paypal.com/v1/shipping/trackers/1234-567890/
carrier = carrier_code(carrier)
# This grabs carrier from a method and gets back: 'DHL'
headers = {
'Content-Type' : 'application/json',
'Authorization' : 'Bearer %s' % access_token,
}
# {'Content-Type': 'application/json', 'Authorization': 'Bearer 1234'}
data = {
"transaction_id":"%s" % paypal_transaction_token,
"tracking_number":"%s" % tracking_number,
"status": "%s" % status,
"carrier": "%s" % carrier
}
# {'transaction_id': '1234', 'tracking_number': '567890', 'status': 'SHIPPED', 'carrier': 'DHL'}
response = requests.put(url, headers=headers, data=json.dumps(data))
return HttpResponse(status=200)
Anyone with experience in paypal or using API's see my issue?
To add a tracking number (not update), use an HTTP POST request, as documented.
The URL to POST to is https://api-m.sandbox.paypal.com/v1/shipping/trackers-batch , with no additional URL parameters.
The body format is
{
"trackers": [
{
"transaction_id": "8MC585209K746392H",
"tracking_number": "443844607820",
"status": "SHIPPED",
"carrier": "FEDEX"
},
{
"transaction_id": "53Y56775AE587553X",
"tracking_number": "443844607821",
"status": "SHIPPED",
"carrier": "FEDEX"
}
]
}
Note that trackers is an array of JSON object(s).
I want to make an apple sign in with social auth python. Now I have a piece of code below. When I do a request, I get an invalid grant error. I think that 'id_token' is not valid. But I don't know how to get that id_token, because I get the id_token after the request. Can anybody help me. Big Thanks!
headers = {
'alg': 'ES256',
'kid': settings.SOCIAL_AUTH_APPLE_ID_KEY
}
payload = {
'iss': settings.SOCIAL_AUTH_APPLE_ID_TEAM,
'iat': now,
'exp': now + TOKEN_TTL_SEC,
'aud': 'https://appleid.apple.com',
'sub': settings.SOCIAL_AUTH_APPLE_ID_CLIENT,
}
client_secret = jwt.encode(
payload,
settings.SOCIAL_AUTH_APPLE_ID_SECRET,
algorithm='ES256',
headers=headers
).decode("utf-8")
headers = {'content-type': "application/x-www-form-urlencoded"}
data = {
'client_id': 'com.scread.app',
'client_secret': client_secret,
'code': 'id_token',
'grant_type': 'authorization_code'
}
res = requests.post('https://appleid.apple.com/auth/token', data=data, headers=headers)
It seems that the service id must end like .app. Thus for example com.company.app. That was the solution for me.
I am currently trying to update a Sharepoint 2013 list.
This is the module that I am using using to accomplish that task. However, when I run the post task I receive the following error:
"b'{"error":{"code":"-1, Microsoft.SharePoint.Client.InvalidClientQueryException","message":{"lang":"en-US","value":"Invalid JSON. A token was not recognized in the JSON content."}}}'"
Any idea of what I am doing wrong?
def update_item(sharepoint_user, sharepoint_password, ad_domain, site_url, sharepoint_listname):
login_user = ad_domain + '\\' + sharepoint_user
auth = HttpNtlmAuth(login_user, sharepoint_password)
sharepoint_url = site_url + '/_api/web/'
sharepoint_contextinfo_url = site_url + '/_api/contextinfo'
headers = {
'accept': 'application/json;odata=verbose',
'content-type': 'application/json;odata=verbose',
'odata': 'verbose',
'X-RequestForceAuthentication': 'true'
}
r = requests.post(sharepoint_contextinfo_url, auth=auth, headers=headers, verify=False)
form_digest_value = r.json()['d']['GetContextWebInformation']['FormDigestValue']
item_id = 4991 # This id is one of the Ids returned by the code above
api_page = sharepoint_url + "lists/GetByTitle('%s')/items(%d)" % (sharepoint_listname, item_id)
update_headers = {
"Accept": "application/json; odata=verbose",
"Content-Type": "application/json; odata=verbose",
"odata": "verbose",
"X-RequestForceAuthentication": "true",
"X-RequestDigest": form_digest_value,
"IF-MATCH": "*",
"X-HTTP-Method": "MERGE"
}
r = requests.post(api_page, {'__metadata': {'type': 'SP.Data.TestListItem'}, 'Title': 'TestUpdated'}, auth=auth, headers=update_headers, verify=False)
if r.status_code == 204:
print(str('Updated'))
else:
print(str(r.status_code))
I used your code for my scenario and fixed the problem.
I also faced the same problem. I think the way that data passed for update is not correct
Pass like below:
json_data = {
"__metadata": { "type": "SP.Data.TasksListItem" },
"Title": "updated title from Python"
}
and pass json_data to requests like below:
r= requests.post(api_page, json.dumps(json_data), auth=auth, headers=update_headers, verify=False).text
Note: I used SP.Data.TasksListItem as it is my type. Use http://SharePointurl/_api/web/lists/getbytitle('name')/ListItemEntityTypeFullName to find the type