I'm developing a Twilio app that leverages the Machine Detection features. I make a request using the Twilio python client as follows:
import twilio
twilio_client = Client(ACCOUNT_SID, AUTH_TOKEN)
call = twilio_client.calls.create(
to=number,
from_=TWILIO_PHONE_NUMBER,
url=EXTERNAL_URL + '/call',
status_callback=EXTERNAL_URL + '/call_status',
status_callback_event=["initiated", "ringing", "answered",
"completed", "no-answer", "busy",
"failed", "canceled", "queued"],
machine_detection='Enable',
machine_detection_timeout=20,
)
but when I try to access the call.answered_by field I discover that the value is None. I also get back None when I use the call status API to fetch the call status 10 seconds later:
call = twilio_client.calls(sid).fetch()
Also in my my webhook that I register at `EXTERNAL_URL + '/call_status' to get notifications I don't get an 'AnsweredBy' field. I've also made sure I enabled the feature in the Twilio web console. What else I am missing?
Twilio developer evangelist here.
Thanks to Andy and Alex in the comments, I think they actually do cover everything.
The key here is that the AnsweredBy field will be sent as part of the webhook to the URL you set as the url parameter in the API request. In this case, that is the URL EXTERNAL_URL + '/call'.
The AnsweredBy field should eventually appear in the REST API for the call resource itself, but the way to use it is via the webhook.
Let me know if this helps at all.
Related
I have done all the setup to verify my receipts server side (in python with the requests package).
Here is my code :
url = "https://buy.itunes.apple.com/verifyReceipt"
request_body = {"receipt-data": token}
headers = {'Content-Type': 'application/json'}
response = requests.post(url=url, headers=headers, data=request_body)
The only variable here is token that is sent from the client (flutter) and that is : purchaseDetails.verificationData.serverVerificationData.
I can't verify the receipt because if i pass token as string, i receive always a 21002 error (malformed).
If i try something like this in python :
token = base64.b64encode(token)
It throws this error : a bytes-like object is required, not 'str' which i don't understand because i am actually passing a bytes object.
What is the correct format to pass to the POST request to verify the iOS receipt ?
Is the flutter one correct or should we encode something ?
Any concrete example will be accepted because i could not find any.
PS : i am redirected to the sandbox url "https://sandbox.itunes.apple.com/verifyReceipt" if the production one fails. The sandbox response is the same as the production one (statusCode: 21002)
It looks like either your receipt is not correct ( sandbox has issues sometimes ) or your server-side setup is wrong
For the first point, you can try creating a receipt by generating a storeKit config file.
This can't be done in flutter, you have to open iOS module with code and setup storekit config file by going here.
After setting up the storekit file, you can either run the app from xCode directly or just close xCode and run from your preferred flutter IDE
Now, iOS will never hit the production purchase identifiers when you try to fetch/buy products from the app, and instead fetch the products from your storekit config and generate a receipt from those. This receipt is accepted by apple sandbox verification endpoint, you can also test refunds and subscription cancellations from xCode using a storekit config.
For the second point, you have to enable the app specific shared secret in iTunes connect and then use that in the 'password' key in the receipt validation API.
Here is where you find it
AppStoreConnect > Your app > Subscriptions
If it still doesn't solve the issue, I'd be happy to assist further.
EDIT: I just tested purchasing an auto renewable subscription purchased in sandbox (not storeki
t) and then validating it using the sandbox URL and it returned the correct receipt data. In your post above, you don't need to base64 encode the purchaseDetails.verificationData.serverVerificationData since its already encoded. Have you tested this on postman? It Works there
EDIT: so the request is malformed because you are not sending data as String so you need to dump the dict :
request_body = json.dumps({"receipt-data": token})
I'm new to flask. I used url_for function to locate twilio xml file for responding the call. I'm running my app in local host. While Executing this code it returns unable to find that xml file error.
The error number is 21205
my route is:
#app.route('/scall', methods=['POST'])
def scall():
account_sid = '***************'
auth_token = '***************'
client = Client(account_sid, auth_token)
phonenumber = request.form['phonenumber']
call = client.calls.create(
url="url_for('templates', filename='res.xml')",
to='+91' + phonenumber,
from_='************'
)
Twilio developer evangelist here.
Your problem is that Twilio is trying to find that XML file in a place it doesn't have access to.
When you pass the url parameter, you're simply telling Twilio what to do when a call is answered. So Twilio then makes an HTTP request to that XML file which will hopefully have TwiML it understands.
What you need to do instead is give it a URL that is accessible publically. There are a few options for how to do this:
Host the XML somewhere accessible (S3, Dropbox, your webserver, etc)
Create a TwiML Bin inside the Twilio console and point directly to it.
Use a tool like ngrok to expose your localhost and point to that URL from within your code.
Hope this helps you out.
I am retrieving a ChromeOS device MAC address via the Google Admin Directory API using the device's Serial Number as reference, and am making my calls through
apiclient.
service = discovery.build('admin', 'directory_v1', developerKey=settings.API_KEY)
Here are the calls available for ChromeOS devices; my issue is that I require a Device ID in order to execute the following:
service.chromeosdevices().get(customerId=settings.CID, deviceId=obtained_id, projection=None).execute()
I can send a GET query via the following format:
https://www.googleapis.com/admin/directory/v1/customer/my_customer/devices/chromeos?projection=full&query=id:" + serial + "&orderBy=status&sortOrder=ascending&maxResults=10", "GET")
... but I'm trying to avoid using OAuth2 and just use my API key. Passing the key in a GET request doesn't work either, as it still returns a "Login Required" notice.
How do I squeeze the above query into an apiclient-friendly format? The only option I found via the above calls was to request every device we have (via list), then sift through the mountain of data for the matching Serial number, which seems silly and excessive.
I did notice I could call apiclient.http.HttpRequests, but I couldn't find a way to pass the API key through it either. There's new_batch_http_request, but I can't discern from the docs how to simply pass a URL to it.
Thank you!
Got it!
You can't use just a key for Directory API queries, you need a Service account.
I'm using google-auth (see here) since oauth2client is deprecated.
You also need to:
Delegate the necessary permissions for your service account (mine has the role of Viewer and has scope access to https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly)
Delegate API access to it separately in the Admin Console (Security -> Advanced Settings -> Authentication)
Get your json client secret key and place it with your app (don't include it in your VCS)
Obtain your credentials like this:
credentials = service_account.Credentials.from_service_account_file(
settings.CLIENT_KEY,
scopes=settings.SCOPES,
subject=settings.ADMIN_USER)
where ADMIN_USER is the email address of an authorized Domain admin.
Then you send a GET request like so:
authed_session = AuthorizedSession(credentials)
response = authed_session.get(request_id_url)
This returns a Requests object you can read via response.content.
Hope it helps someone else!
I am learning django and trying to complete my first webapp.
I am using shopify api & boilder plate (starter code) and am having an issue with the final step of auth.
Specifically, the redirect URL -- it's using HTTP:// when it should NOT and I don't know how to change it..
#in my view
def authenticate(request):
shop = request.GET.get('shop')
print('shop:', shop)
if shop:
scope = settings.SHOPIFY_API_SCOPE
redirect_uri = request.build_absolute_uri(reverse('shopify_app_finalize')) #try this with new store url?
print('redirect url', redirect_uri) # this equals http://myherokuapp.com/login/finalize/
permission_url = shopify.Session(shop.strip()).create_permission_url(scope, redirect_uri)
return redirect(permission_url)
return redirect(_return_address(request))
Which is a problem because my app uses the Embedded Shopify SDK which causes this error to occur at the point of this request
Refused to frame 'http://my.herokuapp.com/' because it violates the following Content Security Policy directive: "child-src 'self' https://* shopify-pos://*". Note that 'frame-src' was not explicitly set, so 'child-src' is used as a fallback.
How do i change the URL to use HTTPS?
Thank you so much in advance. Please let me know if I can share any other details but my code is practically identical to that starter code
This is what the Django doc says about build_absolute_uri:
Mixing HTTP and HTTPS on the same site is discouraged, therefore
build_absolute_uri() will always generate an absolute URI with the
same scheme the current request has. If you need to redirect users to
HTTPS, it’s best to let your Web server redirect all HTTP traffic to
HTTPS.
So you can do two things:
Make sure your site runs entirely on HTTPS (preferred option): Setup your web server to use HTTPS, see the Heroku documentation on how to do this. Django will automatically use HTTPS for request.build_absolute_uri if the incoming request is on HTTPS.
I'm not sure what gets passed in the shop parameter but if it contains personal data I'd suggest to use HTTPS anyway.
Create the URL yourself:
url = "https://{host}{path}".format(
host = request.get_host(),
path = reverse('shopify_app_finalize'))
But you will still need to configure your server to accept incoming HTTPS requests.
I'm trying to setup endpoints api (with google app engine, python), but I'm having some trouble getting user profile info. API is working, I can create entities through API Explorer on my localhost.
My goal is to allow user to register for my app by providing just an email, and authorizing the app to get the reset of the info from their profile. I have this endpoints method:
#User.method(http_method="POST",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=[
endpoints.API_EXPLORER_CLIENT_ID
],
scopes=[
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.me',
],
user_required=True,
request_fields=('email',),
response_fields=('id',),
name="register",
path="users")
def UserRegister(self, instance):
logging.info(os.getenv( 'HTTP_AUTHORIZATION' ))
# 'Beared __TOKEN__'
logging.info(endpoints.users_id_token._get_token(None))
# '__TOKEN__'
instance.put()
return instance
This works fine, I receive authorization token and user is created in datastore, but I can't figure out how to get the profile info. If I enter the token in OAuth2 API (through API Explorer):
POST https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=__TOKEN__
I get token info with some data I need { "user_id": "__ID__", "verified_email": true, ...}, and if I use user_id in +API:
GET https://www.googleapis.com/plus/v1/people/__ID__
I can get the rest of the data I need (name, image, etc).
What do I need to do to achieve this in my UserRegister() method? I'd prefer to return just entity ID and do the rest of registration asynchronously, but that's another issue, I'll figure it out (; Just need some guidance how to call other endpoints from my code...
EDIT:
I've managed to figure out how to call other APIs (code on Gist), now only have one issue with Plus API:
I did some queries and eventually got anonymous quota error. Then I added key parameter and set it to WEB_CLIENT_ID or SERVICE_ACCOUNT:
WEB_CLIENT_ID is OAuth2 Client ID (type: Web Application) from console.developers.google.com/apis/credentials,
SERVICE_ACCOUNT is default App Engine service account - MY_APP#appspot.gserviceaccount.com...
and now I'm getting following error:
HttpError: <HttpError 400 when requesting https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key=__WEB_CLIENT_ID__or__SERVICE_ACCOUNT__&alt=json returned "Bad Request">
When I use +API explorer I get results as expected:
REQUEST:
https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key={YOUR_API_KEY}
RESPONSE:
200 OK + json data for user...
Anyone knows why is this happening?
Why am I getting BadRequest response?
Problem with BadRequest was that I didn't send authorization token... I did try to send it as access_token, but seams like +api docs are outdated - it should be oauth_token. When I included this parameter issue was resolved:
build('plus', 'v1').people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token).execute()
HINT: Use http://localhost:8001/_ah/api/discovery/v1/apis/, and discoveryRestUrl property it has to see real properties of your API - this is where I found the answer.
oauth_token can be obtained like this:
token = os.getenv('HTTP_AUTHORIZATION').split(" ")[1]
# or like in my question:
token = endpoints.users_id_token._get_token(None)
I'd suggest HTTP_AUTHORIZATION variable, because users_id_token docs state that it's a:
Utility library for reading user information from an id_token.
This is an experimental library that can temporarily be used to extract
a user from an id_token. The functionality provided by this library
will be provided elsewhere in the future.
How to call other API Endpoints?
This is also an answer to my first question:
from googleapiclient.discovery import build
service = build('plus', 'v1')
request = service.people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token)
response = request.execute()
data = dict(self.response.POST)
Code that worked for me is here.
NOTE: WEB_CLIENT_ID obtained from https://console.developers.google.com/apis/credentials (OAuth2 Client ID of type Web Application) will NOT work in this case. I had to use SERVICE_ACCOUNT - I didn't try to generate one through console, default service account I got from App Engine worked fine.
...things are much clearer now that I got this working. Hope it will help someone else (;