I'm trying to Integrate Apple Pay in my application. Following the online docs and some SO help, I've managed to show the Apple Pay payment dialog and all the call backs are configured.
the response returned from the Apple Pay is something like this
{
"version": "RSA_v1",
"signature": "somestring",
"data": "DP...A=",
"header": {
"wrappedKey": "MF...5g==",
"publicKeyHash": "kd...l4=",
"transactionId": "a5...3e"
}
}
But my requirement is to get ephemeralPublicKey under header object. According to Apple Pay Payment Token Format Reference
the returned object is correct as its RSA_v1 version which will not have ephemeralPublicKey but wrappedKey. Now my question is how do i get EC_v1 version of the returned token object.
May be I'm missing something here in the docs but please someone point me to the right direction. Any help is appreciated.
Here is the way to get this returned response from the Apple Pay Gateway server. You can't see this response in the console directly. You can see this response only if you will log it on the server by using an ajax request. See mine below sample URL and my source code.
Full Documentation for beginners
https://gist.github.com/jagdeepsingh/c538e21f3839f65732a5932e35809d60
session.onpaymentauthorized = (event) => {
$("#paymentMethodVal").val("apple-pay");
$form = $("#reservation-form");
console.log($form.serialize());
callRequest({
url: '/reserve/init-apple-pay',
method: 'POST',
data: {
token: event.payment.token,
reservationData: $form.serialize()
},
beforeRequest: function(e) {},
afterRequest: function(response) {
response = JSON.parse(response);
if(response.status){
toastr.success(response.message);
session.completePayment(ApplePaySession.STATUS_SUCCESS);
}else{
toastr.error(response.message.split(/\r\n|\r|\n/g).join('<br/>'));
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
}
});
};
token: event.payment.token, line is your answer this will send data directly to server and you can log data over there.
{
"version": "RSA_v1",
"signature": "somestring",
"data": "DP...A=",
"header": {
"wrappedKey": "MF...5g==",
"publicKeyHash": "kd...l4=",
"transactionId": "a5...3e"
}
}
Related
After sending the following post request to Microsoft Germany endpoint (https://graph.microsoft.de/v1.0), I got 400 UnableToDeserializePostBody, but I received 201 after I sent the same request to Microsoft Graph endpoint (https://graph.microsoft.com/v1.0).
Is there limitation on Germany endpoint?
Request
POST https://graph.microsoft.de/v1.0/users/xxx/events/
{
"attendees":[],
"body":{
"content":"<html>\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n<meta content=\"text/html; charset=iso-2022-jp\">\r\n<style type=\"text/css\" style=\"display:none\">\r\n<!--\r\np\r\n\t{margin-top:0;\r\n\tmargin-bottom:0}\r\n-->\r\n</style>\r\n</head>\r\n<body dir=\"ltr\">\r\n<div id=\"divtagdefaultwrapper\" dir=\"ltr\" style=\"font-size:12pt; color:#000000; font-family:Calibri,Helvetica,sans-serif\">\r\n<p style=\"margin-top:0; margin-bottom:0\"><br>\r\n</p>\r\n</div>\r\n</body>\r\n</html>\r\n",
"contentType":"html"
},
"bodyPreview":"",
"categories":[],
"changeKey":"V3BbxVnFKUiKHwfECS7njQABscz/wA==",
"createdDateTime":"2020-06-22T08:04:02.7260413Z",
"end": {
"dateTime":"2020-06-23T00:30:00.0000000",
"timeZone":"UTC"
},
"hasAttachments":false,
"iCalUId":"",
"id":"AAMkADY5ODA5YjI4LTZlMmEtNDk5My1hNTcxLTFmMDJlMTFlM2ZmYQBGAAAAAADXELVefACVTqbk5n3dmlnfBwBXcFvFWcUpSIofB8QJLueNAAGx1uUKAABXcFvFWcUpSIofB8QJLueNAAGx1vTOAAA=",
"importance":"normal",
"isAllDay":false,
"isCancelled":false,
"isOrganizer":true,
"isReminderOn":true,
"lastModifiedDateTime":"2020-06-22T08:04:03.3837944Z",
"location":{
"address":{
"city":"",
"countryOrRegion":"",
"postalCode":"",
"state":"",
"street":""
},
"coordinates":{},
"locationType":"default",
"uniqueIdType":"unknown"
},
"onlineMeetingUrl":"",
"organizer":{
"emailAddress":{
"address":"a#b",
"name":"test"
}
},
"originalEndTimeZone":"Taipei Standard Time",
"originalStart":null,
"originalStartTimeZone":"Taipei Standard Time",
"recurrence":null,
"reminderMinutesBeforeStart":15,
"responseRequested":true,
"responseStatus":{
"response":"organizer",
"time":"0001-01-01T00:00:00Z"
},
"sensitivity":"normal",
"seriesMasterId":"",
"showAs":"busy",
"start":{
"dateTime":"2020-06-23T00:00:00.0000000",
"timeZone":"UTC"
},
"subject":"1",
"type":"singleInstance",
"webLink":"https://outlook.office.de/owa/?itemid=AAMkADY5ODA5YjI4LTZlMmEtNDk5My1hNTcxLTFmMDJlMTFlM2ZmYQBGAAAAAADXELVefACVTqbk5n3dmlnfBwBXcFvFWcUpSIofB8QJLueNAAGx1uUKAABXcFvFWcUpSIofB8QJLueNAAGx1vTOAAA%3D&exvsurl=1&path=/calendar/item"
}
Response from Germany endpoint
400 Bad Request
{
"error": {
"code": "UnableToDeserializePostBody",
"message": "were unable to deserialize ",
"innerError": {
"date": "2020-08-07T08:44:52",
"request-id": "ce4c6d23-9163-42a1-9839-787596f7533b"
}
}
}
You've probably given up or fixed it by now but I just had this error when setting color. I used an integer but when I changed to string it was ok.
I notice you have "reminderMinutesBeforeStart":15 try it as "reminderMinutesBeforeStart":"15".
I ran into the same issue, and as louisl mentioned it was a problem with using the wrong primitive types in the request object.
I found this library of Typescript Types for the Microsoft Graph Client: https://github.com/microsoftgraph/msgraph-typescript-typings. This library provided the "Event" type, which is what I believe you're using.
Below is a snippet of Typescript that helped me ensure my request object was correct. Notice the Event import and the explicit typing of the eventRequest constant.
import { Event, Attendee } from '#microsoft/microsoft-graph-types'
const eventRequest: Event = {
subject: name,
body: {
contentType: 'html',
content: ...
},
...
}
If you're using VSCode and a Mac you can command + click on the type to look at the definition directly, so you can see what properties are optional, and what types they require. I found Microsoft's type library and in-line comments to be more up-to-date than the official documentation.
It's really useful to explore the type file to get a better idea of how the Microsoft Client works. This principle has been helpful with other libraries as well.
I'm not a programmer so please forgive me. But I've spent hours and hours of research on the topic of collecting information with Twilio AutoPilot and posted that data to Airtable, which I will then have Zapier do some things with that data. I finally had a breakthrough today and am now able to post data from a call or text to Airtable. The only way I got the ending to work was to send the call or text to Studio to finish up the call. Everything seems to work from the end user standpoint, but I'm getting an error 90100 from Twilio. I'm sure I'm just missing one line of code for this to work, and I'm at the end of my rope.
{
"actions": [
{
"say": "Okay lets get you a new appointment. I just need you to answer a few questions."
},
{
"collect": {
"name": "member",
"questions": [
{
"question": "Please tell me your first name.",
"name": "name",
"type": "Twilio.FIRST_NAME"
},
{
"question": "Thanks, and what is your email address?",
"name": "email",
"type": "Twilio.EMAIL"
}
],
"on_complete": {
"redirect": "task://complete_booking"
}
}
}
]
}
Then i have another task setup to redirect to the Twilio Function. This is probably overkill, but it's what I found in research.
{
"actions": [
{
"redirect": {
"method": "POST",
"uri": "https://TWILIO_FUNCTION_URL/atable_post"
}
}
]
}
Then the function is as follows. Mind you, this is posting correctly to airtable.
exports.handler = function(context, event, callback) {
let memory = JSON.parse(event.Memory);
let name = memory.twilio.collected_data.member.answers.name.answer;
let email = memory.twilio.collected_data.member.answers.email.answer;
console.log(memory);
let member = {
name : memory.twilio.collected_data.member.answers.name.answer,
email : memory.twilio.collected_data.member.answers.email.answer,
date : Date.now()
};
var Airtable = require("airtable");
var base = new Airtable({apikey: context.AIRTABLE_API_KEY}).base("AIRTABLE_ID");
base("Members1").create(member, function(err, record) {
if (err) { console.error(err); return; }
console.log(record.getId());
callback(null, member);
});
};
The call hung up at this point, so I redirected it to a Studio Flow, which does work and the call finishes with the response I'm give it before ending the call. Again, everything is working fine, but I get the following error from twilio, and I have no idea how to resolve it.
Invalid Autopilot Actions JSON: Invalid Autopilot Action
Any help would be greatly appreciated. Thanks!
Nice work James! It looks the the issue is the redirect to your Twilio Function is not returning the expected JSON Action response to execute.
Autopilot - Redirect
https://www.twilio.com/docs/autopilot/actions/redirect
Redirecting to URLs When redirecting to a URL, Redirect will make an
HTTP callback to your application and will expect an Autopilot Actions
JSON as a response. The request will contain all the dialogue
information. This is an example of a dynamic Action since the JSON is
rendered dynamically with a URL or your own endpoint.
Can you modify the Twilio Function to return valid Action JSON to Autopilot which sets the returned data, if needed via the Remember action which you can access from Studio?
I'm building a chatbot with Twilio Autopilot and I want to get the images that I send to the bot, how can I get it? How can I get locations as well?
Thanks a lot.
Twilio developer evangelist here.
There isn't a simple way to do this, but there are a few potential work-arounds. One is to have a webhook endpoint that will get the input and if the payload contains elements of an image, then do whatever you want with it, otherwise if it is just text, then maybe send to Autopilot. That is gone over in this blog post on Autopilot enhancements in Node.js.
Another is to make a Twilio Function that would point to a Twilio Studio flow or Assets if it is media in the first message.
Another is to use Twilio Functions or a similar server. You should have an Autopilot task that redirects to that Function with JSON like this:
{
"actions": [
{
"redirect": {
"uri": "https://REPLACE-WITH-YOUR-FUNCTION-URL.twil.io/auso",
"method": "POST"
}
}
]
}
Then your Twilio Function could get the image URL with something like this in Node.js:
const bodyContent = event.MediaUrl0;
const filename = event.MessageSid + '.png';
Now in a Collect Action you can also specify the Twilio.MEDIA as the type for questions expecting Media and currently we support all media format which are supported by Twilio Messaging.
{
"question": "Please a take a picture of insurance card?",
"name": "insurance_card",
"type": "Twilio.MEDIA",
"validate": {
"allowed_types": {
"list": [
"image/jpeg",
"image/gif",
"image/png",
"image/bmp"
]
},
Lastly, you may be interested in this blog post on building an image classifier with Autopilot and TensorFlow.
Let me know if this helps at all! :D
Regarding images - As seen in this example of an autopilot task program, specify the input type to be an image
{
"actions": [
{
"collect": {
"name": "contact",
"questions": [
{
"question": "Please upload a cool picture of yourself",
"name": "contact_image",
"type": "Twilio.MEDIA"
}
],
"on_complete": {
"redirect": {
"method": "POST",
"uri": "https://url.twil.io/image-processing"
}
}
}
}
]
}
Then you can access the image as seen have done in the following function
exports.handler = function(context, event, callback) {
//we get the Memory from the answered questions.
let memory = JSON.parse(event.Memory);
//set up an array of object "actions" for the autopilot to continue.
let actions = [];
let responseItem;
//print the url of the image
let image_url = memory.twilio.collected_data.contact.answers.contact_image.media.url;
console.log(image_url);
responseItem = {
"redirect": {
"method": "POST",
"uri": "task://next_task"
}
};
actions.push(responseItem);
let respObj = {
"actions": actions
};
callback(null, respObj);
};
Autopilot Troublehooting
Unable to receive picture messages
Autopilot is currently unable to receive messages with pictures or other media types supported by Twilio on any messaging channel and will throw error with code 11200.
I haven't tried WhatsApp location data but there is a blog on the functionality that may help?
New Rich Features Support Deeper Customer Engagement on WhatsApp
I am getting below errors while trying to create workitems with batch creation method
Error 1
"Message":"No MediaTypeFormatter is available to read an object of type 'JsonBatchHttpRequest' from content with media type 'application/json-patch+json'."
Error 2
{"count":1,"value":{"Message":"One or more errors occurred."}}
I have referred to this documentation https://www.visualstudio.com/en-us/docs/integrate/api/wit/batch from Microsoft . and my question on stackoverflaw Create Large Amount of Work Items in TFS Using Javascript REST API
I have tried to send data as below methods
"json: x"
"body: x:"
"body:JSON.stringify(x)"
"json:[body:x]"
I have tried both "application/json-patch+json" and "application/json"(recommended as MIcrosoft documentation) as Content-Types
I have tired both Post (recommended as MIcrosoft documentation) and Patch methods
There is no references available for this error hence I have sucked at this point.What could be possibly wrong here please help..
public batchOperation( ):q.Promise<boolean>{
let deferred = q.defer<boolean>();
try {
var batchCreateUrl = this.collectionURL+"/_apis/wit/$batch?api-version=1.0";
var x= {
method:"PATCH",
uri:"/VSTS_TFS_Test/_apis/wit/workItems/$Bug?api-version=1.0",
headers:{
"Content-Type":"application/json-patch+json"
},
body:[
{ "op":"add",
"path": "/fields/System.Tags",
"value":"tg;tg1;tg2"
},
{
"op": "add",
"path": "/fields/System.Title",
"value": "Some Title Text "
},
{
"op": "add",
"path": "/fields/System.Description",
"value":"this is description"
}
]
}
var options = {
url: batchCreateUrl,
username: this.username,
password: this.password,
domain: this.domain,
method: 'PATCH',
headers: {
'Content-Type': 'application/json-patch+json'
},
body: x
};
httpntlm.patch(options, function(err,res) {
if(err) {
return deferred.reject(false);}
else{
console.log("Patch Complete");
console.log(res.body);
deferred.resolve(true);
}
});
} catch (error) {
console.log("Failed to Perform Batch Operation ")
deferred.reject(false);
}
return deferred.promise;
}
You need to use "application/json" as Content-Types and the post method just like the tutorial of Microsoft documentation described.
Since you are using httpntlm, you can include the following options:
json: if you want to send json directly (content-type is set to
application/json)
files: an object of files to upload (content-type is set to
multipart/form-data; boundary=xxx)
body: custom body content you want to send. If used, previous
options will be ignored and your custom body will be sent.
(content-type will not be set)
Source Link
If you are using body, your previous options will be ignored(content-type will lose), this may cause the issue. Give a try with directly using json.
I'm trying to insert moments, using access_token, that i generated here https://developers.google.com/oauthplayground/ and receive "Unauthorized" message. What i do wrong?
My request is:
{
"target": {
"url": "http://example.com"
},
"type": "http://schemas.google.com/AddActivity"
}
I send this data on
www.googleapis.com/plus/v1/people/me/moments/vault?access_token=######, where ###### - my access token.
The response is:
{
"error":{
"errors":[
{
"domain":"global",
"reason":"unauthorized",
"message":"Unauthorized"
}
],
"code":401,
"message":"Unauthorized"
}
}
i had the same problem, i solved it by adding "request_visible_actions" and also make sure you have the login scope set on your request i.e "https://www.googleapis.com/auth/plus.login"